├── .eslintrc ├── .gitignore ├── .npmignore ├── .npmrc ├── .storybook ├── main.js └── webpack.config.js ├── .travis.yml ├── CHANGELOG.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── package-lock.json ├── package.json └── src ├── components ├── Collapse.jsx ├── Collapse.test.jsx ├── __snapshots__ │ └── Collapse.test.jsx.snap ├── index.js └── useCollapse.js ├── data └── index.js └── stories ├── App.jsx ├── CollapseWithOverflow.jsx ├── collapse.stories.js ├── index.js ├── perf.stories.js └── style.css /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": ["airbnb", "prettier", "prettier/react"], 4 | "plugins": ["prettier", "jest", "react-hooks"], 5 | "env": { 6 | "jest": true 7 | }, 8 | "globals": { 9 | "window": true, 10 | "document": true 11 | }, 12 | "rules": { 13 | "linebreak-style": 0, 14 | "import/no-extraneous-dependencies": 0, 15 | "react/require-extension": 0, 16 | "react/jsx-filename-extension": [1, { "extensions": [".spec.js", ".stories.js", ".jsx"] }], 17 | "react/no-unused-prop-types": [2, { "skipShapeProps": true}], 18 | "import/no-unresolved": [1, { "caseSensitive": false }] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build 2 | lib/ 3 | 4 | # Other stuff 5 | node_modules 6 | *.log 7 | .DS_Store 8 | *.tgz 9 | 10 | npm-debug* 11 | 12 | /.idea 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Dot files 2 | .gitignore 3 | .eslintrc 4 | .babelrc 5 | 6 | package.json 7 | wallaby.js 8 | 9 | # Folders 10 | src/ 11 | test/ 12 | .storybook/ 13 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ['../src/**/*.stories.@(js|jsx)'], 3 | addons: ['@storybook/addon-actions', '@storybook/addon-knobs'], 4 | }; 5 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | module: { 5 | rules: [ 6 | { 7 | test: /\.css$/, 8 | use: [ 9 | 'style-loader', 10 | { 11 | loader: 'css-loader', 12 | options: { 13 | sourceMap: true, 14 | }, 15 | }, 16 | ], 17 | }, 18 | ], 19 | }, 20 | resolve: { 21 | extensions: ['.js', '.jsx'], 22 | modules: ['node_modules', path.resolve(__dirname, '../src')], 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - stable 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | # 4.1.0 4 | > August 24, 2020 5 | * :nut_and_bolt: **New** Add default transition 6 | * :tada: **Enhancement** Update devDependencies 7 | 8 | # 4.0.6 9 | > February 26, 2020 10 | * :bug: **Bugfix** Set overflow: visible; on initial render when isOpen is true. 11 | 12 | # 4.0.5 13 | > February 26, 2020 14 | * :bug: **Bugfix** Add npmrc and update package-lock to use npmjs registry 15 | 16 | # 4.0.4 17 | > January 14, 2020 18 | * :bug: **Bugfix** Remove animation / calculation on first render 19 | 20 | # 4.0.3 21 | > January 8, 2020 22 | * :tada: **Enhancement** Rewrite tests to use jest snapshots and @testing-library/react 23 | 24 | # 4.0.2 25 | > January 2, 2020 26 | * :tada: **Enhancement** Update build and use browserlist 27 | 28 | # 4.0.1 29 | > Desember 31, 2019 30 | * :tada: **Enhancement** Add willChange: height, add stories and prettify component 31 | 32 | # 4.0.0 33 | > Desember 31, 2019 34 | * :boom: **Breaking** Upgraded React peerDependencies to >= 16.8 35 | * :nut_and_bolt: **New** Added new hook useCollapse 36 | * :nut_and_bolt: **New** Updated Collapse to use new hook. 37 | * :tada: **Enhancement** Add dynamic content to storybook example 38 | * :tada: **Enhancement** Update prettier and eslint setup 39 | 40 | # 3.6.1 41 | > October 11, 2019 42 | * :tada: **Enhancement** Migrate away from unsafe `componentWillReceiveProps` 43 | 44 | # 3.6.1-beta.0 45 | > October 8, 2019 46 | * :tada: **Enhancement** Migrate away from unsafe `componentWillReceiveProps` 47 | 48 | # 3.6.0 49 | > October 1, 2018 50 | * :nut_and_bolt: **New** Add support for ARIA and data attributes 51 | 52 | # 3.5.0 53 | > September 3, 2018 54 | * :nut_and_bolt: **New** Add transition prop 55 | * :tada: **Enhancement** Use Jest for unit tests 56 | * :tada: **Enhancement** Add knobs to the stories 57 | 58 | # 3.4.0 59 | > August 31, 2018 60 | * :tada: **Enhancement** Handle the style changes manually when React can just do this as part of its rendering. Moved the style properties to the state and just uses setState to update them. Changed the test so the actual component styles are checked when they are updated. 61 | * :tada: **Enhancement** PureComponent is more efficient than Component so changed it to that 62 | * :tada: **Enhancement** Added a default className — would be nice not to have to pass one if you just want to use the default 63 | * :bug: **Bugfix** All Component constructors should pass the props through to the super 64 | 65 | # 3.3.2 66 | > July 05, 2018 67 | * :bug: **Bugfix** Added nullcheck to make sure we wont try to set style on nonexisting content 68 | 69 | # 3.3.1 70 | > July 05, 2018 71 | * :bug: **Bugfix** Check if element target equals content on transition end 72 | 73 | # 3.3.0 74 | > July 04, 2018 75 | * :tada: **Enhancement** Setting `visibility: hidden` when collapse is closed to prevent screenreaders from reading the content 76 | 77 | # 3.1.0 78 | > Nov 15, 2017 79 | * :nut_and_bolt: **New** Add prettier-eslint 💅 80 | * :nut_and_bolt: **New** Support react 15.x || 16.x 🕺🏼 . Solving [#14](https://github.com/SparebankenVest/react-css-collapse/issues/14) 81 | * :tada: **Enhancement** Upgrade storybook 🙏 82 | 83 | # 3.0.2 84 | > May 04, 2017 85 | 86 | * :bug: **Bugfix** Accessing PropTypes from 'prop-types' package instead of main React package. React.PropTypes will be deprecated in React 15.5 87 | 88 | # 3.0.1 89 | > Apr 30, 2017 90 | 91 | * :bug: **Bugfix** When `isOpen={true}`, set the `overflow: visible` after the component mounts to prevent cutting off content that may overflow outside the flow of `height: auto` (i.e. child content with `position: relative` and grandchildren with `position: absolute` may get cut off). 92 | 93 | # 3.0.0 94 | > Apr 7, 2017 95 | 96 | * :tada: **Feature** added `onRest` callback. The callback is triggered when your transition on `height` (specified in `className`) is done. 97 | * :boom: **Breaking** Remove `onTransitionEnd` callback. Please use `onRest` instead. 98 | 99 | # 2.1.0 100 | > Apr 7, 2017 101 | 102 | * :tada: **Feature** added `onTransitionEnd` callback that gets called after the expand/collapse animation has finished 103 | 104 | # 2.0.2 105 | > Apr 5, 2017 106 | 107 | * :bug: **Bugfix** Using setTimeout(fn, 0) to ensure correct pixelheight is set before transition (back) starts. This ensures we are not transitioning back from `auto`. Ref: http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful 108 | * :bug: **Bugfix** Checking if content is set before accessing content in requestAnimationFrame callback. This fixes a bug where `this.content` was null initially when navigating back to a page using react-css-collapse you had been to before. 109 | 110 | # 2.0.1 111 | > Apr 3, 2017 112 | 113 | * :tada: **Feature** Setup tests 114 | * :tada: **Feature** Do not require children property 115 | 116 | # 2.0.0 117 | > Mar 29, 2017 118 | 119 | * :tada: **Feature** Set initial height without transition on mount if collapse is open 120 | * :boom: **Breaking** Remove style property from collapse. We had to remove this property to prevent conflicts with crucial style properties used in the component and to set initial height without transition on mount when collapse is open. 121 | 122 | # 1.0.0 123 | > Mar 28, 2017 124 | 125 | * :nut_and_bolt: **New** Created `Collapse` component 126 | 127 | ## Examples 128 | * :nut_and_bolt: **New** 129 | * :tada: **Enhancement** 130 | * :bug: **Bugfix** 131 | * :boom: **Breaking** 132 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @SparebankenVest/react-css-collapse-maintainers 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Thanks for contributing, you rock! 2 | 3 | If you use our code, it is now *our* code. 4 | 5 | Please read https://reactjs.org/ and the Code of Conduct before opening an issue. 6 | 7 | - [Think You Found a Bug?](#bug) 8 | - [Proposing New or Changed API?](#api) 9 | - [Issue Not Getting Attention?](#attention) 10 | - [Making a Pull Request?](#pr) 11 | - [Development](#development) 12 | - [Hacking](#hacking) 13 | 14 | 15 | ## Think You Found a Bug? 16 | 17 | Please provide a test case of some sort. Best is a pull request with a failing test. Next is a link to CodePen/JS Bin or repository that illustrates the bug. Finally, some copy/pastable code is acceptable. 18 | 19 | 20 | ## Proposing New or Changed API? 21 | 22 | Please provide thoughtful comments and some sample code. Proposals without substance will be closed. 23 | 24 | 25 | ## Issue Not Getting Attention? 26 | 27 | If you need a bug fixed and nobody is fixing it, it is your responsibility to fix it. Issues with no activity for 30 days may be closed. 28 | 29 | 30 | ## Making a Pull Request? 31 | 32 | Pull requests need only the :+1: of two or more collaborators to be merged; when the PR author is a collaborator, that counts as one. 33 | 34 | ### Tests 35 | 36 | All commits that fix bugs or add features need a test. 37 | 38 | ``Do not merge code without tests.`` 39 | 40 | ### Changelog 41 | 42 | All commits that change or add to the API must be done in a pull request that also: 43 | 44 | - Adds an entry to `CHANGELOG.md` with clear steps for updating code for changed or removed API 45 | - Updates examples 46 | - Updates the docs 47 | 48 | ## Development 49 | 50 | - `npm start` build the components and watch for changes 51 | - `npm run storybook` starts a storybook that will watch for changes and build the examples 52 | 53 | ## Hacking 54 | 55 | The best way to hack on the component is to symlink it into your project using [`npm link`](https://docs.npmjs.com/cli/link). Then, use `npm start` to automatically watch the `modules` directory and output a new build every time something changes. 56 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present, Ryan Florence, Michael Jackson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-css-collapse 2 | Collapse component with css transition for elements with variable and dynamic height. 3 | 4 | [![Build Status](https://travis-ci.org/SparebankenVest/react-css-collapse.svg?branch=master)](https://travis-ci.org/SparebankenVest/react-css-collapse) 5 | [![npm version](https://img.shields.io/npm/v/react-css-collapse.svg?style=flat-square)](https://www.npmjs.com/package/react-css-collapse) 6 | [![npm downloads](https://img.shields.io/npm/dm/react-css-collapse.svg?style=flat-square)](https://www.npmjs.com/package/react-css-collapse) 7 | 8 | ## Example 9 | ### [Accordion using react-css-collapse](https://codesandbox.io/embed/accordion-using-react-css-collapse-w5r1e) 10 | 11 | ## Install 12 | [![rc-collapse](https://nodei.co/npm/react-css-collapse.png)](https://npmjs.org/package/react-css-collapse) 13 | 14 | ## Support 15 | Global coverage > 92% - [browserl.ist](https://browserl.ist/?q=%22%3E0.2%25%22%2C%22not+dead%22%2C%22not+op_mini+all%22%2C%22ios_saf+%3E%3D+10%22) 16 | 17 | ## Usage 18 | 19 | ```jsx 20 | import Collapse from 'react-css-collapse'; 21 | 22 | 23 |
content
24 |
25 | ``` 26 | 27 | ## Properties 28 | 29 | #### `isOpen`: PropTypes.boolean 30 | 31 | Expands or collapses content. 32 | 33 | #### `children`: PropTypes.node 34 | 35 | ```js 36 | 37 |

Paragraph of text

38 |

Another paragraph is also OK

39 |

Images and any other content are ok too

40 | 41 |
42 | ``` 43 | 44 | #### `className`: PropType.string 45 | Specify transition using the class selector with transition or the style property. 46 | The `react-css-collapse-transition` class selector is added by default unless you specify your own. 47 | The default transition can be overridden using the `transition` prop, or with custom styling 👇. Note: replace the selector with your selector if you have specified a different `className`. 48 | 49 | ```scss 50 | .react-css-collapse-transition { 51 | transition: height 250ms cubic-bezier(.4, 0, .2, 1); 52 | } 53 | ``` 54 | 55 | #### `onRest`: PropTypes.func 56 | Callback function for when your transition on `height` (specified in `className`) is finished. It can be used to trigger any function after transition is done. 57 | 58 | ### ARIA and data attributes 59 | 60 | `Collapse` transfers `aria-` and `data-` attributes to the component's rendered DOM element. For example this can be used to set the `aria-hidden` attribute: 61 | 62 | ```js 63 | 64 |

Paragraph of text

65 |
66 | ``` 67 | 68 | ## Development and testing 69 | To run example covering all features, use `npm run storybook`. 70 | 71 | ```bash 72 | git clone [repo] 73 | cd [repo] 74 | npm install 75 | npm run storybook 76 | ``` 77 | Open [http://localhost:6006](http://localhost:6006) 🎆 78 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-css-collapse", 3 | "version": "4.1.0", 4 | "description": "Component-wrapper for collapse animation with css for elements with variable and dynamic height", 5 | "author": { 6 | "name": "Torleif Halseth", 7 | "email": "halseth.torleif@gmail.com" 8 | }, 9 | "contributors": [ 10 | { 11 | "name": "Tony Hopland" 12 | }, 13 | { 14 | "name": "Daniel Selvik" 15 | }, 16 | { 17 | "name": "Matt Shwery" 18 | }, 19 | { 20 | "name": "Nawal Deshi Rahim" 21 | }, 22 | { 23 | "name": "Kristofer Walters" 24 | }, 25 | { 26 | "name": "Christopher Deutsch" 27 | }, 28 | { 29 | "name": "Espen Thomassen Sæverud" 30 | } 31 | ], 32 | "license": "MIT", 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/SparebankenVest/react-css-collapse" 36 | }, 37 | "main": "lib/index.js", 38 | "scripts": { 39 | "test": "jest src", 40 | "test:watch": "npm run test -- --watch", 41 | "babel": "npm run clean && cross-env NODE_ENV=production npx babel -d lib src/components --ignore **/*.test.jsx --source-maps", 42 | "clean": "rm -rf lib", 43 | "build": "npm test && npm run eslint && npm run babel", 44 | "eslint": "npx eslint src --ext .js --ext .jsx", 45 | "format": "prettier --write \"src/**/*.js\" \"src/**/*.jsx\"", 46 | "fix": "npx eslint --fix src --ext .js --ext .jsx", 47 | "prepublish": "npm run build", 48 | "start": "npm run babel -- -w", 49 | "storybook": "start-storybook -p 6006", 50 | "build-storybook": "build-storybook" 51 | }, 52 | "peerDependencies": { 53 | "react": ">=16.8", 54 | "react-dom": ">=16.8" 55 | }, 56 | "devDependencies": { 57 | "@babel/cli": "^7.10.5", 58 | "@babel/core": "^7.11.4", 59 | "@babel/plugin-transform-runtime": "^7.11.0", 60 | "@babel/preset-env": "^7.11.0", 61 | "@babel/preset-react": "^7.10.4", 62 | "@storybook/addon-actions": "^6.0.16", 63 | "@storybook/addon-knobs": "^6.0.16", 64 | "@storybook/react": "^6.0.16", 65 | "@testing-library/jest-dom": "^5.11.4", 66 | "@testing-library/react": "^11.0.2", 67 | "babel-eslint": "^10.0.3", 68 | "babel-jest": "^26.3.0", 69 | "babel-loader": "^8.1.0", 70 | "babel-preset-react-app": "^9.1.2", 71 | "cross-env": "^7.0.2", 72 | "eslint": "^7.7.0", 73 | "eslint-config-airbnb": "^18.2.0", 74 | "eslint-config-prettier": "^6.11.0", 75 | "eslint-plugin-import": "^2.22.0", 76 | "eslint-plugin-jest": "^24.0.0", 77 | "eslint-plugin-jsx-a11y": "^6.3.1", 78 | "eslint-plugin-prettier": "^3.1.4", 79 | "eslint-plugin-react": "^7.20.6", 80 | "eslint-plugin-react-hooks": "^4.1.0", 81 | "jest": "^26.4.2", 82 | "prettier": "^2.1.0", 83 | "prop-types": "^15.7.2", 84 | "react": "^16.13.1", 85 | "react-dom": "^16.13.1" 86 | }, 87 | "directories": { 88 | "test": "test" 89 | }, 90 | "keywords": [ 91 | "react", 92 | "component", 93 | "react-component", 94 | "react-collapse", 95 | "collapse", 96 | "expand", 97 | "dropdown", 98 | "accordion", 99 | "slide-down", 100 | "slide-up" 101 | ], 102 | "browserslist": [ 103 | ">0.2%", 104 | "not dead", 105 | "not op_mini all", 106 | "ios_saf >= 10" 107 | ], 108 | "babel": { 109 | "presets": [ 110 | "@babel/preset-react", 111 | "@babel/preset-env" 112 | ], 113 | "plugins": [ 114 | "@babel/plugin-transform-runtime" 115 | ] 116 | }, 117 | "jest": { 118 | "transform": { 119 | "^.+\\.(js|jsx)$": "babel-jest" 120 | }, 121 | "testURL": "http://localhost/", 122 | "collectCoverageFrom": [ 123 | "**/*.{js,jsx}", 124 | "!**/node_modules/**" 125 | ] 126 | }, 127 | "prettier": { 128 | "endOfLine": "lf", 129 | "parser": "babel", 130 | "singleQuote": true, 131 | "trailingComma": "all", 132 | "printWidth": 80, 133 | "tabWidth": 2 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/components/Collapse.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef } from 'react'; 2 | import { string, bool, func, shape } from 'prop-types'; 3 | import useCollapse from './useCollapse'; 4 | 5 | function Collapse({ 6 | isOpen, 7 | onRest, 8 | style: initialStyle, 9 | transition, 10 | className, 11 | ...rest 12 | }) { 13 | const content = useRef(null); 14 | const { setIsExpandedStyle, setIsCollapsedStyle, style } = useCollapse({ 15 | isOpen, 16 | content, 17 | }); 18 | 19 | const onTransitionEnd = (e) => { 20 | if (e.target === content.current && e.propertyName === 'height') { 21 | if (isOpen) { 22 | setIsExpandedStyle(); 23 | } else { 24 | setIsCollapsedStyle(); 25 | } 26 | if (onRest) { 27 | onRest(); 28 | } 29 | } 30 | }; 31 | 32 | const styles = { 33 | willChange: 'height', 34 | transition, 35 | ...initialStyle, 36 | ...style, 37 | }; 38 | 39 | return ( 40 |
48 | ); 49 | } 50 | 51 | Collapse.defaultProps = { 52 | isOpen: false, 53 | onRest: null, 54 | style: null, 55 | className: 'react-css-collapse-transition', 56 | transition: 'height 250ms cubic-bezier(0.4, 0, 0.2, 1)', 57 | }; 58 | 59 | Collapse.propTypes = { 60 | isOpen: bool, 61 | onRest: func, 62 | style: shape({}), 63 | className: string, 64 | transition: string, 65 | }; 66 | 67 | export default Collapse; 68 | -------------------------------------------------------------------------------- /src/components/Collapse.test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, act, fireEvent } from '@testing-library/react'; 3 | import '@testing-library/jest-dom/extend-expect'; 4 | import Collapse from './Collapse'; 5 | 6 | function transitionEndEventWithHeightProperty(el) { 7 | const event = new window.Event('transitionend', { 8 | bubbles: true, 9 | cancelable: true, 10 | }); 11 | event.propertyName = 'height'; 12 | fireEvent(el, event); 13 | } 14 | 15 | describe('Collapse', () => { 16 | beforeEach(() => { 17 | jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => { 18 | cb(); 19 | }); 20 | }); 21 | 22 | afterEach(() => { 23 | window.requestAnimationFrame.mockRestore(); 24 | }); 25 | 26 | test('default', async () => { 27 | let component; 28 | await act(async () => { 29 | component = render(); 30 | }); 31 | const el = component.getByTestId('collapse'); 32 | expect(el).toHaveStyle( 33 | 'overflow: hidden; visibility: hidden; height: 0px;', 34 | ); 35 | expect(el).toHaveClass('react-css-collapse-transition'); 36 | expect(component.asFragment()).toMatchSnapshot(); 37 | }); 38 | 39 | test('with children', async () => { 40 | let component; 41 | await act(async () => { 42 | component = render(children); 43 | }); 44 | const el = component.getByTestId('collapse'); 45 | expect(el).toHaveTextContent('children'); 46 | expect(component.asFragment()).toMatchSnapshot(); 47 | }); 48 | 49 | test('with class', async () => { 50 | let component; 51 | await act(async () => { 52 | component = render( 53 | , 54 | ); 55 | }); 56 | const el = component.getByTestId('collapse'); 57 | expect(el).toHaveClass('className'); 58 | expect(component.asFragment()).toMatchSnapshot(); 59 | }); 60 | 61 | test('with style', async () => { 62 | let component; 63 | await act(async () => { 64 | component = render( 65 | , 69 | ); 70 | }); 71 | const el = component.getByTestId('collapse'); 72 | expect(el).toHaveStyle( 73 | 'will-change: height; overflow: hidden; visibility: hidden; height: 0px; transition: height 1337ms cubic-bezier(.4, 0, .2, 1);', 74 | ); 75 | expect(component.asFragment()).toMatchSnapshot(); 76 | }); 77 | 78 | test('with transition prop', async () => { 79 | let component; 80 | await act(async () => { 81 | component = render( 82 | , 86 | ); 87 | }); 88 | const el = component.getByTestId('collapse'); 89 | expect(el).toHaveStyle( 90 | 'will-change: height; overflow: hidden; visibility: hidden; height: 0px; transition: height 1337ms cubic-bezier(.4, 0, .2, 1);', 91 | ); 92 | expect(component.asFragment()).toMatchSnapshot(); 93 | }); 94 | 95 | test('with aria and data props', async () => { 96 | let component; 97 | await act(async () => { 98 | component = render( 99 | , 100 | ); 101 | }); 102 | const el = component.getByTestId('collapse'); 103 | expect(el).toHaveAttribute('data-any-value', '1337'); 104 | expect(component.asFragment()).toMatchSnapshot(); 105 | }); 106 | 107 | test('when open before transition end', async () => { 108 | let component; 109 | await act(async () => { 110 | component = render(); 111 | }); 112 | const el = component.getByTestId('collapse'); 113 | expect(el).toHaveStyle( 114 | 'overflow: visible; visibility: visible; height: auto;', 115 | ); 116 | expect(component.asFragment()).toMatchSnapshot(); 117 | }); 118 | 119 | test('when open after transition end', async () => { 120 | let component; 121 | 122 | await act(async () => { 123 | component = render(); 124 | }); 125 | const el = component.getByTestId('collapse'); 126 | transitionEndEventWithHeightProperty(el); 127 | expect(el).toHaveStyle( 128 | 'overflow: visible; visibility: visible; height: auto;', 129 | ); 130 | expect(component.asFragment()).toMatchSnapshot(); 131 | }); 132 | 133 | test('lifecycle', async () => { 134 | // Render default collapsed 135 | const { getByTestId, rerender } = render( 136 | , 137 | ); 138 | expect(getByTestId('collapse')).toHaveStyle( 139 | 'visibility: hidden; overflow: hidden;', 140 | ); 141 | 142 | // Expand 143 | rerender(); 144 | const el = getByTestId('collapse'); 145 | // Style during transition 146 | expect(el).toHaveStyle('visibility: visible; overflow: hidden;'); 147 | // Finished expand transition 148 | transitionEndEventWithHeightProperty(el); 149 | // Style after transition 150 | expect(el).toHaveStyle( 151 | 'visibility: visible; overflow: visible; height: auto;', 152 | ); 153 | 154 | // Collapse 155 | rerender(); 156 | const elCollapsed = getByTestId('collapse'); 157 | // Style during transition 158 | expect(elCollapsed).toHaveStyle('visibility: visible; overflow: visible;'); 159 | 160 | await act(async () => { 161 | // Collapse transition finished 162 | transitionEndEventWithHeightProperty(elCollapsed); 163 | }); 164 | 165 | // Style after transition 166 | expect(elCollapsed).toHaveStyle( 167 | 'visibility: hidden; overflow: hidden; height: 0px', 168 | ); 169 | }); 170 | }); 171 | -------------------------------------------------------------------------------- /src/components/__snapshots__/Collapse.test.jsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Collapse default 1`] = ` 4 | 5 |