From a54f8db2657500a9e056cc3c359a96651d43bd6c Mon Sep 17 00:00:00 2001 From: Billy Rukh Date: Mon, 1 Aug 2016 19:00:07 -0500 Subject: [PATCH] 0.0.14 --- dist-modules/Accordion/index.js | 32 +++---- dist-modules/Accordion/test.js | 98 +++++++++++++++++++++ dist-modules/AccordionItem/index.js | 112 +++++++++++++++--------- dist-modules/AccordionItemBody/index.js | 3 +- package.json | 2 +- 5 files changed, 187 insertions(+), 60 deletions(-) diff --git a/dist-modules/Accordion/index.js b/dist-modules/Accordion/index.js index c66d251..cd6b422 100644 --- a/dist-modules/Accordion/index.js +++ b/dist-modules/Accordion/index.js @@ -30,6 +30,13 @@ var arrayify = function arrayify(obj) { return [].concat(obj); }; +// removes duplicate from array +var dedupeArr = function dedupeArr(arr) { + return arr.filter(function (item, index, inputArray) { + return inputArray.indexOf(item) === index; + }); +}; + var Accordion = function (_Component) { _inherits(Accordion, _Component); @@ -50,17 +57,6 @@ var Accordion = function (_Component) { } _createClass(Accordion, [{ - key: 'componentDidMount', - value: function componentDidMount() { - var _this2 = this; - - this.state.activeItems.forEach(function (index) { - if (_this2.refs['item-' + index]) { - _this2.refs['item-' + index].allowOverflow(); - } - }); - } - }, { key: 'handleClick', value: function handleClick(index) { var newState = {}; @@ -72,6 +68,10 @@ var Accordion = function (_Component) { if (position !== -1) { newState.activeItems.splice(position, 1); + + if (this.props.openNextAccordionItem && index !== this.props.children.length - 1) { + newState.activeItems.push(index + 1); + } } else if (this.props.allowMultiple) { newState.activeItems.push(index); } else { @@ -82,12 +82,14 @@ var Accordion = function (_Component) { this.props.onChange(newState); } + // removes duplicate items in activeItems array + newState.activeItems = dedupeArr(newState.activeItems); this.setState(newState); } }, { key: 'renderItems', value: function renderItems() { - var _this3 = this; + var _this2 = this; if (!this.props.children) { return null; @@ -95,13 +97,13 @@ var Accordion = function (_Component) { var children = arrayify(this.props.children); return children.map(function (item, index) { - var key = item.props.slug || index; - var expanded = _this3.state.activeItems.indexOf(key) !== -1; + var key = _this2.props.openNextAccordionItem ? index : item.props.slug || index; + var expanded = _this2.state.activeItems.indexOf(key) !== -1; return _react2.default.cloneElement(item, { expanded: expanded, key: key, - onClick: _this3.handleClick.bind(_this3, key), + onClick: _this2.handleClick.bind(_this2, key), ref: 'item-' + key }); }); diff --git a/dist-modules/Accordion/test.js b/dist-modules/Accordion/test.js index 6d9dca6..a0ccefc 100644 --- a/dist-modules/Accordion/test.js +++ b/dist-modules/Accordion/test.js @@ -197,4 +197,102 @@ describe('Accordion Test Case', function () { (0, _unexpected2.default)(instance.state.activeItems, 'to equal', [1, 0]); }); }); + + describe('openNextAccordionItem', function () { + + it('should open next accordion item', function () { + var tree = _skinDeep2.default.shallowRender(_react2.default.createElement( + _index2.default, + { openNextAccordionItem: true }, + _react2.default.createElement(_AccordionItem2.default, { title: 'First' }), + _react2.default.createElement(_AccordionItem2.default, { title: 'Second' }) + )); + + instance = tree.getMountedInstance(); + vdom = tree.getRenderOutput(); + items = vdom.props.children; + + (0, _unexpected2.default)(items[0].props.expanded, 'to be true'); + (0, _unexpected2.default)(items[1].props.expanded, 'to be false'); + + instance.handleClick(0); + + vdom = tree.getRenderOutput(); + items = vdom.props.children; + + (0, _unexpected2.default)(items[0].props.expanded, 'to be false'); + (0, _unexpected2.default)(items[1].props.expanded, 'to be true'); + }); + + it('should close last item and not open another accordion item', function () { + var tree = _skinDeep2.default.shallowRender(_react2.default.createElement( + _index2.default, + { openNextAccordionItem: true, activeItems: [1] }, + _react2.default.createElement(_AccordionItem2.default, { title: 'First' }), + _react2.default.createElement(_AccordionItem2.default, { title: 'Second' }) + )); + + instance = tree.getMountedInstance(); + vdom = tree.getRenderOutput(); + items = vdom.props.children; + + (0, _unexpected2.default)(items[0].props.expanded, 'to be false'); + (0, _unexpected2.default)(items[1].props.expanded, 'to be true'); + + instance.handleClick(1); + + vdom = tree.getRenderOutput(); + items = vdom.props.children; + + (0, _unexpected2.default)(items[0].props.expanded, 'to be false'); + (0, _unexpected2.default)(items[1].props.expanded, 'to be false'); + }); + + it('should open multiple if allowMultiple present', function () { + var tree = _skinDeep2.default.shallowRender(_react2.default.createElement( + _index2.default, + { openNextAccordionItem: true, allowMultiple: true }, + _react2.default.createElement(_AccordionItem2.default, { title: 'First' }), + _react2.default.createElement(_AccordionItem2.default, { title: 'Second' }), + _react2.default.createElement(_AccordionItem2.default, { title: 'Third' }) + )); + + instance = tree.getMountedInstance(); + vdom = tree.getRenderOutput(); + items = vdom.props.children; + + (0, _unexpected2.default)(items[0].props.expanded, 'to be true'); + (0, _unexpected2.default)(items[1].props.expanded, 'to be false'); + (0, _unexpected2.default)(items[2].props.expanded, 'to be false'); + + instance.handleClick(1); + instance.handleClick(2); + + vdom = tree.getRenderOutput(); + items = vdom.props.children; + + (0, _unexpected2.default)(items[0].props.expanded, 'to be true'); + (0, _unexpected2.default)(items[1].props.expanded, 'to be true'); + (0, _unexpected2.default)(items[2].props.expanded, 'to be true'); + }); + + it('should override slug property and assign key to index', function () { + var tree = _skinDeep2.default.shallowRender(_react2.default.createElement( + _index2.default, + { openNextAccordionItem: true }, + _react2.default.createElement(_AccordionItem2.default, { title: 'First', slug: 'first' }), + _react2.default.createElement(_AccordionItem2.default, { title: 'Second', slug: 'second' }) + )); + + instance = tree.getMountedInstance(); + + instance.handleClick(0); + + vdom = tree.getRenderOutput(); + items = vdom.props.children; + + (0, _unexpected2.default)(items[0].props.expanded, 'to be false'); + (0, _unexpected2.default)(items[1].props.expanded, 'to be true'); + }); + }); }); \ No newline at end of file diff --git a/dist-modules/AccordionItem/index.js b/dist-modules/AccordionItem/index.js index cb2438d..bc712ea 100644 --- a/dist-modules/AccordionItem/index.js +++ b/dist-modules/AccordionItem/index.js @@ -52,7 +52,8 @@ var AccordionItem = function (_Component) { _this.state = { maxHeight: props.expanded ? 'none' : 0, - overflow: props.expanded ? 'visible' : 'hidden' + overflow: props.expanded ? 'visible' : 'hidden', + duration: 300 }; return _this; } @@ -62,53 +63,29 @@ var AccordionItem = function (_Component) { value: function componentWillMount() { this.uuid = _uuid2.default.v4(); } - }, { - key: 'componentDidMount', - value: function componentDidMount() { - var _this2 = this; - - // allow overflow for absolute positioned elements inside - // the item body, but only after animation is complete - _reactDom2.default.findDOMNode(this).addEventListener('transitionend', function () { - if (_this2.props.expanded) _this2.allowOverflow(); - }); - } }, { key: 'componentDidUpdate', value: function componentDidUpdate(prevProps) { if (prevProps.expanded !== this.props.expanded) { - this.setMaxHeight(); + if (this.props.expanded) { + this.maybeExpand(); + } else { + this.handleCollapse(); + } } } }, { - key: 'allowOverflow', - value: function allowOverflow() { + key: 'startTransition', + value: function startTransition() { this.setState({ - maxHeight: 'none', - overflow: 'visible' + maxHeight: this.maxHeight, + overflow: 'hidden' }); + clearTimeout(this.timeout); } }, { - key: 'updateState', - value: function updateState(node) { - var _this3 = this; - - if (!this.props.expanded) { - this.setState({ - maxHeight: node.scrollHeight + 'px' - }); - } - - setTimeout(function () { - return _this3.setState({ - maxHeight: _this3.props.expanded ? node.scrollHeight + 'px' : 0, - overflow: 'hidden' - }); - }, 0); - } - }, { - key: 'setMaxHeight', - value: function setMaxHeight() { + key: 'maybeExpand', + value: function maybeExpand() { var bodyNode = _reactDom2.default.findDOMNode(this.refs.body); var images = bodyNode.querySelectorAll('img'); @@ -117,23 +94,64 @@ var AccordionItem = function (_Component) { return; } - this.updateState(bodyNode); + this.handleExpand(); } + }, { + key: 'handleExpand', + value: function handleExpand() { + var _this2 = this; - // Wait for images to load before calculating maxHeight + var onExpand = this.props.onExpand; + + + this.startTransition(); + this.timeout = setTimeout(function () { + _this2.setState({ + maxHeight: 'none', + overflow: 'visible' + }); + if (onExpand) { + onExpand(); + } + }, this.state.duration); + } + }, { + key: 'handleCollapse', + value: function handleCollapse() { + var _this3 = this; + + var onClose = this.props.onClose; + + + this.startTransition(); + this.timeout = setTimeout(function () { + _this3.setState({ + maxHeight: 0, + overflow: 'hidden' + }); + + if (onClose) { + onClose(); + } + }, 0); + } }, { key: 'preloadImages', - value: function preloadImages(node, images) { + + + // Wait for images to load before calculating maxHeight + value: function preloadImages(node) { var _this4 = this; - var imagesLoaded = 0; + var images = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; + var imagesLoaded = 0; var imgLoaded = function imgLoaded() { imagesLoaded++; if (imagesLoaded === images.length) { - _this4.updateState(node); + _this4.handleExpand(); } }; @@ -174,7 +192,9 @@ var AccordionItem = function (_Component) { uuid: this.uuid }), _react2.default.createElement( _AccordionItemBody2.default, - { maxHeight: this.state.maxHeight, + { + maxHeight: this.state.maxHeight, + duration: this.state.duration, className: this.props.bodyClassName, overflow: this.state.overflow, ref: 'body', @@ -183,6 +203,12 @@ var AccordionItem = function (_Component) { ) ); } + }, { + key: 'maxHeight', + get: function get() { + var body = _reactDom2.default.findDOMNode(this.refs.body); + return body.scrollHeight + 'px'; + } }]); return AccordionItem; diff --git a/dist-modules/AccordionItemBody/index.js b/dist-modules/AccordionItemBody/index.js index 29b839c..856dc54 100644 --- a/dist-modules/AccordionItemBody/index.js +++ b/dist-modules/AccordionItemBody/index.js @@ -37,7 +37,7 @@ var AccordionItemBody = function (_Component) { var style = { maxHeight: this.props.maxHeight, overflow: this.props.overflow, - transition: 'max-height .3s ease' + transition: 'max-height ' + this.props.duration + 'ms ease' }; return _react2.default.createElement( @@ -64,6 +64,7 @@ exports.default = AccordionItemBody; AccordionItemBody.propTypes = { className: _react.PropTypes.string, maxHeight: _react.PropTypes.oneOfType([_react2.default.PropTypes.string, _react2.default.PropTypes.number]), + duration: _react.PropTypes.number, overflow: _react.PropTypes.string, uuid: _react.PropTypes.string }; \ No newline at end of file diff --git a/package.json b/package.json index cbf5ced..e6a24a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-sanfona", - "version": "0.0.13", + "version": "0.0.14", "description": "React accessible accordion component", "main": "./dist-modules/index.js", "scripts": {