Skip to content

Commit

Permalink
feat!: Update Accordion component default behavior to match USWDS (#922)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: If the multiselectable behavior is desired, the multiselectable prop must now be passed to the Accordion component
  • Loading branch information
SirenaBorracha authored and brandonlenz committed May 12, 2021
1 parent 87e90c6 commit a89634f
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 4 deletions.
4 changes: 4 additions & 0 deletions src/components/Accordion/Accordion.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ export const bordered = (): React.ReactElement => (
<Accordion bordered={true} items={testItems} />
)

export const multiselectable = (): React.ReactElement => (
<Accordion items={testItems} multiselectable={true} />
)

const customTestItems = [
{
title: (
Expand Down
71 changes: 71 additions & 0 deletions src/components/Accordion/Accordion.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(<Accordion items={testItems} />)

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(
<Accordion items={testItems} multiselectable={true} />
)

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 = [
{
Expand Down
17 changes: 13 additions & 4 deletions src/components/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface AccordionItem {

interface AccordionProps {
bordered?: boolean
multiselectable?: boolean
items: AccordionItem[]
className?: string
}
Expand Down Expand Up @@ -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)
Expand All @@ -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 (
<div className={classes} data-testid="accordion">
<div
className={classes}
data-testid="accordion"
aria-multiselectable={multiselectable || undefined}>
{items.map((item, i) => (
<AccordionItem
key={`accordionItem_${i}`}
Expand Down

0 comments on commit a89634f

Please sign in to comment.