Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/orm-1594 #25

Merged
merged 10 commits into from
Aug 12, 2024
5 changes: 4 additions & 1 deletion apps/storybook/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ const preview = {
(story, context) => {
const bgTheme = context.globals?.backgrounds?.value === DARK_THEME_BG ? 'theme-dark' : 'theme-light'
const heightValue = context.parameters?.layout === 'set-height' ? '613px' : 'auto'
const publicComponent = context.kind?.includes('Components') && !context.kind?.includes('Internal')
const wrapperId = !publicComponent && `orama-ui-${context.id}`
const wrapperClass = !publicComponent && bgTheme
return html`
<style>
orama-chat-box {
height: ${heightValue};
}
</style>
<div id="orama-ui" class="${bgTheme}">${story()}</div>
<div id="${wrapperId}" class="${wrapperClass}">${story()}</div>
`
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,13 @@ Design tokens are a critical part of ensuring consistency and scalability in you

We're using global styles that are available to all components and allow theme customization by exposing CSS custom properties.

We use the `mapToCustomProperties(map)` mixin to apply custom properties from a map of key-value pairs.
Theme switcher classes `.theme-light` and `.theme-dark` are used to apply the color tokens and they must be applied to the host element.
As an extra layer of safety, to avoid any possible collisions with same named custom properties defined in the hosting application under the same class name, we use the `id^='orama-ui'` selector to scope the custom properties, therefore all shadow hosts must have an id starting with `orama-ui`.
Theme switcher classes `.theme-light` and `.theme-dark` are used to apply the color tokens and they are automatically applied to the root element of the shadow DOM when the color scheme is changed.

As an extra layer of safety, to avoid any possible collisions with same named custom properties defined in the hosting application under the same class name, we use the `id^='orama-ui'` selector to scope the custom properties, therefore **all shadow hosts must have an `id` starting with `orama-ui`**.

```css
[id^='orama-ui'] {
@include mapToCustomProperties($theme-light); // light theme is the default
@include mapToCustomProperties($theme-typography);
// other global styles...

&.theme-dark {
@include mapToCustomProperties($theme-dark);
}
}

```html
<orama-search-button id="orama-ui-search-button">Search</orama-search-button>
```

## Colors
Expand Down
3 changes: 2 additions & 1 deletion apps/storybook/stories/public/orama-search-box.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export default meta

const Template = ({ preset, colorScheme }) => {
return html`<orama-search-box
id='orama-ui-search-box'
open=${preset?.open}
.facetProperty=${preset?.facetProperty}
.resultMap=${preset?.resultMap}
Expand All @@ -86,7 +87,7 @@ const Template = ({ preset, colorScheme }) => {
type Story = StoryObj<Components.OramaSearchBox & { preset: keyof DemoIndexConfig }>

export const SearchBox: Story = {
render: Template,
render: Template as any,
args: {
preset: 'orama',
},
Expand Down
24 changes: 23 additions & 1 deletion apps/storybook/stories/public/orama-search-button.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { StoryObj, Meta } from '@storybook/web-components'
import type { Components } from '@orama/wc-components'
import demoIndexes from '../config'
import { html } from 'lit-html'

const meta: Meta<Components.OramaSearchButton> = {
title: 'Components/SearchButton',
Expand All @@ -9,4 +11,24 @@ const meta: Meta<Components.OramaSearchButton> = {
export default meta
type Story = StoryObj<Components.OramaSearchButton>

export const SearchButton: Story = {}
const Template = (label: string) => (args) => {
return html`
<div>
<orama-search-button label="${args.label}" id="orama-ui-search-button">${label}</orama-search-button>
<orama-search-box
.open=${false}
.index=${demoIndexes.orama.index}
.placeholder=${demoIndexes.orama.placeholder}
.sourceBaseUrl=${demoIndexes.orama.sourceBaseUrl}
.sourcesMap=${demoIndexes.orama.sourcesMap}
.suggestions=${demoIndexes.orama.suggestions}
>
</orama-search-box>
</div>
`
}

export const SearchButton: Story = {
render: Template('Search...'),
args: {},
}
9 changes: 7 additions & 2 deletions packages/ui-stencil-vue/lib/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,10 @@ export const OramaModal = /*@__PURE__*/ defineContainer<JSX.OramaModal>('orama-m
]);


export const OramaNavigationBar = /*@__PURE__*/ defineContainer<JSX.OramaNavigationBar>('orama-navigation-bar', undefined);
export const OramaNavigationBar = /*@__PURE__*/ defineContainer<JSX.OramaNavigationBar>('orama-navigation-bar', undefined, [
'handleClose',
'showChatActions'
]);


export const OramaSearch = /*@__PURE__*/ defineContainer<JSX.OramaSearch>('orama-search', undefined, [
Expand All @@ -139,7 +142,9 @@ export const OramaSearchBox = /*@__PURE__*/ defineContainer<JSX.OramaSearchBox>(
]);


export const OramaSearchButton = /*@__PURE__*/ defineContainer<JSX.OramaSearchButton>('orama-search-button', undefined);
export const OramaSearchButton = /*@__PURE__*/ defineContainer<JSX.OramaSearchButton>('orama-search-button', undefined, [
'colorScheme'
]);


export const OramaSearchResults = /*@__PURE__*/ defineContainer<JSX.OramaSearchResults>('orama-search-results', undefined, [
Expand Down
8 changes: 7 additions & 1 deletion packages/ui-stencil/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ export namespace Components {
"open": boolean;
}
interface OramaNavigationBar {
"handleClose": () => void;
"showChatActions": boolean;
}
interface OramaSearch {
"focusInput"?: boolean;
Expand All @@ -110,7 +112,7 @@ export namespace Components {
"colorScheme"?: ColorScheme;
"facetProperty"?: string;
"index": CloudIndexConfig;
"open"?: boolean;
"open": boolean;
"placeholder"?: string;
"resultMap"?: Partial<ResultMap>;
"sourceBaseUrl"?: string;
Expand All @@ -119,6 +121,7 @@ export namespace Components {
"themeConfig"?: Partial<TThemeOverrides>;
}
interface OramaSearchButton {
"colorScheme": ColorScheme;
}
interface OramaSearchResults {
"error": boolean;
Expand Down Expand Up @@ -453,6 +456,8 @@ declare namespace LocalJSX {
"open"?: boolean;
}
interface OramaNavigationBar {
"handleClose"?: () => void;
"showChatActions"?: boolean;
}
interface OramaSearch {
"focusInput"?: boolean;
Expand All @@ -472,6 +477,7 @@ declare namespace LocalJSX {
"themeConfig"?: Partial<TThemeOverrides>;
}
interface OramaSearchButton {
"colorScheme"?: ColorScheme;
}
interface OramaSearchResults {
"error"?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
}

.button--secondary {
background-color: var(--button-background-color-secondary, button-background-color('secondary'));
color: var(--button-text-color-secondary, button-text-color('secondary'));
border: 1px solid var(--button-border-color-secondary, map-get($palette, 'button-border', 'secondary'));

&:disabled {
background-color: var(--background-color-tertiary, background-color('tertiary'));
color: var(--text-color-tertiary);
Expand Down Expand Up @@ -65,6 +69,12 @@
}
}

// slot elements should be aligned center
::slotted(*) {
display: inline-flex;
align-items: center;
}

@keyframes fadeInOut {
0% {
opacity: 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ export class OramaButton {

return (
<Tag class={buttonClass} {...buttonProps} disabled={this.disabled}>
<slot />
<slot name="adorment-start" />
{this.withTooltip && <span class="button__tooltip">{this.withTooltip}</span>}
<slot />
<slot name="adorment-end" />
</Tag>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@
- [orama-chat](../orama-chat)
- [orama-chat-assistent-message](../orama-chat-messages-container/orama-chat-assistent-message)
- [orama-navigation-bar](../orama-navigation-bar)
- [orama-search-button](../../orama-search-button)

### Graph
```mermaid
graph TD;
orama-chat --> orama-button
orama-chat-assistent-message --> orama-button
orama-navigation-bar --> orama-button
orama-search-button --> orama-button
style orama-button fill:#f9f,stroke:#333,stroke-width:4px
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
.suggestion-button-list {
display: flex;
align-items: center;
text-align: left;
column-gap: var(--spacing-s, $spacing-s);
border: 0;
background-color: transparent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { SourcesMap } from '@/types'
import '@phosphor-icons/webcomponents/dist/icons/PhPaperPlaneTilt.mjs'
import '@phosphor-icons/webcomponents/dist/icons/PhStop.mjs'
import '@phosphor-icons/webcomponents/dist/icons/PhArrowDown.mjs'
import { globalContext } from '@/context/GlobalContext'

const BOTTOM_THRESHOLD = 1

Expand All @@ -24,7 +25,9 @@ export class OramaChat {

@Watch('defaultTerm')
handleDefaultTermChange() {
this.inputValue = this.defaultTerm
if (this.defaultTerm) {
chatContext.chatService?.sendQuestion(this.defaultTerm)
}
}

@Watch('focusInput')
Expand Down Expand Up @@ -121,7 +124,6 @@ export class OramaChat {
}

componentWillLoad() {
this.inputValue = this.defaultTerm || ''
this.handleFocus()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
| `placeholder` | `placeholder` | | `string` | `'Ask me anything'` |
| `showClearChat` | `show-clear-chat` | | `boolean` | `true` |
| `sourceBaseUrl` | `source-base-url` | | `string` | `''` |
| `sourcesMap` | -- | | `{ title?: string; path?: string; description?: string; }` | `undefined` |
| `sourcesMap` | -- | | `{ title?: string; description?: string; path?: string; }` | `undefined` |
| `suggestions` | -- | | `string[]` | `undefined` |


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import { Component, h, Prop, State, Listen, Element } from '@stencil/core'
scoped: true,
})
export class OramaModal {
@Prop() open = false
@Prop({ mutable: true }) open = false
@Prop() closeOnEscape = true
@Prop() mainTitle = ''
@State() activeElement: HTMLElement
@Element() el: HTMLElement

private firstFocusableElement: HTMLElement
private lastFocusableElement: HTMLElement
private innerModalRef: HTMLElement

@Listen('keydown', { target: 'document' })
handleKeyDown(ev: KeyboardEvent) {
Expand Down Expand Up @@ -85,6 +86,13 @@ export class OramaModal {
this.activeElement = document.activeElement as HTMLElement
this.handleFocus()
}
this.el.addEventListener('click', (event) => {
event.stopPropagation()
event.preventDefault()
if (!this.innerModalRef.contains(event.target as Node)) {
this.closeModal()
}
})
}

componentDidUpdate() {
Expand Down Expand Up @@ -132,7 +140,7 @@ export class OramaModal {
aria-labelledby="modalTitle"
aria-describedby="modalContent"
>
<div class="modal-inner">
<div class="modal-inner" ref={(ref) => (this.innerModalRef = ref)}>
<h1 id="modalTitle" class="modal-title">
{this.mainTitle}
</h1>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,56 @@
import { Component, Host, h } from '@stencil/core'
import { Component, Host, Prop, h } from '@stencil/core'
import '@phosphor-icons/webcomponents/dist/icons/PhClock.mjs'
import '@phosphor-icons/webcomponents/dist/icons/PhPlus.mjs'
import '@phosphor-icons/webcomponents/dist/icons/PhCaretLeft.mjs'
import { chatContext } from '@/context/chatContext'

@Component({
tag: 'orama-navigation-bar',
styleUrl: 'orama-navigation-bar.scss',
scoped: true,
})
export class OramaNavigationBar {
@Prop() handleClose: () => void
@Prop() showChatActions = false

// TODO: maybe better to make this component context agnostic
private handleStartNewChat = () => {
chatContext.chatService?.resetChat()
}

render() {
return (
<Host>
<div class="corner-section start">
<orama-button type="button" variant="icon" aria-label="Exit">
<orama-button
type="button"
variant="icon"
aria-label="Exit"
onClick={this.handleClose}
onKeyDown={this.handleClose}
>
<ph-caret-left size="20px" />
</orama-button>
</div>
<div class="corner-section center">
<orama-toggler />
</div>
{/* TODO: uncomment when feature is ready */}
<div class="corner-section end">
{/* <orama-button type="button" variant="icon" aria-label="View history">
{this.showChatActions && (
<div class="corner-section end">
{/* <orama-button type="button" variant="icon" aria-label="View history">
<ph-clock size="20px" />
</orama-button>
<orama-button type="button" variant="icon" aria-label="Start new chat">
<ph-plus size="20px" />
</orama-button> */}
</div>
<orama-button
type="button"
variant="icon"
aria-label="Start new chat"
onClick={this.handleStartNewChat}
onKeyDown={this.handleStartNewChat}
>
<ph-plus size="20px" />
</orama-button>
</div>
)}
</Host>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
<!-- Auto Generated Below -->


## Properties

| Property | Attribute | Description | Type | Default |
| ----------------- | ------------------- | ----------- | ------------ | ----------- |
| `handleClose` | -- | | `() => void` | `undefined` |
| `showChatActions` | `show-chat-actions` | | `boolean` | `false` |


## Dependencies

### Used by
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
| `index` | -- | | `{ api_key: string; endpoint: string; }` | `undefined` |
| `placeholder` | `placeholder` | | `string` | `undefined` |
| `sourceBaseUrl` | `source-base-url` | | `string` | `undefined` |
| `sourcesMap` | -- | | `{ title?: string; path?: string; description?: string; }` | `undefined` |
| `sourcesMap` | -- | | `{ title?: string; description?: string; path?: string; }` | `undefined` |
| `suggestions` | -- | | `string[]` | `undefined` |


Expand Down
Loading
Loading