diff --git a/CHANGELOG.md b/CHANGELOG.md index efb4d9d..7367b76 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,23 @@ > All notable changes to this project are documented in this file. This project > adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [[v6.0.0]](https://github.com/springload/react-accessible-accordion/releases/tag/v6.0.0) + +**A sort-of breaking change** which updates the default behavior regarding +`allowMultipleExpanded` and `allowZeroExpanded`. They both now default to +`true`. + +This is based on +[advice from NNG](https://www.nngroup.com/articles/accordions-on-desktop/), as +well as Springload's views on accordion usability. + +From this version onwards: + +- to disable allowing more than one accordion panel to be open at once, you + must explicitly set `allowMultipleExpanded={false}` +- to disable collapsing all accordion panels, you must explicitly set + `allowZeroExpanded={false}` + ## [[v5.0.0]](https://github.com/springload/react-accessible-accordion/releases/tag/v5.0.0) React Accessible Accordion now supports React 18 with its out-of-order streaming diff --git a/README.md b/README.md index 328d395..a0e8d27 100644 --- a/README.md +++ b/README.md @@ -85,11 +85,11 @@ needs, particularly if you're using your own `className`s. ### Accordion -#### allowMultipleExpanded : `boolean` [*optional*, default: `false`] +#### allowMultipleExpanded : `boolean` [*optional*, default: `true`] -Don't autocollapse items when expanding other items. +Don't auto-collapse items when expanding other items. -#### allowZeroExpanded : `boolean` [*optional*, default: `false`] +#### allowZeroExpanded : `boolean` [*optional*, default: `true`] Allow the only remaining expanded item to be collapsed. diff --git a/demo/src/code-examples.tsx b/demo/src/code-examples.tsx index 37c7970..9704d7e 100644 --- a/demo/src/code-examples.tsx +++ b/demo/src/code-examples.tsx @@ -21,21 +21,6 @@ export const ExampleDefault = `import { ))} `; -export const ExampleAllowMultipleExpanded = ` - {items.map((item) => ( - - - - {item.heading} - - - - {item.content} - - - ))} -`; - export const ExampleAllowMultipleExpandedFalse = ` {items.map((item) => ( @@ -51,7 +36,7 @@ export const ExampleAllowMultipleExpandedFalse = ``; -export const ExampleAllowZeroExpanded = ` +export const ExampleAllowZeroExpandedFalse = ` {items.map((item) => ( diff --git a/demo/src/index.tsx b/demo/src/index.tsx index e61a455..192b4af 100644 --- a/demo/src/index.tsx +++ b/demo/src/index.tsx @@ -18,9 +18,8 @@ import Code from './components/Code'; // tslint:disable-next-line no-import-side-effect ordered-imports import { ExampleDefault, - ExampleAllowMultipleExpanded, ExampleAllowMultipleExpandedFalse, - ExampleAllowZeroExpanded, + ExampleAllowZeroExpandedFalse, ExamplePreExpanded, ExampleOnChange, ExampleAccordionItemState, @@ -54,8 +53,7 @@ const App = (): JSX.Element => (

Default behaviour

- By default, only one item may be expanded and it can only be - collapsed again by expanding another. + By default, any number of items may be expanded at any given time.

@@ -73,32 +71,15 @@ const App = (): JSX.Element => ( -

Expanding multiple items at once

- -

- If you set allowMultipleExpanded to{' '} - true then the accordion will permit multiple items - to be expanded at once. -

- - - {placeholders.map((placeholder: Placeholder) => ( - - - - {placeholder.heading} - - - {placeholder.panel} - - ))} - - - -

- Same as above except with allowMultipleExpanded=false + Prevent multiple items being expanded at a time

+

+ Note: we do not recommend this behavior. Users may + wish to view the content of more than one panel at once. Also, + collapsing a panel when opening another can cause unexpected scroll + position changes. +

{placeholders.map((placeholder: Placeholder) => ( @@ -115,17 +96,25 @@ const App = (): JSX.Element => ( -

Collapsing the last expanded item

+

Pre-expanded items

- If you set allowZeroExpanded to{' '} - true then a solitary expanded item may be collapsed - again. + If you set preExpanded, then you can choose which + items are expanded on mount. +

+ +

+ The strings passed to preExpanded are directly + related to the uuid props of{' '} + AccordionItem.

- + {placeholders.map((placeholder: Placeholder) => ( - + {placeholder.heading} @@ -136,22 +125,27 @@ const App = (): JSX.Element => ( ))} - + -

Pre-expanded items

+

Preventing the collapsing of all items

- If you set preExpanded, then you can choose which - items are expanded on mount. + If you set allowZeroExpanded to{' '} + false then the user must have at least one panel + open at a time.

-

- The strings passed to preExpanded are directly - related to the uuid props of{' '} - AccordionItem. + Note: we do not recommend this behavior. Users + would be able to expand items but not necessarily collapse them, + which might not match their expectations. If you do choose to use + this setting, we recommend you pair it with having{' '} + preExpanded item(s).

- + {placeholders.map((placeholder: Placeholder) => ( ( ))} - +

Informative onChange

diff --git a/integration/wai-aria.spec.js b/integration/wai-aria.spec.js index b7b2141..d920c9b 100644 --- a/integration/wai-aria.spec.js +++ b/integration/wai-aria.spec.js @@ -365,23 +365,6 @@ describe('WAI ARIA Spec', () => { } }); - it(`If the accordion panel associated with an accordion header is - visible, and if the accordion does not permit the panel to be - collapsed, the header button element has aria-disabled set to true.`, async () => { - const { browser, page, buttonsHandles } = await setup(); - expect(buttonsHandles.length).toEqual(3); - - const [firstButtonHandle] = buttonsHandles; - await firstButtonHandle.click(); - - const buttonAriaDisabled = await page.evaluate( - (button) => button.getAttribute('aria-disabled'), - firstButtonHandle, - ); - - expect(buttonAriaDisabled).toEqual('true'); - }); - it(`Optionally, each element that serves as a container for panel content has role region and aria-labelledby with a value that refers to the button that controls display of the panel.`, async () => { diff --git a/package.json b/package.json index 1634fa7..32153f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-accessible-accordion", - "version": "5.0.0", + "version": "6.0.0", "description": "Accessible Accordion component for React", "main": "dist/umd/index.js", "module": "dist/es/index.js", @@ -73,6 +73,10 @@ { "name": "Emilia Zapata", "url": "https://github.com/synecdokey" + }, + { + "name": "Liam Johnston", + "url": "https://github.com/liamjohnston" } ], "license": "MIT", diff --git a/src/components/Accordion.spec.tsx b/src/components/Accordion.spec.tsx index 58ee99a..216896f 100644 --- a/src/components/Accordion.spec.tsx +++ b/src/components/Accordion.spec.tsx @@ -39,9 +39,9 @@ describe('Accordion', () => { describe('expanding and collapsing: ', () => { describe('allowMultipleExpanded prop', () => { - it('permits multiple items to be expanded when explicitly true', () => { + it('prevents multiple items to be expanded when set to false', () => { const { getByTestId } = render( - + @@ -60,13 +60,13 @@ describe('Accordion', () => { expect( getByTestId(UUIDS.FOO).getAttribute('aria-expanded'), - ).toEqual('true'); + ).toEqual('false'); expect( getByTestId(UUIDS.BAR).getAttribute('aria-expanded'), ).toEqual('true'); }); - it('prevents multiple items being expanded by default', () => { + it('allows multiple items being expanded by default', () => { const { getByTestId } = render( @@ -87,7 +87,7 @@ describe('Accordion', () => { expect( getByTestId(UUIDS.FOO).getAttribute('aria-expanded'), - ).toEqual('false'); + ).toEqual('true'); expect( getByTestId(UUIDS.BAR).getAttribute('aria-expanded'), ).toEqual('true'); @@ -95,9 +95,9 @@ describe('Accordion', () => { }); describe('allowZeroExpanded prop', () => { - it('permits the last-expanded item to be collapsed when explicitly true', () => { + it('prevents the last-expanded item to be collapsed when explicitly false', () => { const { getByTestId } = render( - + @@ -106,15 +106,17 @@ describe('Accordion', () => { , ); + // open it fireEvent.click(getByTestId(UUIDS.FOO)); + // attempt to close it fireEvent.click(getByTestId(UUIDS.FOO)); expect( getByTestId(UUIDS.FOO).getAttribute('aria-expanded'), - ).toEqual('false'); + ).toEqual('true'); }); - it('prevents the last-expanded item being collapsed by default', () => { + it('allows the last-expanded to be collapsed by default', () => { const { getByTestId } = render( @@ -130,7 +132,7 @@ describe('Accordion', () => { expect( getByTestId(UUIDS.FOO).getAttribute('aria-expanded'), - ).toEqual('true'); + ).toEqual('false'); }); }); diff --git a/src/components/AccordionContext.tsx b/src/components/AccordionContext.tsx index 1509fed..1ed3120 100644 --- a/src/components/AccordionContext.tsx +++ b/src/components/AccordionContext.tsx @@ -42,8 +42,8 @@ export class Provider extends React.PureComponent< ProviderState > { static defaultProps: ProviderProps = { - allowMultipleExpanded: false, - allowZeroExpanded: false, + allowMultipleExpanded: true, + allowZeroExpanded: true, }; state: ProviderState = new AccordionStore({ diff --git a/src/helpers/AccordionStore.spec.ts b/src/helpers/AccordionStore.spec.ts index b917896..05e6ed9 100644 --- a/src/helpers/AccordionStore.spec.ts +++ b/src/helpers/AccordionStore.spec.ts @@ -53,9 +53,10 @@ describe('Accordion', () => { expect(container.expanded).toEqual([UUIDS.FOO]); }); - it('collapses the currently expanded items', () => { + it('collapses the currently expanded items, if allowMultipleExpanded is set to false', () => { const container = new AccordionStore({ expanded: [UUIDS.BAR], + allowMultipleExpanded: false, }).toggleExpanded(UUIDS.FOO); expect(container.expanded).toEqual([UUIDS.FOO]); @@ -63,52 +64,35 @@ describe('Accordion', () => { }); describe('collapsing', () => { - it('doesnt collapse the only expanded item', () => { + it('collapses the only expanded item', () => { const container = new AccordionStore({ expanded: [UUIDS.FOO], }).toggleExpanded(UUIDS.FOO); - expect(container.expanded).toEqual([UUIDS.FOO]); + expect(container.expanded).toEqual([]); }); - it('collapses the only expanded item when allowZeroExpanded', () => { + it('does not collapse the only expanded item when allowZeroExpanded is false', () => { const container = new AccordionStore({ - allowZeroExpanded: true, + allowZeroExpanded: false, expanded: [UUIDS.FOO], }).toggleExpanded(UUIDS.FOO); - expect(container.expanded).toEqual([]); + expect(container.expanded).toEqual([UUIDS.FOO]); }); }); }); describe('isDisabled', () => { describe('expanded item', () => { - it('is disabled if alone', () => { + it('is disabled if alone, if allowZeroExpanded is set to false', () => { const container = new AccordionStore({ + allowZeroExpanded: false, expanded: [UUIDS.FOO], }); expect(container.isItemDisabled(UUIDS.FOO)).toEqual(true); }); - - it('is not disabled if multiple expanded', () => { - const container = new AccordionStore({ - allowMultipleExpanded: true, - expanded: [UUIDS.FOO, UUIDS.BAR], - }); - - expect(container.isItemDisabled(UUIDS.FOO)).toEqual(false); - }); - - it('is not disabled if allowZeroExpanded', () => { - const container = new AccordionStore({ - allowZeroExpanded: true, - expanded: [UUIDS.FOO], - }); - - expect(container.isItemDisabled(UUIDS.FOO)).toEqual(false); - }); }); describe('collapsed item', () => { diff --git a/src/helpers/AccordionStore.ts b/src/helpers/AccordionStore.ts index 3c85e3d..e55a6f3 100644 --- a/src/helpers/AccordionStore.ts +++ b/src/helpers/AccordionStore.ts @@ -28,8 +28,8 @@ export default class AccordionStore { constructor({ expanded = [], - allowMultipleExpanded = false, - allowZeroExpanded = false, + allowMultipleExpanded = true, + allowZeroExpanded = true, }: { expanded?: ID[]; allowMultipleExpanded?: boolean; diff --git a/src/helpers/focus.spec.tsx b/src/helpers/focus.spec.tsx index ee947ab..c1e9dcb 100644 --- a/src/helpers/focus.spec.tsx +++ b/src/helpers/focus.spec.tsx @@ -200,15 +200,12 @@ describe('focus', () => { `); - const [ - firstButton, - secondButton, - thirdButton, - ]: HTMLElement[] = Array.from( - tree.querySelectorAll( - '[data-accordion-component="AccordionItemButton"]', - ), - ); + const [firstButton, secondButton, thirdButton]: HTMLElement[] = + Array.from( + tree.querySelectorAll( + '[data-accordion-component="AccordionItemButton"]', + ), + ); // Predicate if ( @@ -239,15 +236,12 @@ describe('focus', () => { `); - const [ - firstButton, - secondButton, - thirdButton, - ]: HTMLElement[] = Array.from( - tree.querySelectorAll( - '[data-accordion-component="AccordionItemButton"]', - ), - ); + const [firstButton, secondButton, thirdButton]: HTMLElement[] = + Array.from( + tree.querySelectorAll( + '[data-accordion-component="AccordionItemButton"]', + ), + ); // Predicate if ( @@ -278,15 +272,12 @@ describe('focus', () => { `); - const [ - firstButton, - secondButton, - thirdButton, - ]: HTMLElement[] = Array.from( - tree.querySelectorAll( - '[data-accordion-component="AccordionItemButton"]', - ), - ); + const [firstButton, secondButton, thirdButton]: HTMLElement[] = + Array.from( + tree.querySelectorAll( + '[data-accordion-component="AccordionItemButton"]', + ), + ); // Predicate if (