-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(filters): make filters components controlled
- Loading branch information
Showing
5 changed files
with
406 additions
and
858 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.button-group__apply-only { | ||
display: grid; | ||
grid-template-columns: 1fr 1fr; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,150 +1,242 @@ | ||
import { BADGE } from '@geometricpanda/storybook-addon-badges'; | ||
import { StoryObj, Meta } from '@storybook/react'; | ||
import { within } from '@storybook/testing-library'; | ||
import isChromatic from 'chromatic/isChromatic'; | ||
import React from 'react'; | ||
|
||
import { Filters } from './Filters'; | ||
import { FiltersDrawer } from '../FiltersDrawer/FiltersDrawer'; | ||
import styles from './Filters.stories.module.css'; | ||
import { Checkbox } from '../Checkbox/Checkbox'; | ||
|
||
export default { | ||
title: 'Organisms/Tables/Filters', | ||
title: 'Organisms/Interactive/Filters', | ||
component: Filters, | ||
parameters: { | ||
badges: [BADGE.BETA], | ||
}, | ||
args: { | ||
checkboxFields: [ | ||
{ | ||
legend: 'Filters Segment 1', | ||
checkboxes: [ | ||
{ label: 'Filters label 1', value: 'Filters label 1' }, | ||
{ label: 'Filters label 2', value: 'Filters label 2' }, | ||
{ label: 'Filters label 3', value: 'Filters label 3' }, | ||
], | ||
}, | ||
], | ||
onClose: (checkedValues) => { | ||
console.log(checkedValues); | ||
}, | ||
triggerText: 'Filters', | ||
hasSelectedFilters: false, | ||
children: ( | ||
<Filters.FiltersCheckboxField legend="Filters Segment 1"> | ||
<Checkbox | ||
label="Filters label 1" | ||
onChange={ | ||
() => {} /* eslint-disable-line @typescript-eslint/no-empty-function */ | ||
} | ||
/> | ||
<Checkbox | ||
label="Filters label 2" | ||
onChange={ | ||
() => {} /* eslint-disable-line @typescript-eslint/no-empty-function */ | ||
} | ||
/> | ||
<Checkbox | ||
label="Filters label 3" | ||
onChange={ | ||
() => {} /* eslint-disable-line @typescript-eslint/no-empty-function */ | ||
} | ||
/> | ||
</Filters.FiltersCheckboxField> | ||
), | ||
}, | ||
decorators: [ | ||
(Story) => ( | ||
<div style={{ margin: '0.25rem', height: '100vh' }}> | ||
<Story /> | ||
</div> | ||
), | ||
], | ||
} as Meta<Args>; | ||
|
||
type Args = React.ComponentProps<typeof Filters>; | ||
|
||
export const DefaultInteractive: StoryObj<Args> = {}; | ||
|
||
const overflowCheckboxFields = [ | ||
{ | ||
legend: 'Filters Segment 1', | ||
checkboxes: [ | ||
{ | ||
label: | ||
'Filters long label 1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod', | ||
value: 'segment1label1', | ||
}, | ||
{ label: 'Filters label 2', value: 'segment1label2' }, | ||
{ label: 'Filters label 3', value: 'segment1label3' }, | ||
], | ||
}, | ||
{ | ||
legend: 'Filters Segment 2', | ||
checkboxes: [ | ||
{ label: 'Filters label 1', value: 'segment2label1' }, | ||
{ | ||
label: | ||
'Filters long label 2 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod', | ||
value: 'segment2label2', | ||
}, | ||
{ label: 'Filters label 3', value: 'segment2label3' }, | ||
], | ||
export const Default: StoryObj<Args> = { | ||
play: async ({ canvasElement }) => { | ||
// We want to test visual regression for the drawer as well as the button, but don't want the drawer open initally outside Chromatic | ||
if (isChromatic()) { | ||
const canvas = within(canvasElement); | ||
const filtersTrigger = await canvas.findByRole('button'); | ||
filtersTrigger.click(); | ||
} | ||
}, | ||
{ | ||
legend: 'Filters Segment 3', | ||
checkboxes: [ | ||
{ label: 'Filters label 1', value: 'segment3label1' }, | ||
{ label: 'Filters label 2', value: 'segment3label2' }, | ||
{ | ||
label: | ||
'Filters long label 3 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod', | ||
value: 'segment3label3', | ||
}, | ||
], | ||
}, | ||
{ | ||
legend: 'Filters Segment 4', | ||
checkboxes: [ | ||
{ label: 'Filters label 1', value: 'segment4label1' }, | ||
{ label: 'Filters label 2', value: 'segment4label2' }, | ||
{ label: 'Filters label 3', value: 'segment4label3' }, | ||
{ label: 'Filters label 4', value: 'segment4label4' }, | ||
{ label: 'Filters label 5', value: 'segment4label5' }, | ||
{ label: 'Filters label 6', value: 'segment4label6' }, | ||
], | ||
}, | ||
]; | ||
|
||
const checkedMap = {}; | ||
overflowCheckboxFields.forEach(({ checkboxes }) => { | ||
checkboxes.forEach(({ value }) => { | ||
checkedMap[value] = false; | ||
}); | ||
}); | ||
}; | ||
|
||
export const OverflowInteractive: StoryObj<Args> = { | ||
export const WithOnClear: StoryObj<Args> = { | ||
...Default, | ||
args: { | ||
checkboxFields: overflowCheckboxFields, | ||
onClose: (checkedValues) => { | ||
console.log(checkedValues); | ||
}, | ||
onClear: | ||
() => {} /* eslint-disable-line @typescript-eslint/no-empty-function */, | ||
}, | ||
}; | ||
|
||
type FiltersDrawerArgs = React.ComponentProps<typeof FiltersDrawer>; | ||
/** | ||
* 1) Axe testing throws error due to Drawer component not being controlled, but this story is mostly for visual regression testing as the interactive stories would only capture the toggle button. | ||
*/ | ||
export const FiltersDrawerComponent: StoryObj<FiltersDrawerArgs> = { | ||
export const WithOnApplyAndCustomButtonGroup: StoryObj<Args> = { | ||
...Default, | ||
args: { | ||
checkboxFields: overflowCheckboxFields, | ||
checkedMap: { ...checkedMap }, | ||
onClose: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function | ||
isActive: true, | ||
}, | ||
render: (args) => <FiltersDrawer {...args} />, | ||
parameters: { | ||
axe: { | ||
disabledRules: ['aria-allowed-role'] /* 1 */, | ||
}, | ||
footerButtonGroupClassName: styles['button-group__apply-only'], | ||
onApply: | ||
() => {} /* eslint-disable-line @typescript-eslint/no-empty-function */, | ||
}, | ||
}; | ||
|
||
export const FiltersDrawerComponentNoLegend: StoryObj<FiltersDrawerArgs> = { | ||
args: { | ||
checkboxFields: [ | ||
{ | ||
checkboxes: [ | ||
{ | ||
label: 'Checkbox label 1', | ||
value: 'checkbox1', | ||
}, | ||
{ | ||
label: 'Checkbox label 2', | ||
value: 'checkbox2', | ||
}, | ||
{ | ||
label: 'Checkbox label 3', | ||
value: 'checkbox3', | ||
}, | ||
], | ||
}, | ||
], | ||
checkedMap: { checkbox1: false, checkbox2: false, checkbox3: false }, | ||
onClose: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function | ||
isActive: true, | ||
}, | ||
render: (args) => <FiltersDrawer {...args} />, | ||
parameters: { | ||
axe: { | ||
disabledRules: ['aria-allowed-role'] /* 1 */, | ||
const OverflowCheckboxFields = () => { | ||
const initialCheckedState = [ | ||
false, | ||
false, | ||
false, | ||
false, | ||
false, | ||
false, | ||
false, | ||
false, | ||
false, | ||
false, | ||
false, | ||
false, | ||
false, | ||
false, | ||
false, | ||
]; | ||
const [transientChecked, setTransientChecked] = | ||
React.useState(initialCheckedState); | ||
const onCheckboxChange = (index: number) => { | ||
const newTransientChecked = [...transientChecked]; | ||
newTransientChecked[index] = !transientChecked[index]; | ||
setTransientChecked(newTransientChecked); | ||
}; | ||
|
||
const [appliedChecked, setAppliedChecked] = | ||
React.useState(initialCheckedState); | ||
const onClear = () => { | ||
setTransientChecked(initialCheckedState); | ||
setAppliedChecked(initialCheckedState); | ||
}; | ||
const onClose = () => { | ||
setTransientChecked([...appliedChecked]); | ||
}; | ||
const onApply = () => { | ||
setAppliedChecked([...transientChecked]); | ||
}; | ||
|
||
/** | ||
* Counts how many filters have been applied. | ||
*/ | ||
const filterCount = Object.values(appliedChecked).reduce( | ||
(count: number, status) => { | ||
if (status) return count + 1; | ||
else return count; | ||
}, | ||
0, | ||
); | ||
|
||
const hasSelectedFilters = filterCount > 0; | ||
const triggerText = hasSelectedFilters | ||
? `Filters (${filterCount})` | ||
: 'Filters'; | ||
|
||
return ( | ||
<Filters | ||
hasSelectedFilters={hasSelectedFilters} | ||
onApply={onApply} | ||
onClear={onClear} | ||
onClose={onClose} | ||
triggerText={triggerText} | ||
> | ||
<Filters.FiltersCheckboxField legend="Filters Segment 1"> | ||
<Checkbox | ||
checked={transientChecked[0]} | ||
label="Filters long label 1 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod" | ||
onChange={() => onCheckboxChange(0)} | ||
/> | ||
<Checkbox | ||
checked={transientChecked[1]} | ||
label="Filters label 2" | ||
onChange={() => onCheckboxChange(1)} | ||
/> | ||
<Checkbox | ||
checked={transientChecked[2]} | ||
label="Filters label 3" | ||
onChange={() => onCheckboxChange(2)} | ||
/> | ||
</Filters.FiltersCheckboxField> | ||
<Filters.FiltersCheckboxField legend="Filters Segment 2"> | ||
<Checkbox | ||
checked={transientChecked[3]} | ||
label="Filters label 1" | ||
onChange={() => onCheckboxChange(3)} | ||
/> | ||
<Checkbox | ||
checked={transientChecked[4]} | ||
label="Filters long label 2 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod" | ||
onChange={() => onCheckboxChange(4)} | ||
/> | ||
<Checkbox | ||
checked={transientChecked[5]} | ||
label="Filters label 3" | ||
onChange={() => onCheckboxChange(5)} | ||
/> | ||
</Filters.FiltersCheckboxField> | ||
<Filters.FiltersCheckboxField legend="Filters Segment 3"> | ||
<Checkbox | ||
checked={transientChecked[6]} | ||
label="Filters label 1" | ||
onChange={() => onCheckboxChange(6)} | ||
/> | ||
<Checkbox | ||
checked={transientChecked[7]} | ||
label="Filters label 2" | ||
onChange={() => onCheckboxChange(7)} | ||
/> | ||
<Checkbox | ||
checked={transientChecked[8]} | ||
label="Filters long label 3 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod" | ||
onChange={() => onCheckboxChange(8)} | ||
/> | ||
</Filters.FiltersCheckboxField> | ||
<Filters.FiltersCheckboxField legend="Filters Segment 4"> | ||
<Checkbox | ||
checked={transientChecked[9]} | ||
label="Filters label 1" | ||
onChange={() => onCheckboxChange(9)} | ||
/> | ||
<Checkbox | ||
checked={transientChecked[10]} | ||
label="Filters label 2" | ||
onChange={() => onCheckboxChange(10)} | ||
/> | ||
<Checkbox | ||
checked={transientChecked[11]} | ||
label="Filters label 3" | ||
onChange={() => onCheckboxChange(11)} | ||
/> | ||
<Checkbox | ||
checked={transientChecked[12]} | ||
label="Filters label 4" | ||
onChange={() => onCheckboxChange(12)} | ||
/> | ||
<Checkbox | ||
checked={transientChecked[13]} | ||
label="Filters label 5" | ||
onChange={() => onCheckboxChange(13)} | ||
/> | ||
<Checkbox | ||
checked={transientChecked[14]} | ||
label="Filters label 6" | ||
onChange={() => onCheckboxChange(14)} | ||
/> | ||
</Filters.FiltersCheckboxField> | ||
</Filters> | ||
); | ||
}; | ||
|
||
export const OverflowInteractive: StoryObj<Args> = { | ||
render: () => <OverflowCheckboxFields />, | ||
play: async ({ canvasElement }) => { | ||
// We want to test visual regression for the drawer as well as the button, but don't want the drawer open initally outside Chromatic | ||
if (isChromatic()) { | ||
const canvas = within(canvasElement); | ||
const filtersTrigger = await canvas.findByRole('button'); | ||
filtersTrigger.click(); | ||
} | ||
}, | ||
}; |
Oops, something went wrong.