Skip to content

Commit

Permalink
feat(filters): make filters components controlled
Browse files Browse the repository at this point in the history
  • Loading branch information
jinlee93 authored Aug 19, 2022
2 parents 97ae870 + d27cd3a commit e589c46
Show file tree
Hide file tree
Showing 5 changed files with 406 additions and 858 deletions.
4 changes: 4 additions & 0 deletions src/components/Filters/Filters.stories.module.css
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;
}
332 changes: 212 additions & 120 deletions src/components/Filters/Filters.stories.tsx
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();
}
},
};
Loading

0 comments on commit e589c46

Please sign in to comment.