react-accessible-accordion
Advanced tools
Comparing version 1.0.2 to 2.0.0
@@ -7,2 +7,46 @@ Changelog | ||
## [[v2.0.0]](https://github.com/springload/react-accessible-accordion/releases/tag/v2.0.0) | ||
Version 2.0 represents a total refactor, with a new context-based approach which should make this library more flexible, more maintainable and more comprehensively testable. | ||
As this is a major release, users should expect some breaking changes - though they should be limited to the removal of the `activeItems` prop (read more below). | ||
### Added | ||
- Exports `resetNextId` (https://github.com/springload/react-accessible-accordion/issues/41). | ||
### Fixed | ||
- Defect where controlled components' props were overridden by React.Children.map (https://github.com/springload/react-accessible-accordion/issues/33). | ||
- Defect where accordion crashed with unexpected `children` types (https://github.com/springload/react-accessible-accordion/issues/45). | ||
- Defect where React Accessible Accordion's components could not be extended. | ||
- Defect where the `children` of `Accordion` or `AccordionItem` could not be arbitrary. | ||
- Defect where `AccordionItem` had to be a child of `Accordion` (as opposed to to an arbitrary-level descendant). | ||
- Defect where `AccordionItemBody` and `AccordionItemTitle` had to be children of `AccordionItem` (as opposed to arbitrary-level descendants). | ||
### Removed: | ||
- 🚨 Breaking change 🚨 `activeItems` property is no longer supported. | ||
Control at the `Accordion` level (via the `activeItems` prop) and `AccordionItem` level (via the `expanded` prop) fought against one another, and choosing which control mechanism to give preference to would have been an arbitrary decision - and whichever way we went, we would have had test cases which demonstrated unusual/unpredictable behaviour. The `activeItems` mechanism was the obvious one to remove - it was arguably the "less React-y way", and we considered it more of a convenience than a feature. Crucially though, it fought too hard against the new architecture of the library, and keeping it would have prevented us enabling lots of other new features or resolving some of the issues that our users had raised. | ||
If you're currently using activeItems, you're upgrade path might look like this: | ||
```diff | ||
const items = ['Foo', 'Bar']; | ||
const activeItems = [0]; | ||
return ( | ||
- <Accordion activeItems={activeItems} /> | ||
+ <Accordion /> | ||
{activeItems.forEach((item, i) => ( | ||
- <AccordionItem key={item}>{item}</AccordionItem> | ||
+ <AccordionItem key={item} expanded={activeItems.includes(i)}>{item}</AccordionItem> | ||
)} | ||
</Accordion> | ||
); | ||
``` | ||
Please don't hesitate to reach out to one of the maintainers (or raise an issue) if you're having trouble upgrading - we're happy to help! | ||
## [[v1.0.1]](https://github.com/springload/react-accessible-accordion/releases/tag/v1.0.1) | ||
@@ -9,0 +53,0 @@ |
@@ -13,8 +13,10 @@ 'use strict'; | ||
var _utils = require('../utils'); | ||
var _unistore = require('unistore'); | ||
var _unistore2 = _interopRequireDefault(_unistore); | ||
var _react3 = require('unistore/react'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -26,2 +28,4 @@ | ||
// import { isArraysEqualShallow } from '../utils'; | ||
var Accordion = function (_Component) { | ||
@@ -41,102 +45,119 @@ _inherits(Accordion, _Component); | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Accordion.__proto__ || Object.getPrototypeOf(Accordion)).call.apply(_ref, [this].concat(args))), _this), _this.state = { | ||
activeItems: _this.preExpandedItems(), | ||
accordion: true | ||
}, _this.renderItems = _this.renderItems.bind(_this), _temp), _possibleConstructorReturn(_this, _ret); | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Accordion.__proto__ || Object.getPrototypeOf(Accordion)).call.apply(_ref, [this].concat(args))), _this), _this.store = (0, _unistore2.default)({ | ||
accordion: 0 | ||
// onChange: this.props.onChange, | ||
// activeItems: this.props.activeItems, | ||
}), _temp), _possibleConstructorReturn(_this, _ret); | ||
} | ||
_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 | ||
}); | ||
key: 'render', | ||
nextProps.onChange(nextProps.accordion ? newActiveItems[0] : newActiveItems); | ||
} | ||
} | ||
}, { | ||
key: 'preExpandedItems', | ||
value: function preExpandedItems() { | ||
var _this2 = this; | ||
var activeItems = []; | ||
_react2.default.Children.map(this.props.children, function (item, index) { | ||
if (item.props.expanded) { | ||
if (_this2.props.accordion) { | ||
if (activeItems.length === 0) activeItems.push(item.props.customKey || index); | ||
} else { | ||
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; | ||
} | ||
}, { | ||
key: 'handleClick', | ||
value: function handleClick(key) { | ||
var activeItems = this.state.activeItems; | ||
if (this.props.accordion) { | ||
activeItems = activeItems[0] === key ? [] : [key]; | ||
} else { | ||
activeItems = [].concat(_toConsumableArray(activeItems)); | ||
var index = activeItems.indexOf(key); | ||
var isActive = index > -1; | ||
if (isActive) { | ||
// remove active state | ||
activeItems.splice(index, 1); | ||
} else { | ||
activeItems.push(key); | ||
} | ||
} | ||
this.setState({ | ||
activeItems: activeItems | ||
}); | ||
// state = { | ||
// activeItems: this.preExpandedItems(), | ||
// accordion: true, | ||
// }; | ||
this.props.onChange(this.props.accordion ? activeItems[0] : activeItems); | ||
} | ||
}, { | ||
key: 'renderItems', | ||
value: function renderItems() { | ||
var _this3 = this; | ||
// componentWillReceiveProps(nextProps: AccordionProps) { | ||
// if ( | ||
// !isArraysEqualShallow(nextProps.activeItems, this.state.activeItems) | ||
// ) { | ||
// let newActiveItems; | ||
// if (nextProps.accordion) { | ||
// newActiveItems = nextProps.activeItems.length | ||
// ? [nextProps.activeItems[0]] | ||
// : []; | ||
// } else { | ||
// newActiveItems = nextProps.activeItems.slice(); | ||
// } | ||
// this.setState({ | ||
// activeItems: newActiveItems, | ||
// }); | ||
var _props = this.props, | ||
accordion = _props.accordion, | ||
children = _props.children; | ||
// nextProps.onChange( | ||
// nextProps.accordion ? newActiveItems[0] : newActiveItems, | ||
// ); | ||
// } | ||
// } | ||
// preExpandedItems() { | ||
// let activeItems = []; | ||
// React.Children.map(this.props.children, (item, index) => { | ||
// if (item.props.expanded) { | ||
// if (this.props.accordion) { | ||
// if (activeItems.length === 0) | ||
// activeItems.push(item.props.customKey || index); | ||
// } else { | ||
// 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; | ||
// } | ||
return _react2.default.Children.map(children, function (item, index) { | ||
var key = item.props.customKey || index; | ||
var expanded = _this3.state.activeItems.indexOf(key) !== -1 && !item.props.disabled; | ||
// handleClick(key: number | string) { | ||
// let activeItems = this.state.activeItems; | ||
// if (this.props.accordion) { | ||
// activeItems = activeItems[0] === key ? [] : [key]; | ||
// } else { | ||
// activeItems = [...activeItems]; | ||
// const index = activeItems.indexOf(key); | ||
// const isActive = index > -1; | ||
// if (isActive) { | ||
// // remove active state | ||
// activeItems.splice(index, 1); | ||
// } else { | ||
// activeItems.push(key); | ||
// } | ||
// } | ||
// this.setState({ | ||
// activeItems, | ||
// }); | ||
return _react2.default.cloneElement(item, { | ||
disabled: item.props.disabled, | ||
accordion: accordion, | ||
expanded: expanded, | ||
key: 'accordion__item-' + key, | ||
onClick: _this3.handleClick.bind(_this3, key) | ||
}); | ||
}); | ||
} | ||
}, { | ||
key: 'render', | ||
// this.props.onChange( | ||
// this.props.accordion ? activeItems[0] : activeItems, | ||
// ); | ||
// } | ||
// renderItems() { | ||
// const { accordion, children } = this.props; | ||
// return React.Children.map(children, (item, index) => { | ||
// const key = item.props.customKey || index; | ||
// const expanded = | ||
// this.state.activeItems.indexOf(key) !== -1 && | ||
// !item.props.disabled; | ||
// return React.cloneElement(item, { | ||
// disabled: item.props.disabled, | ||
// accordion, | ||
// expanded, | ||
// key: `accordion__item-${key}`, | ||
// onClick: this.handleClick.bind(this, key), | ||
// }); | ||
// }); | ||
// } | ||
// renderItems = this.renderItems.bind(this); | ||
value: function render() { | ||
var _props2 = this.props, | ||
className = _props2.className, | ||
accordion = _props2.accordion; | ||
var _props = this.props, | ||
className = _props.className, | ||
children = _props.children; | ||
return _react2.default.createElement( | ||
'div', | ||
{ role: accordion ? 'tablist' : null, className: className }, | ||
this.renderItems() | ||
_react3.Provider, | ||
{ store: this.store }, | ||
_react2.default.createElement( | ||
'div', | ||
{ className: className }, | ||
children | ||
) | ||
); | ||
@@ -143,0 +164,0 @@ } |
@@ -11,2 +11,4 @@ 'use strict'; | ||
var _enzyme = require('enzyme'); | ||
var _accordion = require('./accordion'); | ||
@@ -20,2 +22,10 @@ | ||
var _accordionItemTitle = require('../AccordionItemTitle/accordion-item-title'); | ||
var _accordionItemTitle2 = _interopRequireDefault(_accordionItemTitle); | ||
var _accordionItemBody = require('../AccordionItemBody/accordion-item-body'); | ||
var _accordionItemBody2 = _interopRequireDefault(_accordionItemBody); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -27,2 +37,9 @@ | ||
jest.mock('../AccordionItemTitle/accordion-item-title', function () { | ||
return 'div'; | ||
}); | ||
jest.mock('../AccordionItemBody/accordion-item-body', function () { | ||
return 'div'; | ||
}); | ||
describe('Accordion', function () { | ||
@@ -469,2 +486,89 @@ it('renders correctly with min params', function () { | ||
}); | ||
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(); | ||
}); | ||
// it('supports controlled component inside accordion', () => { | ||
// class App extends Component<*, { value: string }> { | ||
// constructor(props) { | ||
// super(props); | ||
// this.state = { | ||
// value: '', | ||
// }; | ||
// } | ||
// handleChange = evt => { | ||
// this.setState({ | ||
// value: evt.target.value, | ||
// }); | ||
// }; | ||
// render() { | ||
// return ( | ||
// <Accordion activeItems={[1]} id="accordion"> | ||
// <AccordionItem id="accordion-item--1"> | ||
// <AccordionItemTitle> | ||
// {`Title One`} | ||
// </AccordionItemTitle> | ||
// <AccordionItemBody> | ||
// <input | ||
// id="controlled-input--1" | ||
// onChange={this.handleChange} | ||
// value={this.state.value} | ||
// /> | ||
// </AccordionItemBody> | ||
// </AccordionItem> | ||
// <AccordionItem> | ||
// <AccordionItemTitle> | ||
// {`Title Two`} | ||
// </AccordionItemTitle> | ||
// <AccordionItemBody> | ||
// {`Body Two`} | ||
// </AccordionItemBody> | ||
// </AccordionItem> | ||
// </Accordion> | ||
// ); | ||
// } | ||
// } | ||
// const wrapper = mount(<App />); | ||
// const accordion = wrapper.find(Accordion); | ||
// const input = wrapper.find('#controlled-input--1'); | ||
// const itemOne = wrapper.find('#accordion-item--1'); | ||
// expect(accordion.instance().state.activeItems).toEqual([1]); | ||
// itemOne.simulate('click'); | ||
// expect(accordion.instance().state.activeItems).toEqual([0]); | ||
// input.simulate('change', { value: 'foo' }); | ||
// expect(accordion.instance().state.activeItems).toEqual([0]); | ||
// }); | ||
}); |
@@ -21,2 +21,8 @@ 'use strict'; | ||
var _react3 = require('unistore/react'); | ||
var _actions = require('../actions'); | ||
var _actions2 = _interopRequireDefault(_actions); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -91,2 +97,3 @@ | ||
value: function render() { | ||
// console.log(props, 'props'); | ||
var _props2 = this.props, | ||
@@ -103,4 +110,5 @@ className = _props2.className, | ||
{ className: itemClassName }, | ||
this.renderChildren() | ||
'Child' | ||
); | ||
//return <div className={itemClassName}>{this.renderChildren()}</div>; | ||
} | ||
@@ -112,2 +120,5 @@ }]); | ||
// export default AccordionItem; | ||
AccordionItem.defaultProps = { | ||
@@ -120,2 +131,10 @@ accordion: true, | ||
}; | ||
exports.default = AccordionItem; | ||
exports.default = (0, _react3.connect)('accordion', _actions2.default)(function (_ref2) { | ||
var accordion = _ref2.accordion, | ||
setAccordion = _ref2.setAccordion; | ||
return _react2.default.createElement( | ||
'div', | ||
null, | ||
'Test' | ||
); | ||
}); |
{ | ||
"name": "react-accessible-accordion", | ||
"version": "1.0.2", | ||
"version": "2.0.0", | ||
"description": "Accessible Accordion component for React", | ||
"main": "dist/index.js", | ||
"main": "dist/umd/index.js", | ||
"jsnext:main": "dist/es/index.js", | ||
"scripts": { | ||
@@ -16,3 +17,3 @@ "test": "jest", | ||
"css": "cp src/react-accessible-accordion.css dist", | ||
"build": "npm run js -s && npm run css -s", | ||
"build": "NODE_ENV=production rollup -c", | ||
"start": "npm run js:watch", | ||
@@ -31,8 +32,25 @@ "start-demo": "webpack-dev-server --config ./webpack/webpack.config.demo.js", | ||
"presets": [ | ||
"es2015", | ||
[ | ||
"es2015", | ||
{ | ||
"modules": false | ||
} | ||
], | ||
"react", | ||
"stage-2" | ||
] | ||
], | ||
"env": { | ||
"test": { | ||
"presets": [ | ||
"es2015", | ||
"react", | ||
"stage-2" | ||
] | ||
} | ||
} | ||
}, | ||
"jest": { | ||
"setupFiles": [ | ||
"./src/setupTests.js" | ||
], | ||
"testPathIgnorePatterns": [ | ||
@@ -80,2 +98,4 @@ "/node_modules/", | ||
"css-loader": "^0.28.7", | ||
"enzyme": "^3.2.0", | ||
"enzyme-adapter-react-16": "^1.1.0", | ||
"eslint": "^4.8.0", | ||
@@ -92,5 +112,12 @@ "eslint-config-airbnb": "^15.1.0", | ||
"prettier": "^1.7.3", | ||
"raf": "^3.4.0", | ||
"react": "^16.0.0", | ||
"react-dom": "^16.0.0", | ||
"react-test-renderer": "^16.0.0", | ||
"rollup": "^0.56.2", | ||
"rollup-plugin-babel": "^3.0.3", | ||
"rollup-plugin-commonjs": "^8.3.0", | ||
"rollup-plugin-eslint": "^4.0.0", | ||
"rollup-plugin-node-resolve": "^3.0.3", | ||
"rollup-plugin-replace": "^2.0.0", | ||
"style-loader": "^0.18.2", | ||
@@ -102,3 +129,5 @@ "webpack": "^3.6.0", | ||
"classnames": "^2.2.5", | ||
"consecutive": "^5.0.4" | ||
"consecutive": "^5.0.4", | ||
"mobx": "^3.4.0", | ||
"mobx-react": "^4.3.5" | ||
}, | ||
@@ -105,0 +134,0 @@ "peerDependencies": { |
@@ -7,3 +7,3 @@ [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) | ||
This is a work in progress. Feel free to contribute. [Try a demo now](https://springload.github.io/react-accessible-accordion/). | ||
Try a demo now](https://springload.github.io/react-accessible-accordion/). | ||
@@ -17,5 +17,3 @@ If you like accessible components, feel free to check this other repo [react-accessible-modal](https://github.com/springload/react-accessible-modal). | ||
```sh | ||
npm install --save react-accessible-accordion | ||
# react-accessible-accordion's peerDependencies: | ||
npm install --save react@^15.0.0 react-dom@^15.0.0 | ||
npm install --save react-accessible-accordion react react-dom | ||
``` | ||
@@ -25,3 +23,3 @@ | ||
```js | ||
```jsx | ||
import React from 'react'; | ||
@@ -46,5 +44,3 @@ import ReactDOM from 'react-dom'; | ||
<AccordionItemBody> | ||
<p> | ||
Body content | ||
</p> | ||
<p>Body content</p> | ||
</AccordionItemBody> | ||
@@ -58,5 +54,3 @@ </AccordionItem> | ||
<AccordionItemBody> | ||
<p> | ||
Body content | ||
</p> | ||
<p>Body content</p> | ||
</AccordionItemBody> | ||
@@ -104,8 +98,2 @@ </AccordionItem> | ||
</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> | ||
@@ -146,8 +134,2 @@ </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> | ||
@@ -214,2 +196,15 @@ </table> | ||
### resetNextUuid | ||
<table class="table table-bordered table-striped"> | ||
<tbody> | ||
<tr> | ||
<td>Function(void)</td> | ||
</tr> | ||
<tr> | ||
<td>Resets the internal counter for Accordion items' identifiers (including `id` attributes). For use in test suites and isomorphic frameworks.</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
## Accessibility | ||
@@ -227,6 +222,6 @@ | ||
- Accordion: `tablist` | ||
- AccordionItem: no specific role | ||
- AccordionItemTitle: `tab` | ||
- AccordionItemBody: `tabpanel` | ||
* Accordion: `tablist` | ||
* AccordionItem: no specific role | ||
* AccordionItemTitle: `tab` | ||
* AccordionItemBody: `tabpanel` | ||
@@ -239,6 +234,6 @@ #### Multiple items | ||
- Accordion: no specific role | ||
- AccordionItem: no specific role | ||
- AccordionItemTitle: `button` | ||
- AccordionItemBody: no specific role | ||
* Accordion: no specific role | ||
* AccordionItem: no specific role | ||
* AccordionItemTitle: `button` | ||
* AccordionItemBody: no specific role | ||
@@ -297,3 +292,2 @@ ## Development | ||
# Browser support | ||
@@ -303,10 +297,10 @@ | ||
| 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 || | ||
| 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 | | |
Sorry, the diff of this file is not supported yet
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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
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
484552
26
10863
6
34
1
295
3
1
+ Addedmobx@^3.4.0
+ Addedmobx-react@^4.3.5
+ Addedhoist-non-react-statics@2.5.5(transitive)
+ Addedmobx@3.6.2(transitive)
+ Addedmobx-react@4.4.3(transitive)