Skip to content

Commit

Permalink
docs(listbox-primitive): add stories (#3265)
Browse files Browse the repository at this point in the history
* test(reakit): add stories for composite

* test(listbox-primitive): improve stories for listboxprimitive

* docs: fix story keyboard usage bug

* fix: undo

* fix: annoying linter
  • Loading branch information
TheSisb committed Jun 9, 2023
1 parent 8fc130d commit da61a7b
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {As} from './types';

export interface ListboxPrimitiveItemProps
extends Omit<CompositeItemProps, 'unstable_virtual' | 'unstable_moves' | 'unstable_system' | 'wrapElement' | 'wrap'> {
/** Applies the aria-selected attribute for accessibility purposes */
selected?: boolean;
/** Event handler to respond to selection events */
onSelect?: (event: React.MouseEvent<HTMLButtonElement>) => void;
Expand Down
121 changes: 121 additions & 0 deletions packages/paste-core/primitives/listbox/stories/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import * as React from 'react';
import {Stack} from '@twilio-paste/stack';
import {Box} from '@twilio-paste/box';
import {Grid, Column} from '@twilio-paste/grid';
import {useUIDSeed} from '@twilio-paste/uid-library';
import {Button} from '@twilio-paste/button';
import {CheckboxCheckIcon} from '@twilio-paste/icons/esm/CheckboxCheckIcon';
import {PlusIcon} from '@twilio-paste/icons/esm/PlusIcon';
import {MinusIcon} from '@twilio-paste/icons/esm/MinusIcon';

import {useListboxPrimitiveState, ListboxPrimitive, ListboxPrimitiveGroup, ListboxPrimitiveItem} from '../src';

Expand All @@ -25,8 +31,13 @@ export const VerticalListbox = (): React.ReactNode => {
data-testid={`item-${index}`}
{...listbox}
selected={selected === item}
style={{
...(selected === item && {backgroundColor: '#0263e0', color: 'white'}),
}}
onSelect={() => {
setSelected(item);
// eslint-disable-next-line no-console
console.log(item);
}}
>
{item}
Expand All @@ -48,8 +59,13 @@ export const HorizontalListbox = (): React.ReactNode => {
data-testid={`item-${index}`}
{...listbox}
selected={selected === item}
style={{
...(selected === item && {backgroundColor: '#0263e0', color: 'white'}),
}}
onSelect={() => {
setSelected(item);
// eslint-disable-next-line no-console
console.log(item);
}}
>
{item}
Expand All @@ -74,8 +90,13 @@ export const GroupedOptions = (): React.ReactNode => {
key={item}
{...listbox}
selected={selected === item}
style={{
...(selected === item && {backgroundColor: '#0263e0', color: 'white'}),
}}
onSelect={() => {
setSelected(item);
// eslint-disable-next-line no-console
console.log(item);
}}
>
{item}
Expand All @@ -91,8 +112,13 @@ export const GroupedOptions = (): React.ReactNode => {
key={item}
{...listbox}
selected={selected === item}
style={{
...(selected === item && {backgroundColor: '#0263e0', color: 'white'}),
}}
onSelect={() => {
setSelected(item);
// eslint-disable-next-line no-console
console.log(item);
}}
>
{item}
Expand All @@ -107,6 +133,7 @@ export const GroupedOptions = (): React.ReactNode => {
export const MultiselectListbox = (): React.ReactNode => {
const [selectedSet, updateSelectedSet] = React.useState<Set<string>>(new Set());
const listbox = useListboxPrimitiveState({orientation: 'horizontal'});

return (
<ListboxPrimitive {...listbox} aria-label="Multiselect" variant="multiple">
{ITEMS.map((item, index) => (
Expand All @@ -115,6 +142,9 @@ export const MultiselectListbox = (): React.ReactNode => {
data-testid={`item-${index}`}
{...listbox}
selected={selectedSet.has(item)}
style={{
...(selectedSet.has(item) && {backgroundColor: '#0263e0', color: 'white'}),
}}
onSelect={() => {
const newSelectedSet = new Set(selectedSet);
if (newSelectedSet.has(item)) {
Expand All @@ -123,6 +153,8 @@ export const MultiselectListbox = (): React.ReactNode => {
newSelectedSet.add(item);
}
updateSelectedSet(newSelectedSet);
// eslint-disable-next-line no-console
console.log(newSelectedSet);
}}
>
{item}
Expand All @@ -131,3 +163,92 @@ export const MultiselectListbox = (): React.ReactNode => {
</ListboxPrimitive>
);
};

export const DualExample = (): React.ReactNode => {
const [components, updateComponents] = React.useState(['Alert', 'Anchor', 'Button', 'Card', 'Heading', 'List']);
const [selectedComps, updateSelectedComps] = React.useState(new Set());
const compListbox = useListboxPrimitiveState();

const [favs, updateFavs] = React.useState(['Modal']);
const [selectedFavs, updateSelectedFavs] = React.useState(new Set());
const favListbox = useListboxPrimitiveState();

return (
<Grid gutter="space30">
<Column>
<ListboxPrimitive {...compListbox} aria-label="Components" variant="multiple" as={Box} height="300px">
<Stack orientation="vertical" spacing="space40">
{components.map((item) => (
<ListboxPrimitiveItem
as={Button}
size="small"
key={item}
{...compListbox}
selected={selectedComps.has(item)}
onSelect={() => {
const newSelectedComps = new Set(selectedComps);
if (newSelectedComps.has(item)) {
newSelectedComps.delete(item);
} else {
newSelectedComps.add(item);
}
updateSelectedComps(newSelectedComps);
}}
>
{selectedComps.has(item) && <CheckboxCheckIcon decorative />}
{item}
</ListboxPrimitiveItem>
))}
</Stack>
</ListboxPrimitive>
<Button
variant="primary_icon"
onClick={() => {
updateFavs([...favs, ...(Array.from(selectedComps) as string[])]);
updateComponents(components.filter((item) => !selectedComps.has(item)));
selectedComps.clear();
}}
>
Add <PlusIcon decorative={false} title="Add items" />
</Button>
</Column>
<Column>
<ListboxPrimitive {...favListbox} aria-label="Favorite components" variant="multiple" as={Box} height="300px">
<Stack orientation="vertical" spacing="space40">
{favs.map((item) => (
<ListboxPrimitiveItem
as={Button}
size="small"
key={item}
{...favListbox}
selected={selectedFavs.has(item)}
onSelect={() => {
const newSelectedFavs = new Set(selectedFavs);
if (newSelectedFavs.has(item)) {
newSelectedFavs.delete(item);
} else {
newSelectedFavs.add(item);
}
updateSelectedFavs(newSelectedFavs);
}}
>
{selectedFavs.has(item) && <CheckboxCheckIcon decorative />}
{item}
</ListboxPrimitiveItem>
))}
</Stack>
</ListboxPrimitive>
<Button
variant="primary_icon"
onClick={() => {
updateComponents([...components, ...(Array.from(selectedFavs) as string[])]);
updateFavs(favs.filter((item) => !selectedFavs.has(item)));
selectedFavs.clear();
}}
>
Remove <MinusIcon decorative={false} title="Remove items" />
</Button>
</Column>
</Grid>
);
};
93 changes: 93 additions & 0 deletions packages/paste-libraries/reakit/stories/composite.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import * as React from 'react';

import {useCompositeState, Composite, CompositeGroup, CompositeItem} from '../src';

// eslint-disable-next-line import/no-default-export
export default {
title: 'Libraries/Reakit/Composite',
component: Composite,
subcomponents: {Composite, CompositeItem},
};

const onClick =
(num: number): (() => void) =>
() =>
// eslint-disable-next-line no-console
console.log(`clicked ${num}`);

export const BasicUsage = (): React.ReactNode => {
const composite = useCompositeState();

return (
<Composite {...composite} role="toolbar" aria-label="My toolbar">
<CompositeItem {...composite} onClick={onClick(1)}>
Item 1
</CompositeItem>
<CompositeItem {...composite} onClick={onClick(2)}>
Item 2
</CompositeItem>
<CompositeItem {...composite} onClick={onClick(3)}>
Item 3
</CompositeItem>
</Composite>
);
};

// https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-activedescendant
export const AriaActivedescendant = (): React.ReactNode => {
const composite = useCompositeState({unstable_virtual: true});
return (
<Composite {...composite} role="toolbar" aria-label="My toolbar">
<CompositeItem {...composite} onClick={onClick(1)}>
Item 1
</CompositeItem>
<CompositeItem {...composite} onClick={onClick(2)}>
Item 2
</CompositeItem>
<CompositeItem {...composite} onClick={onClick(3)}>
Item 3
</CompositeItem>
</Composite>
);
};

const Grid: React.FC<any> = (props) => {
return <Composite role="grid" {...props} />;
};

const GridRow: React.FC<any> = (props) => {
return <CompositeGroup role="row" {...props} style={{display: 'flex', columnGap: '10px'}} />;
};

const GridCell: React.FC<any> = (props) => {
return <CompositeItem as="div" role="gridcell" {...props} onClick={onClick(props.currentId)} />;
};

export const TwoDimensional = (): React.ReactNode => {
const composite = useCompositeState({wrap: true});
return (
<Grid {...composite} aria-label="My grid">
<GridRow {...composite}>
<GridCell {...composite}>Item</GridCell>
<GridCell {...composite}>Item</GridCell>
<GridCell {...composite}>Item</GridCell>
<GridCell {...composite}>Item</GridCell>
<GridCell {...composite}>Item</GridCell>
</GridRow>
<GridRow {...composite}>
<GridCell {...composite}>Item</GridCell>
<GridCell {...composite}>Item</GridCell>
<GridCell {...composite}>Item</GridCell>
<GridCell {...composite}>Item</GridCell>
<GridCell {...composite}>Item</GridCell>
</GridRow>
<GridRow {...composite}>
<GridCell {...composite}>Item</GridCell>
<GridCell {...composite}>Item</GridCell>
<GridCell {...composite}>Item</GridCell>
<GridCell {...composite}>Item</GridCell>
<GridCell {...composite}>Item</GridCell>
</GridRow>
</Grid>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ const DualExample = () => {
</Stack>
</ListboxPrimitive>
<Button variant="primary_icon" onClick={() => {
updateFavs(favs => [...favs, ...Array.from(selectedComps)]);
updateFavs([...favs, ...Array.from(selectedComps)]);
updateComponents(components.filter((item) => !selectedComps.has(item)));
selectedComps.clear();
}}
Expand Down Expand Up @@ -227,7 +227,7 @@ const DualExample = () => {
</Stack>
</ListboxPrimitive>
<Button variant="primary_icon" onClick={() => {
updateComponents(components => [...components, ...Array.from(selectedFavs)]);
updateComponents([...components, ...Array.from(selectedFavs)]);
updateFavs(favs.filter((item) => !selectedFavs.has(item)));
selectedFavs.clear();
}}
Expand Down

0 comments on commit da61a7b

Please sign in to comment.