From f7801874dc5b8777c8624709f99d62feb19b3563 Mon Sep 17 00:00:00 2001 From: bestguy <7zark7@gmail.com> Date: Fri, 20 Aug 2021 20:53:22 -0700 Subject: [PATCH] feat(collapse): add horizontal collapse --- docs/lib/Components/CollapsePage.js | 11 +++++++++ docs/lib/examples/CollapseHorizontal.js | 24 ++++++++++++++++++++ src/Collapse.js | 30 ++++++++++++++----------- src/__tests__/Collapse.spec.js | 13 +++++++---- types/lib/Collapse.d.ts | 1 + 5 files changed, 62 insertions(+), 17 deletions(-) create mode 100644 docs/lib/examples/CollapseHorizontal.js diff --git a/docs/lib/Components/CollapsePage.js b/docs/lib/Components/CollapsePage.js index 378719373..e4881292c 100644 --- a/docs/lib/Components/CollapsePage.js +++ b/docs/lib/Components/CollapsePage.js @@ -5,11 +5,13 @@ import PageTitle from '../UI/PageTitle'; import SectionTitle from '../UI/SectionTitle'; import CollapseExample from '../examples/Collapse'; +import CollapseHorizontalExample from '../examples/CollapseHorizontal'; import UncontrolledCollapseExample from '../examples/CollapseUncontrolled'; import CollapseEventsExample from '../examples/CollapseEvents'; const CollapseExampleSource = require('!!raw-loader!../examples/Collapse'); +const CollapseHorizontalExampleSource = require('!!raw-loader!../examples/CollapseHorizontal'); const CollapseEventsExampleSource = require('!!raw-loader!../examples/CollapseEvents'); const UncontrolledCollapseExampleSource = require('!!raw-loader!../examples/CollapseUncontrolled'); @@ -31,6 +33,7 @@ export default class CollapsePage extends React.Component { {`Collapse.propTypes = { ...Transition.propTypes, // see note below + horizontal: PropTypes.bool, isOpen: PropTypes.bool, children: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.node), @@ -51,6 +54,14 @@ export default class CollapsePage extends React.Component { http://reactcommunity.org/react-transition-group/transition/.

+ Horizontal +
+ +
+
+          {CollapseHorizontalExampleSource}
+        
+ Events

Use the onEnter, onEntering, onEntered, onExiting and onExited props for diff --git a/docs/lib/examples/CollapseHorizontal.js b/docs/lib/examples/CollapseHorizontal.js new file mode 100644 index 000000000..ed9508cfc --- /dev/null +++ b/docs/lib/examples/CollapseHorizontal.js @@ -0,0 +1,24 @@ +import React, { useState } from 'react'; +import { Alert, Collapse, Button } from 'reactstrap'; + +const Example = () => { + const [isOpen, setIsOpen] = useState(false); + + const toggle = () => setIsOpen(!isOpen); + + return ( +

+ + + + Anim pariatur cliche reprehenderit, + enim eiusmod high life accusamus terry richardson ad squid. Nihil + anim keffiyeh helvetica, craft beer labore wes anderson cred + nesciunt sapiente ea proident. + + +
+ ); +} + +export default Example; diff --git a/src/Collapse.js b/src/Collapse.js index 9bac27e6d..b42efb1d9 100644 --- a/src/Collapse.js +++ b/src/Collapse.js @@ -6,6 +6,7 @@ import { mapToCssModules, omit, pick, TransitionTimeouts, TransitionPropTypeKeys const propTypes = { ...Transition.propTypes, + horizontal: PropTypes.bool, isOpen: PropTypes.bool, children: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.node), @@ -24,6 +25,7 @@ const propTypes = { const defaultProps = { ...Transition.defaultProps, + horizontal: false, isOpen: false, appear: false, enter: true, @@ -43,16 +45,12 @@ function getTransitionClass(status) { return transitionStatusToClassHash[status] || 'collapse'; } -function getHeight(node) { - return node.scrollHeight; -} - class Collapse extends Component { constructor(props) { super(props); this.state = { - height: null + dimension: null }; ['onEntering', 'onEntered', 'onExit', 'onExiting', 'onExited'].forEach((name) => { @@ -60,36 +58,41 @@ class Collapse extends Component { }); } + getDimension(node) { + return this.props.horizontal ? node.scrollWidth : node.scrollHeight; + } + onEntering(node, isAppearing) { - this.setState({ height: getHeight(node) }); + this.setState({ dimension: this.getDimension(node) }); this.props.onEntering(node, isAppearing); } onEntered(node, isAppearing) { - this.setState({ height: null }); + this.setState({ dimension: null }); this.props.onEntered(node, isAppearing); } onExit(node) { - this.setState({ height: getHeight(node) }); + this.setState({ dimension: this.getDimension(node) }); this.props.onExit(node); } onExiting(node) { // getting this variable triggers a reflow - const _unused = node.offsetHeight; // eslint-disable-line no-unused-vars - this.setState({ height: 0 }); + const _unused = this.getDimension(node); // eslint-disable-line no-unused-vars + this.setState({ dimension: 0 }); this.props.onExiting(node); } onExited(node) { - this.setState({ height: null }); + this.setState({ dimension: null }); this.props.onExited(node); } render() { const { tag: Tag, + horizontal, isOpen, className, navbar, @@ -99,7 +102,7 @@ class Collapse extends Component { ...otherProps } = this.props; - const { height } = this.state; + const { dimension } = this.state; const transitionProps = pick(otherProps, TransitionPropTypeKeys); const childProps = omit(otherProps, TransitionPropTypeKeys); @@ -117,10 +120,11 @@ class Collapse extends Component { let collapseClass = getTransitionClass(status); const classes = mapToCssModules(classNames( className, + horizontal && 'collapse-horizontal', collapseClass, navbar && 'navbar-collapse' ), cssModule); - const style = height === null ? null : { height }; + const style = dimension === null ? null : { [horizontal ? 'width' : 'height']: dimension }; return ( { expect(wrapper.find('div').hasClass('collapse')).toEqual(true); }); + it('should render with class "collapse-horizontal" if it has prop horizontal', () => { + wrapper = mount(); + expect(wrapper.find('div').hasClass('collapse-horizontal')).toEqual(true); + }); + it('should render with class "navbar-collapse" if it has prop navbar', () => { wrapper = mount(); expect(wrapper.find('div').hasClass('navbar-collapse')).toEqual(true); @@ -50,12 +55,12 @@ describe('Collapse', () => { it('should set height to null when isOpen is true', () => { wrapper = shallow(); - expect(wrapper.state('height')).toBe(null); + expect(wrapper.state('dimension')).toBe(null); }); it('should not set height when isOpen is false', () => { wrapper = shallow(); - expect(wrapper.state('height')).toBe(null); + expect(wrapper.state('dimension')).toBe(null); }); it('should forward all styles', () => { @@ -111,7 +116,7 @@ describe('Collapse', () => { isOpen = true; wrapper = mount(); toggle(); - expect(wrapper.state('height')).toBe(0); + expect(wrapper.state('dimension')).toBe(0); wrapper.unmount(); }); @@ -119,7 +124,7 @@ describe('Collapse', () => { wrapper = mount(); toggle(); jest.runTimersToTime(380); - expect(wrapper.state('height')).toBe(null); + expect(wrapper.state('dimension')).toBe(null); wrapper.unmount(); }); }); diff --git a/types/lib/Collapse.d.ts b/types/lib/Collapse.d.ts index 2a4971543..a606db149 100644 --- a/types/lib/Collapse.d.ts +++ b/types/lib/Collapse.d.ts @@ -6,6 +6,7 @@ export interface CollapseProps extends React.HTMLAttributes { isOpen?: boolean; cssModule?: CSSModule; tag?: React.ElementType; + horizontal?: boolean; navbar?: boolean; delay?: { show: number;