react-accessible-accordion
Advanced tools
Comparing version 0.5.0 to 0.6.0
@@ -7,2 +7,10 @@ Changelog | ||
## [[v0.6.0]](https://github.com/springload/react-accessible-accordion/releases/tag/v0.6.0) | ||
- Improved accessibility support (Following https://github.com/springload/react-accessible-accordion/pull/19) | ||
- Adds possibility to programmatically open items(https://github.com/springload/react-accessible-accordion/pull/13) | ||
Thanks @epotockiy for the contribution | ||
- Improved accessibility status on demo page | ||
- Documentation about accessibility for this component | ||
## [[v0.5.0]](https://github.com/springload/react-accessible-accordion/releases/tag/v0.5.0) | ||
@@ -9,0 +17,0 @@ |
@@ -17,2 +17,4 @@ 'use strict'; | ||
var _utils = require('../utils'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -31,3 +33,4 @@ | ||
onChange: function onChange() {}, | ||
className: 'accordion' | ||
className: 'accordion', | ||
activeItems: [] | ||
}; | ||
@@ -38,2 +41,3 @@ | ||
children: _propTypes2.default.oneOfType([_propTypes2.default.arrayOf(_propTypes2.default.element), _propTypes2.default.object]).isRequired, | ||
activeItems: _propTypes2.default.arrayOf(_propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.number])), | ||
className: _propTypes2.default.string, | ||
@@ -61,2 +65,19 @@ onChange: _propTypes2.default.func | ||
_createClass(Accordion, [{ | ||
key: 'componentWillReceiveProps', | ||
value: function componentWillReceiveProps(nextProps) { | ||
if (!(0, _utils.isArraysEqualShallow)(nextProps.activeItems, this.state.activeItems)) { | ||
var newActiveItems = void 0; | ||
if (nextProps.accordion) { | ||
newActiveItems = nextProps.activeItems.length ? [nextProps.activeItems[0]] : []; | ||
} else { | ||
newActiveItems = nextProps.activeItems.slice(); | ||
} | ||
this.setState({ | ||
activeItems: newActiveItems | ||
}); | ||
nextProps.onChange(nextProps.accordion ? newActiveItems[0] : newActiveItems); | ||
} | ||
} | ||
}, { | ||
key: 'preExpandedItems', | ||
@@ -70,8 +91,11 @@ value: function preExpandedItems() { | ||
if (_this2.props.accordion) { | ||
if (activeItems.length === 0) activeItems.push(index); | ||
if (activeItems.length === 0) activeItems.push(item.props.customKey || index); | ||
} else { | ||
activeItems.push(index); | ||
activeItems.push(item.props.customKey || index); | ||
} | ||
} | ||
}); | ||
if (activeItems.length === 0 && this.props.activeItems.length !== 0) { | ||
activeItems = this.props.accordion ? [this.props.activeItems[0]] : this.props.activeItems.slice(); | ||
} | ||
return activeItems; | ||
@@ -113,3 +137,3 @@ } | ||
return _react2.default.Children.map(children, function (item, index) { | ||
var key = index; | ||
var key = item.props.customKey || index; | ||
var expanded = _this3.state.activeItems.indexOf(key) !== -1 && !item.props.disabled; | ||
@@ -129,7 +153,9 @@ | ||
value: function render() { | ||
var className = this.props.className; | ||
var _props2 = this.props, | ||
className = _props2.className, | ||
accordion = _props2.accordion; | ||
return _react2.default.createElement( | ||
'div', | ||
{ className: className }, | ||
{ role: accordion ? 'tablist' : null, className: className }, | ||
this.renderItems() | ||
@@ -136,0 +162,0 @@ ); |
@@ -205,2 +205,263 @@ 'use strict'; | ||
}); | ||
it('pre expand accordion via accordion props', function () { | ||
var tree = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordion2.default, | ||
{ activeItems: [0] }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
) | ||
)).toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
it('pre expand accordion via accordion props vs accordion item props. Expanded only second item.', function () { | ||
var tree = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordion2.default, | ||
{ activeItems: [0] }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
{ expanded: true }, | ||
'Fake Child' | ||
) | ||
)).toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
it('pre expand multiple accordions via accordion props', function () { | ||
var tree = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordion2.default, | ||
{ accordion: false, activeItems: [0, 2] }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
) | ||
)).toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
it('pre expand accordion via accordion props with custom key', function () { | ||
var tree = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordion2.default, | ||
{ activeItems: ['custom'] }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
{ customKey: 'custom' }, | ||
'Fake Child' | ||
) | ||
)).toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
it('expand accordion via accordion props dynamicly', function () { | ||
var wrapper = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordion2.default, | ||
{ activeItems: [0] }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
) | ||
)); | ||
wrapper.update(_react2.default.createElement( | ||
_accordion2.default, | ||
{ activeItems: [1] }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
) | ||
)); | ||
expect(wrapper.getInstance().state.activeItems).toEqual([1]); | ||
expect(wrapper).toMatchSnapshot(); | ||
}); | ||
it('expand multiple accordions via accordion props props dynamicly', function () { | ||
var wrapper = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordion2.default, | ||
{ accordion: false, activeItems: [0, 2] }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
) | ||
)); | ||
wrapper.update(_react2.default.createElement( | ||
_accordion2.default, | ||
{ accordion: false, activeItems: [1, 2] }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
) | ||
)); | ||
expect(wrapper.getInstance().state.activeItems).toEqual([1, 2]); | ||
expect(wrapper).toMatchSnapshot(); | ||
}); | ||
it('expand multiple accordions via accordion props props dynamicly with default\n expanded on accordion items', function () { | ||
var wrapper = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordion2.default, | ||
{ accordion: false }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
{ expanded: true }, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
) | ||
)); | ||
wrapper.update(_react2.default.createElement( | ||
_accordion2.default, | ||
{ accordion: false, activeItems: [1, 2] }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
{ expanded: true }, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
) | ||
)); | ||
expect(wrapper.getInstance().state.activeItems).toEqual([1, 2]); | ||
expect(wrapper).toMatchSnapshot(); | ||
}); | ||
it('close accordions via accordion props props dynamicly', function () { | ||
var wrapper = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordion2.default, | ||
{ activeItems: [1] }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
) | ||
)); | ||
wrapper.update(_react2.default.createElement( | ||
_accordion2.default, | ||
{ activeItems: [] }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
) | ||
)); | ||
expect(wrapper.getInstance().state.activeItems).toEqual([]); | ||
expect(wrapper).toMatchSnapshot(); | ||
}); | ||
it('different className with the same activeItems prop', function () { | ||
var wrapper = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordion2.default, | ||
{ activeItems: [1], className: 'test-1' }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
) | ||
)); | ||
wrapper.update(_react2.default.createElement( | ||
_accordion2.default, | ||
{ activeItems: [1], className: 'test-2' }, | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
), | ||
_react2.default.createElement( | ||
_accordionItem2.default, | ||
null, | ||
'Fake Child' | ||
) | ||
)); | ||
expect(wrapper).toMatchSnapshot(); | ||
}); | ||
}); |
@@ -21,4 +21,10 @@ 'use strict'; | ||
var _classnames = require('classnames'); | ||
var _classnames2 = _interopRequireDefault(_classnames); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -34,3 +40,4 @@ | ||
onClick: function onClick() {}, | ||
className: 'accordion__item' | ||
className: 'accordion__item', | ||
hideBodyClassName: null | ||
}; | ||
@@ -43,3 +50,4 @@ | ||
children: _propTypes2.default.arrayOf(_propTypes2.default.element).isRequired, | ||
className: _propTypes2.default.string | ||
className: _propTypes2.default.string, | ||
hideBodyClassName: _propTypes2.default.string | ||
}; | ||
@@ -89,3 +97,3 @@ | ||
itemProps.id = 'accordion__body-' + itemUuid; | ||
itemProps.role = accordion ? 'tabpanel' : ''; | ||
itemProps.role = accordion ? 'tabpanel' : null; | ||
@@ -101,7 +109,13 @@ return _react2.default.cloneElement(item, itemProps); | ||
value: function render() { | ||
var className = this.props.className; | ||
var _props2 = this.props, | ||
className = _props2.className, | ||
expanded = _props2.expanded, | ||
hideBodyClassName = _props2.hideBodyClassName; | ||
var itemClassName = (0, _classnames2.default)(className, _defineProperty({}, hideBodyClassName, !expanded && hideBodyClassName)); | ||
return _react2.default.createElement( | ||
'div', | ||
{ className: className }, | ||
{ className: itemClassName }, | ||
this.renderChildren() | ||
@@ -108,0 +122,0 @@ ); |
@@ -192,2 +192,28 @@ 'use strict'; | ||
it('renders with different hideBodyClassName', function () { | ||
var tree = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordionItem2.default, | ||
{ expanded: false, hideBodyClassName: 'testCSSClass--hidden' }, | ||
_react2.default.createElement( | ||
_accordionItemTitle2.default, | ||
null, | ||
_react2.default.createElement( | ||
'div', | ||
null, | ||
'Fake title' | ||
) | ||
), | ||
_react2.default.createElement( | ||
_accordionItemBody2.default, | ||
null, | ||
_react2.default.createElement( | ||
'div', | ||
null, | ||
'Fake body' | ||
) | ||
) | ||
)).toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
it('renders correctly with other blocks inside', function () { | ||
@@ -194,0 +220,0 @@ var tree = _reactTestRenderer2.default.create(_react2.default.createElement( |
@@ -56,2 +56,3 @@ 'use strict'; | ||
'aria-hidden': ariaHidden, | ||
'aria-labelledby': id.replace('accordion__body-', 'accordion__title-'), | ||
role: role | ||
@@ -58,0 +59,0 @@ }, |
@@ -69,2 +69,15 @@ 'use strict'; | ||
}); | ||
it('renders correctly with an id (aria-labelledBy filled)', function () { | ||
var tree = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordionItemBody2.default, | ||
{ id: 'accordion__body-HASHID', expanded: true }, | ||
_react2.default.createElement( | ||
'div', | ||
null, | ||
'Fake body' | ||
) | ||
)).toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
}); |
@@ -17,4 +17,10 @@ 'use strict'; | ||
var _classnames = require('classnames'); | ||
var _classnames2 = _interopRequireDefault(_classnames); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -32,2 +38,3 @@ | ||
className: 'accordion__title', | ||
hideBodyClassName: null, | ||
role: '' | ||
@@ -43,2 +50,3 @@ }; | ||
className: _propTypes2.default.string, | ||
hideBodyClassName: _propTypes2.default.string, | ||
role: _propTypes2.default.string | ||
@@ -78,11 +86,30 @@ }; | ||
className = _props.className, | ||
role = _props.role; | ||
role = _props.role, | ||
hideBodyClassName = _props.hideBodyClassName; | ||
var titleClassName = (0, _classnames2.default)(className, _defineProperty({}, hideBodyClassName, hideBodyClassName && !expanded)); | ||
if (role === 'tab') { | ||
return _react2.default.createElement( | ||
'div', | ||
{ | ||
id: id, | ||
'aria-selected': expanded, | ||
'aria-controls': ariaControls, | ||
className: titleClassName, | ||
onClick: onClick, | ||
role: role, | ||
tabIndex: '0', | ||
onKeyPress: this.handleKeyPress | ||
}, | ||
children | ||
); | ||
} | ||
return _react2.default.createElement( | ||
'div', | ||
{ // eslint-disable-line jsx-a11y/no-static-element-interactions | ||
{ | ||
id: id, | ||
'aria-expanded': expanded, | ||
'aria-controls': ariaControls, | ||
className: className, | ||
className: titleClassName, | ||
onClick: onClick, | ||
@@ -89,0 +116,0 @@ role: role, |
@@ -44,2 +44,28 @@ 'use strict'; | ||
it('renders with different hideBodyClassName', function () { | ||
var tree = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordionItemTitle2.default, | ||
{ expanded: false, hideBodyClassName: 'testCSSClass--hidden' }, | ||
_react2.default.createElement( | ||
'div', | ||
null, | ||
'Fake title' | ||
) | ||
)).toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
it('doesn\'t respect hideBodyClassName when collapsed', function () { | ||
var tree = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordionItemTitle2.default, | ||
{ expanded: true, hideBodyClassName: 'testCSSClass--hidden' }, | ||
_react2.default.createElement( | ||
'div', | ||
null, | ||
'Fake title' | ||
) | ||
)).toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
it('renders correctly when pressing enter', function () { | ||
@@ -95,2 +121,15 @@ var mockOnClick = jest.fn(); | ||
}); | ||
it('renders correctly with role of tab', function () { | ||
var tree = _reactTestRenderer2.default.create(_react2.default.createElement( | ||
_accordionItemTitle2.default, | ||
{ role: 'tab', className: 'testCSSClass' }, | ||
_react2.default.createElement( | ||
'div', | ||
null, | ||
'Fake Title' | ||
) | ||
)).toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
}); |
{ | ||
"name": "react-accessible-accordion", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"description": "Accessible Accordion component for React", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
[react-accessible-accordion](https://springload.github.io/react-accessible-accordion/) [![npm](https://img.shields.io/npm/v/react-accessible-accordion.svg?style=flat-square)](https://www.npmjs.com/package/react-accessible-accordion) [![Build Status](https://travis-ci.org/springload/react-accessible-accordion.svg?branch=master)](https://travis-ci.org/springload/react-accessible-accordion) [![Coverage Status](https://coveralls.io/repos/github/springload/react-accessible-accordion/badge.svg)](https://coveralls.io/github/springload/react-accessible-accordion) [![Dependency Status](https://david-dm.org/springload/react-accessible-accordion.svg?style=flat-square)](https://david-dm.org/springload/react-accessible-accordion) [![devDependency Status](https://david-dm.org/springload/react-accessible-accordion/dev-status.svg?style=flat-square)](https://david-dm.org/springload/react-accessible-accordion#info=devDependencies) | ||
[![Accessibility status](https://img.shields.io/badge/a11y-0%20error-brightgreen.svg)](http://wave.webaim.org/report#/https://springload.github.io/react-accessible-accordion/) | ||
========= | ||
@@ -96,2 +97,8 @@ | ||
</tr> | ||
<tr> | ||
<td>activeItems</td> | ||
<td>Array</td> | ||
<td>[]</td> | ||
<td>Indexes (or custom keys) to pre expand items. Can be changed dynamically. Doesn't have the priority against `AccordionItem - expanded` on first render.</td> | ||
</tr> | ||
</tbody> | ||
@@ -132,2 +139,8 @@ </table> | ||
</tr> | ||
<tr> | ||
<td>customKey</td> | ||
<td>String</td> | ||
<td></td> | ||
<td>Custom key to be used as a reference in `Accordion - activeItems`</td> | ||
</tr> | ||
</tbody> | ||
@@ -194,2 +207,30 @@ </table> | ||
## Accessibility | ||
### What this project is doing accessibility-wise? | ||
This project manages two types of Accordions, with single or multiple items open. | ||
#### Single item | ||
> Use this with with props `accordion` set to `true` on `Accordion`. | ||
For this type of Accordion, you will get the following `role` set up on your elements: | ||
- Accordion: `tablist` | ||
- AccordionItem: no specific role | ||
- AccordionItemTitle: `tab` | ||
- AccordionItemBody: `tabpanel` | ||
#### Multiple items | ||
For this type of Accordion, you will get the following `role` set up on your elements: | ||
> Use this with with props `accordion` set to `false` on `Accordion`. | ||
- Accordion: no specific role | ||
- AccordionItem: no specific role | ||
- AccordionItemTitle: `button` | ||
- AccordionItemBody: no specific role | ||
## Development | ||
@@ -246,1 +287,16 @@ | ||
``` | ||
# Browser support | ||
**Supported browser / device versions:** | ||
| Browser | Device/OS | Version | Notes | | ||
|---------|-----------|---------|-------| | ||
| Mobile Safari | iOS | latest || | ||
| Chrome | Android | latest || | ||
| IE | Windows | 11 || | ||
| MS Edge | Windows | latest || | ||
| Chrome | Desktop | latest || | ||
| Firefox | Desktop | latest || | ||
| Safari | OSX | latest || |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
352142
17
10008
299
1