Skip to content

Commit

Permalink
feat(components): add select all/none button to dropdown selector #361
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasKellerer committed Jul 31, 2024
1 parent c7b1b3b commit fdce127
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 11 deletions.
104 changes: 93 additions & 11 deletions components/src/preact/components/checkbox-selector.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { type Meta, type StoryObj } from '@storybook/preact';
import { expect, fn, waitFor, within } from '@storybook/test';
import { type FunctionComponent } from 'preact';
import { useState } from 'preact/hooks';

import { type CheckboxItem, CheckboxSelector, type CheckboxSelectorProps } from './checkbox-selector';

Expand All @@ -14,12 +16,33 @@ const meta: Meta<CheckboxSelectorProps> = {

export default meta;

const WrapperWithState: FunctionComponent<CheckboxSelectorProps> = ({
items: initialItems,
label,
setItems: setItemsMock,
}) => {
const [items, setItems] = useState<CheckboxItem[]>(initialItems);

return (
<div className='w-32'>
<CheckboxSelector
items={items}
label={label}
setItems={(items: CheckboxItem[]) => {
setItemsMock(items);
setItems(items);
}}
/>
</div>
);
};

export const CheckboxSelectorStory: StoryObj<CheckboxSelectorProps> = {
render: (args) => {
let wrapperStateItems = args.items;

return (
<CheckboxSelector
<WrapperWithState
items={wrapperStateItems}
label={args.label}
setItems={(items: CheckboxItem[]) => {
Expand All @@ -37,20 +60,79 @@ export const CheckboxSelectorStory: StoryObj<CheckboxSelectorProps> = {
label: 'Some label',
setItems: fn(),
},
play: async ({ canvasElement, args }) => {
play: async ({ canvasElement, args, step }) => {
const canvas = within(canvasElement);

const open = () => canvas.getByText('Some label', { exact: false });
const open = () => canvas.getByText('Some label');
const selectAll = () => canvas.getByText('Select all');
const selectNone = () => canvas.getByText('Select none');
const firstItem = () => canvas.getByLabelText('item1');
open().click();

const item1 = canvas.getByLabelText('item1', { exact: false });
item1.click();
await step('Select one item', async () => {
firstItem().click();

await waitFor(() =>
expect(args.setItems).toHaveBeenCalledWith([
{ checked: true, label: 'item1' },
{ checked: false, label: 'item2' },
]),
);
await waitFor(() =>
expect(args.setItems).toHaveBeenCalledWith([
{ checked: true, label: 'item1' },
{ checked: false, label: 'item2' },
]),
);
});

await step('Select all items with one item already selected', async () => {
selectAll().click();

await waitFor(() =>
expect(args.setItems).toHaveBeenCalledWith([
{ checked: true, label: 'item1' },
{ checked: true, label: 'item2' },
]),
);
});

await step('Deselect one item', async () => {
firstItem().click();

await waitFor(() =>
expect(args.setItems).toHaveBeenCalledWith([
{ checked: false, label: 'item1' },
{ checked: true, label: 'item2' },
]),
);
});

await step('Select none with one item already selected', async () => {
selectNone().click();

await waitFor(() =>
expect(args.setItems).toHaveBeenCalledWith([
{ checked: false, label: 'item1' },
{ checked: false, label: 'item2' },
]),
);
});

await step('Select all items with none selected', async () => {
selectAll().click();

await waitFor(() =>
expect(args.setItems).toHaveBeenCalledWith([
{ checked: true, label: 'item1' },
{ checked: true, label: 'item2' },
]),
);
});

await step('Select none with all items selected', async () => {
selectNone().click();

await waitFor(() =>
expect(args.setItems).toHaveBeenCalledWith([
{ checked: false, label: 'item1' },
{ checked: false, label: 'item2' },
]),
);
});
},
};
19 changes: 19 additions & 0 deletions components/src/preact/components/checkbox-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,25 @@ export const CheckboxSelector = <Item extends CheckboxItem>({
}: CheckboxSelectorProps<Item>) => {
return (
<Dropdown buttonTitle={label} placement={'bottom-start'}>
<button
className='btn btn-xs btn-ghost'
onClick={() => {
const newItems = items.map((item) => ({ ...item, checked: true }));
setItems(newItems);
}}
>
Select all
</button>
<button
className='btn btn-xs btn-ghost'
onClick={() => {
const newItems = items.map((item) => ({ ...item, checked: false }));
setItems(newItems);
}}
>
Select none
</button>
<div className='divider mt-0 mb-0' />
<ul>
{items.map((item, index) => (
<li className='flex flex-row items-center' key={item.label}>
Expand Down

0 comments on commit fdce127

Please sign in to comment.