Skip to content

Commit

Permalink
#55 add prop to set accordion items open/closed
Browse files Browse the repository at this point in the history
add prop table to Readme

remove unused var

add isLocked prop to remove user control

rename to allOpen and allLocked
  • Loading branch information
jscottsmith committed Nov 6, 2018
1 parent 1b9e30d commit 76ff9ed
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 28 deletions.

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ $RECYCLE.BIN/

# App specific
.out/
.cache/
build/
node_modules/
dist/
Expand Down
16 changes: 14 additions & 2 deletions _stories/molecules/Accordion/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
The `<Accordion>` component is a collapsible container for holding related elements. Nest `<AccordionItem>` components inside `<Accordion>` for each drawer. Nest `<AccordionItemContent>` in each `<AccordionItem>` if you would like to display listed information within an open `<AccordionItem>`. Each `<Accordion>` related component accepts a className and otherProps so you are able to further customize the component.

_Accordion example_:
## Props

The following props may be passed to configure the Accordion:

| name | type | description | Required |
| --------- | --------- | ------------------------------------------------------------------------------- | -------- |
| children | `Node` | Children should be `<AccordionItem>` | Yes |
| context | `String` | Indicate the context of the Accordion. One of `dark`, `white`, or `undefined` | |
| allOpen | `Boolean` | Indicate if all `<AccordionItem>` children should be opened or closed | |
| allLocked | `Boolean` | Indicate if all `<AccordionItem>` children will be locked either open or closed | |
| size | `String` | Indicate the size of the Accordion. One of `sm` or `undefined` | |

## Examples

You can use `<Accordion>` to display any information that you pass in, such as:

Expand Down Expand Up @@ -40,6 +52,6 @@ You can also use `<Accordion>` to display information in a list form, in the cas
</Accordion>
```

**Keyboard Accessibility:**
## Keyboard Accessibility

When an accordion is in focus you can toggle it expanded/collapsed with the spacebar or enter keys.
6 changes: 4 additions & 2 deletions _stories/molecules/Accordion/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { text } from '@storybook/addon-knobs';
import { text, boolean } from '@storybook/addon-knobs';
import { optionalSelect } from '../../../components/utils/optionalSelect';

import readme from './README.md';
Expand All @@ -22,6 +22,8 @@ const contextOptions = {
const component = () => (
<Accordion
size={optionalSelect('Size', sizeOptions, '')}
allOpen={boolean('allOpen', false)}
allLocked={boolean('allLocked', false)}
context={optionalSelect('Context', contextOptions, '')}
className={text('Class', '')}>
<AccordionItem label={text('Item Label 1', 'Item 1')}>
Expand All @@ -35,7 +37,7 @@ const component = () => (
<Accordion
size={optionalSelect('Size', sizeOptions, '')}
context={optionalSelect('Context', contextOptions, '')}>
<AccordionItem label={text('Nested Item Label 1', 'Nested Item 1')}>
<AccordionItem label={text('Nested Item Label 1', 'Nested Item 1')} isLocked>
<AccordionItemContent>Nested Content</AccordionItemContent>
</AccordionItem>
</Accordion>
Expand Down
2 changes: 0 additions & 2 deletions _tests/atoms/__snapshots__/AccordionItem.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ exports[`Expect <AccordionItem> to render 1`] = `
>
<li
aria-controls="AccordionItem-region-QWNjb3JkaW9uSXRlbQ==0"
aria-expanded={false}
aria-pressed={false}
className="gds-accordion__item foo gds-accordion__item--danger"
onClick={[Function]}
onKeyPress={[Function]}
Expand Down
51 changes: 47 additions & 4 deletions _tests/molecules/Accordion.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* globals mount */
/* globals mount, shallow */
import React from 'react';
import Accordion from '../../components/molecules/Accordion';
import AccordionItem from '../../components/atoms/AccordionItem';

describe('Expect <Accordion>', () => {
it('to render', () => {
Expand All @@ -11,11 +12,53 @@ describe('Expect <Accordion>', () => {
};
const wrapper = mount(
<Accordion {...props}>
<li>Accordion Item 1</li>
<li>Accordion Item 2</li>
<li>Accordion Item 3</li>
<AccordionItem label="Accordion Item 1" />
<AccordionItem label="Accordion Item 2" />
<AccordionItem label="Accordion Item 3" />
</Accordion>
);
expect(wrapper).toMatchSnapshot();
});

it('to render expanded', () => {
const props = {
allOpen: true
};
const wrapper = mount(
<Accordion {...props}>
<AccordionItem label="Accordion Item 1" />
<AccordionItem label="Accordion Item 2" />
<AccordionItem label="Accordion Item 3" />
</Accordion>
);
wrapper.find('AccordionItem li').forEach(item => {
expect(item.hasClass('gds-accordion__item--active')).toBe(true);
});
wrapper.setProps({ allOpen: false });
wrapper.find('AccordionItem li').forEach(item => {
expect(item.hasClass('gds-accordion__item--active')).toBe(false);
});
});

it('to render locked', () => {
const props = {
allLocked: false
};

const wrapper = mount(
<Accordion {...props}>
<AccordionItem label="Accordion Item 1" />
<AccordionItem label="Accordion Item 2" />
<AccordionItem label="Accordion Item 3" />
</Accordion>
);

wrapper.find('AccordionItem li').forEach(item => {
expect(item.find('.gds-accordion__item-icon').length).toBe(1);
});
wrapper.setProps({ allLocked: true });
wrapper.find('AccordionItem li').forEach(item => {
expect(item.find('.gds-accordion__item-icon').length).toBe(0);
});
});
});
93 changes: 84 additions & 9 deletions _tests/molecules/__snapshots__/Accordion.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,102 @@ exports[`Expect <Accordion> to render 1`] = `
<ul
className="gds-accordion-list"
>
<li
<AccordionItem
context="primary"
key=".0"
label="Accordion Item 1"
size="sm"
>
Accordion Item 1
</li>
<li
<li
aria-controls="AccordionItem-region-QWNjb3JkaW9uSXRlbQ==0"
className="gds-accordion__item gds-accordion__item--primary"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex={0}
>
<h4
className="gds-accordion__item-title gds-accordion__item-title--sm"
id="AccordionItem-label-QWNjb3JkaW9uSXRlbQ==0"
>
Accordion Item 1
</h4>
<span
className="gds-accordion__item-icon gds-accordion__item-icon--sm"
/>
<ul
aria-hidden={true}
aria-labelledby="AccordionItem-label-QWNjb3JkaW9uSXRlbQ==0"
className="gds-accordion__child-items gds-accordion__child-items--sm"
id="AccordionItem-region-QWNjb3JkaW9uSXRlbQ==0"
role="region"
/>
</li>
</AccordionItem>
<AccordionItem
context="primary"
key=".1"
label="Accordion Item 2"
size="sm"
>
Accordion Item 2
</li>
<li
<li
aria-controls="AccordionItem-region-QWNjb3JkaW9uSXRlbQ==1"
className="gds-accordion__item gds-accordion__item--primary"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex={0}
>
<h4
className="gds-accordion__item-title gds-accordion__item-title--sm"
id="AccordionItem-label-QWNjb3JkaW9uSXRlbQ==1"
>
Accordion Item 2
</h4>
<span
className="gds-accordion__item-icon gds-accordion__item-icon--sm"
/>
<ul
aria-hidden={true}
aria-labelledby="AccordionItem-label-QWNjb3JkaW9uSXRlbQ==1"
className="gds-accordion__child-items gds-accordion__child-items--sm"
id="AccordionItem-region-QWNjb3JkaW9uSXRlbQ==1"
role="region"
/>
</li>
</AccordionItem>
<AccordionItem
context="primary"
key=".2"
label="Accordion Item 3"
size="sm"
>
Accordion Item 3
</li>
<li
aria-controls="AccordionItem-region-QWNjb3JkaW9uSXRlbQ==2"
className="gds-accordion__item gds-accordion__item--primary"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex={0}
>
<h4
className="gds-accordion__item-title gds-accordion__item-title--sm"
id="AccordionItem-label-QWNjb3JkaW9uSXRlbQ==2"
>
Accordion Item 3
</h4>
<span
className="gds-accordion__item-icon gds-accordion__item-icon--sm"
/>
<ul
aria-hidden={true}
aria-labelledby="AccordionItem-label-QWNjb3JkaW9uSXRlbQ==2"
className="gds-accordion__child-items gds-accordion__child-items--sm"
id="AccordionItem-region-QWNjb3JkaW9uSXRlbQ==2"
role="region"
/>
</li>
</AccordionItem>
</ul>
</div>
</Accordion>
Expand Down
23 changes: 17 additions & 6 deletions components/atoms/AccordionItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@ import charCodes from '../../constants/charCodes';

class AccordionItem extends Component {
state = {
isOpen: false
isOpen: this.props.isOpen
};

static getDerivedStateFromProps({ isOpen }) {
return {
isOpen
};
}

uid = generateUID(this);

toggleOpen = event => {
const { type, charCode } = event;
event.stopPropagation(); // don't want nested accordions to propagate events

if (this.props.isLocked) return;

if (
type === 'keypress' &&
(charCode === charCodes.SPACEBAR || charCode === charCodes.ENTER)
Expand All @@ -27,7 +35,8 @@ class AccordionItem extends Component {
};

render() {
const { size, context, className, children, label } = this.props;
const { size, context, className, children, label, isLocked } = this.props;

const { isOpen } = this.state;

const rootClass = cx('gds-accordion__item', className, {
Expand Down Expand Up @@ -66,11 +75,11 @@ class AccordionItem extends Component {
onClick={this.toggleOpen}
onKeyPress={this.toggleOpen}
role="button"
tabIndex={0}>
tabIndex={isLocked ? -1 : 0}>
<h4 id={labelId} className={titleClass}>
{label}
</h4>
<span className={iconClass} />
{!isLocked && <span className={iconClass} />}
<ul
aria-labelledby={labelId}
aria-hidden={!isOpen}
Expand All @@ -89,9 +98,11 @@ AccordionItem.displayName = 'AccordionItem';
AccordionItem.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
context: PropTypes.string,
isOpen: PropTypes.bool,
isLocked: PropTypes.bool,
label: PropTypes.string,
size: PropTypes.string,
context: PropTypes.string
size: PropTypes.string
};

export default AccordionItem;
8 changes: 6 additions & 2 deletions components/molecules/Accordion.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';

const Accordion = ({ children, context, size, className, ...otherProps }) => {
const Accordion = ({ children, context, size, className, allOpen, allLocked, ...otherProps }) => {
const rootClass = cx('gds-accordion', className, {
[`gds-accordion--${context}`]: context
});

const newChildren = React.Children.map(children, child => {
return React.cloneElement(child, {
context,
size
size,
isOpen: allOpen,
isLocked: allLocked
});
});

Expand All @@ -27,6 +29,8 @@ Accordion.propTypes = {
children: PropTypes.node.isRequired,
/** dark, white */
context: PropTypes.string,
allOpen: PropTypes.bool,
allLocked: PropTypes.bool,
/** sm */
size: PropTypes.oneOf(['sm']),
className: PropTypes.string
Expand Down

0 comments on commit 76ff9ed

Please sign in to comment.