From a89634ff76b20f08d5c8ea085a3716e2d7120106 Mon Sep 17 00:00:00 2001 From: Arianna Kellogg Date: Tue, 9 Mar 2021 14:21:58 -0800 Subject: [PATCH] feat!: Update Accordion component default behavior to match USWDS (#922) BREAKING CHANGE: If the multiselectable behavior is desired, the multiselectable prop must now be passed to the Accordion component --- .../Accordion/Accordion.stories.tsx | 4 ++ src/components/Accordion/Accordion.test.tsx | 71 +++++++++++++++++++ src/components/Accordion/Accordion.tsx | 17 +++-- 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/components/Accordion/Accordion.stories.tsx b/src/components/Accordion/Accordion.stories.tsx index a17f4d783a..857dbe1863 100644 --- a/src/components/Accordion/Accordion.stories.tsx +++ b/src/components/Accordion/Accordion.stories.tsx @@ -99,6 +99,10 @@ export const bordered = (): React.ReactElement => ( ) +export const multiselectable = (): React.ReactElement => ( + +) + const customTestItems = [ { title: ( diff --git a/src/components/Accordion/Accordion.test.tsx b/src/components/Accordion/Accordion.test.tsx index 6a7dcc489b..26337984e3 100644 --- a/src/components/Accordion/Accordion.test.tsx +++ b/src/components/Accordion/Accordion.test.tsx @@ -151,6 +151,77 @@ describe('Accordion component', () => { }) }) + describe('when multiselectable is false (default behavior)', () => { + it('when an item is opened, clicking a different item closes the previously opened item', () => { + const { getByText, getByTestId } = render() + + expect(getByTestId(`accordionItem_${testItems[0].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[1].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[2].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[3].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[4].id}`)).not.toBeVisible() + + fireEvent.click(getByText(testItems[3].title)) + fireEvent.click(getByText(testItems[1].title)) + + expect(getByTestId(`accordionItem_${testItems[0].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[1].id}`)).toBeVisible() + expect(getByTestId(`accordionItem_${testItems[2].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[3].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[4].id}`)).not.toBeVisible() + + fireEvent.click(getByText(testItems[4].title)) + fireEvent.click(getByText(testItems[2].title)) + + expect(getByTestId(`accordionItem_${testItems[0].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[1].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[2].id}`)).toBeVisible() + expect(getByTestId(`accordionItem_${testItems[3].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[4].id}`)).not.toBeVisible() + }) + }) + + describe('when multiselectable is true', () => { + it('when an item is opened, previously open items remain open', () => { + const { getByText, getByTestId } = render( + + ) + + expect(getByTestId(`accordionItem_${testItems[0].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[1].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[2].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[3].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[4].id}`)).not.toBeVisible() + + fireEvent.click(getByText(testItems[0].title)) + fireEvent.click(getByText(testItems[1].title)) + + expect(getByTestId(`accordionItem_${testItems[0].id}`)).toBeVisible() + expect(getByTestId(`accordionItem_${testItems[1].id}`)).toBeVisible() + expect(getByTestId(`accordionItem_${testItems[2].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[3].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[4].id}`)).not.toBeVisible() + + fireEvent.click(getByText(testItems[0].title)) + fireEvent.click(getByText(testItems[3].title)) + + expect(getByTestId(`accordionItem_${testItems[0].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[1].id}`)).toBeVisible() + expect(getByTestId(`accordionItem_${testItems[2].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[3].id}`)).toBeVisible() + expect(getByTestId(`accordionItem_${testItems[4].id}`)).not.toBeVisible() + + fireEvent.click(getByText(testItems[2].title)) + fireEvent.click(getByText(testItems[4].title)) + + expect(getByTestId(`accordionItem_${testItems[0].id}`)).not.toBeVisible() + expect(getByTestId(`accordionItem_${testItems[1].id}`)).toBeVisible() + expect(getByTestId(`accordionItem_${testItems[2].id}`)).toBeVisible() + expect(getByTestId(`accordionItem_${testItems[3].id}`)).toBeVisible() + expect(getByTestId(`accordionItem_${testItems[4].id}`)).toBeVisible() + }) + }) + describe('with expanded items on mount', () => { const testExpandedItems = [ { diff --git a/src/components/Accordion/Accordion.tsx b/src/components/Accordion/Accordion.tsx index f65aeecca8..a1a6553730 100644 --- a/src/components/Accordion/Accordion.tsx +++ b/src/components/Accordion/Accordion.tsx @@ -12,6 +12,7 @@ interface AccordionItem { interface AccordionProps { bordered?: boolean + multiselectable?: boolean items: AccordionItem[] className?: string } @@ -51,7 +52,7 @@ export const AccordionItem = (props: AccordionItem): React.ReactElement => { } export const Accordion = (props: AccordionProps): React.ReactElement => { - const { bordered, items, className } = props + const { bordered, items, className, multiselectable = false } = props const [openItems, setOpenState] = useState( items.filter((i) => !!i.expanded).map((i) => i.id) @@ -68,18 +69,26 @@ export const Accordion = (props: AccordionProps): React.ReactElement => { const toggleItem = (itemId: AccordionItem['id']): void => { const newOpenItems = [...openItems] const itemIndex = openItems.indexOf(itemId) + const isMultiselectable = multiselectable if (itemIndex > -1) { newOpenItems.splice(itemIndex, 1) } else { - newOpenItems.push(itemId) + if (isMultiselectable) { + newOpenItems.push(itemId) + } else { + newOpenItems.splice(0, newOpenItems.length) + newOpenItems.push(itemId) + } } - setOpenState(newOpenItems) } return ( -
+
{items.map((item, i) => (