Skip to content

Commit

Permalink
chore: Allow replacing card commands dynamically - flex layout + type…
Browse files Browse the repository at this point in the history
… fixes.
  • Loading branch information
mturoci committed Oct 5, 2023
1 parent d38db47 commit af759a6
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 58 deletions.
28 changes: 19 additions & 9 deletions ui/src/card_menu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,42 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { initializeIcons } from '@fluentui/react'
import { fireEvent, render } from '@testing-library/react'
import { Card, box } from 'h2o-wave'
import React from 'react'
import { CardMenu } from './card_menu'
import { box } from 'h2o-wave'

const name = 'card'
let card: Card
describe('CardMenu.tsx', () => {
beforeAll(() => initializeIcons())

beforeEach(() => {
card = {
name,
changed: box(false),
state: { commands: [{ name }], },
id: 'id',
set: jest.fn(),
}
})
it('Does not render data-test attr', () => {
const { container } = render(<CardMenu commands={[{ name }]} changedB={box(false)} />)
// @ts-ignore
card.name = undefined
const { container } = render(<CardMenu card={card} />)
expect(container.querySelectorAll('[data-test]')).toHaveLength(0)
})

it('Renders data-test attr', () => {
const { queryByTestId } = render(<CardMenu name={name} commands={[{ name }]} changedB={box(false)} />)
const { queryByTestId } = render(<CardMenu card={card} />)
expect(queryByTestId(name)).toBeInTheDocument()
})

it('Renders menu when commands are specified', () => {
const { queryByTestId } = render(<CardMenu name={name} commands={[{ name }]} changedB={box(false)} />)
const { queryByTestId } = render(<CardMenu card={card} />)
expect(queryByTestId(name)).toBeTruthy()
})

it('Does not change hash when command name does not start with #', () => {
const { getByTestId, getByText } = render(<CardMenu name={name} commands={[{ name }]} changedB={box(false)} />)
const { getByTestId, getByText } = render(<CardMenu card={card} />)

fireEvent.click(getByTestId(name))
fireEvent.click(getByText('card'))
Expand All @@ -47,7 +56,8 @@ describe('CardMenu.tsx', () => {
})

it('Changes hash when command name starts with #', () => {
const { getByTestId, getByText } = render(<CardMenu name={name} commands={[{ name: '#card' }]} changedB={box(false)} />)
card.state.commands = [{ name: '#card' }]
const { getByTestId, getByText } = render(<CardMenu card={card} />)

fireEvent.click(getByTestId(name))
fireEvent.click(getByText('#card'))
Expand Down
67 changes: 33 additions & 34 deletions ui/src/card_menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// limitations under the License.

import { ContextualMenu, Icon, IContextualMenuItem } from '@fluentui/react'
import { B, box, Card } from 'h2o-wave'
import { B, Card, S } from 'h2o-wave'
import * as React from 'react'
import { stylesheet } from 'typestyle'
import { deleteCard, editCard } from './editing'
Expand Down Expand Up @@ -83,39 +83,38 @@ const
}

export const
CardMenu = bond(({ card, canEdit }: { card: Card, canEdit?: B }) => {
ContextMenu = ({ name, commands }: { commands: Command[], name?: S }) => {
const
target = React.createRef<HTMLDivElement>(),
hiddenB = box(true),
show = () => hiddenB(false),
hide = () => hiddenB(true),
render = () => {
const cmds = card.state.commands ?? []
if (canEdit) {
cmds.push(
{ name: editCommand, label: 'Edit this card', icon: 'Edit', value: card.name },
{ name: deleteCommand, label: 'Delete this card', icon: 'Delete', value: card.name },
)
}
const
hidden = hiddenB(),
items = cmds.map(toContextMenuItem)
return items.length ? (
// `w-card-menu` is a marker class.
<div className={clas(css.menu, 'w-card-menu')} data-test={card.name}>
<div className={css.target} ref={target} onClick={show}>
<Icon className={css.icon} iconName='MoreVertical' />
</div>
<ContextualMenu
target={target}
items={items}
hidden={hidden}
onItemClick={hide}
onDismiss={hide}
styles={fixMenuOverflowStyles}
/>
</div>
) : <></>
target = React.useRef<HTMLDivElement>(null),
[isHidden, setIsHidden] = React.useState(true)

return commands.length ? (
// `w-card-menu` is a marker class.
<div className={clas(css.menu, 'w-card-menu')} data-test={name}>
<div className={css.target} ref={target} onClick={() => setIsHidden(false)}>
<Icon className={css.icon} iconName='MoreVertical' />
</div>
<ContextualMenu
target={target}
items={commands.map(toContextMenuItem)}
hidden={isHidden}
onItemClick={() => setIsHidden(true)}
onDismiss={() => setIsHidden(true)}
styles={fixMenuOverflowStyles}
/>
</div>
) : null
},
CardMenu = bond(({ card, canEdit }: { card: Card, canEdit?: B }) => {
const render = () => {
const cmds = card.state.commands ?? []
if (canEdit) {
cmds.push(
{ name: editCommand, label: 'Edit this card', icon: 'Edit', value: card.name },
{ name: deleteCommand, label: 'Delete this card', icon: 'Delete', value: card.name },
)
}
return { render, changedB: card.changed, hiddenB }
return <ContextMenu name={card.name} commands={cmds} />
}
return { render, changed: card.changed }
})
2 changes: 1 addition & 1 deletion ui/src/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ const
return (
<div key={c.id} className={getCardEffectClass(c)} style={toSlotStyle(cardslot)}>
<CardView card={c} />
<CardMenu name={c.name} commands={c.state.commands} changedB={c.changed} canEdit={hasEditor} />
<CardMenu card={c} canEdit={hasEditor} />
</div>
)
})
Expand Down
23 changes: 10 additions & 13 deletions ui/src/text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import * as Fluent from '@fluentui/react'
import { B, Dict, S } from 'h2o-wave'
import React from 'react'
import { stylesheet } from 'typestyle'
import { CardMenu } from './card_menu'
import { ContextMenu } from './card_menu'
import { Markdown } from './markdown'
import { margin } from './theme'
import { Command } from './toolbar'
Expand Down Expand Up @@ -132,15 +132,12 @@ const
},
toTextVariant = (s: S) => textVariants[s] || 'mediumPlus'

export const XText = ({ content, name, size, commands }: { content: S, name?: S, size?: S, commands?: Command[] }) => {
const menuName = name ? `${name}-menu` : name
return (
<div className={css.text}>
{/* `w-text` is a marker class. */}
<Fluent.Text data-test={name} variant={toTextVariant(size || 'm')} block className='w-text'>
<Markdown source={content} />
</Fluent.Text>
{!!commands?.length && <CardMenu name={menuName} commands={commands} />}
</div>
)
}
export const XText = ({ content, name, size, commands }: { content: S, name?: S, size?: S, commands?: Command[] }) => (
<div className={css.text}>
{/* `w-text` is a marker class. */}
<Fluent.Text data-test={name} variant={toTextVariant(size || 'm')} block className='w-text'>
<Markdown source={content} />
</Fluent.Text>
{!!commands?.length && <ContextMenu name={name ? `${name}-menu` : name} commands={commands} />}
</div>
)
2 changes: 1 addition & 1 deletion website/widgets/ai/audio_annotator.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Useful for labelling audio data.

Check the full API at [ui.audio_annotator](/docs/api/ui#audio_annotator).

```py sleep 2
```py sleep 4
q.page['example'] = ui.form_card(box='1 1 7 7', items=[
ui.audio_annotator(
name='annotator',
Expand Down

0 comments on commit af759a6

Please sign in to comment.