-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Rogin Farrer <rfarrer@wayfair.com>
- Loading branch information
1 parent
15af3d5
commit 5e427ec
Showing
45 changed files
with
2,282 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
--- | ||
'react-collapsed': major | ||
--- | ||
|
||
This is a big refactor of `react-collapsed`, enough I wanted to denote it with a new major version. | ||
|
||
## BREAKING CHANGES | ||
|
||
- `expandStyles` and `collapseStyles` options have been removed. | ||
- `onExpandStart`, `onExpandEnd`, `onCollapseStart`, `onCollapseEnd` options have been removed and replaced with `onTransitionStateChange`: | ||
|
||
```typescript | ||
const useCollapse({ | ||
onTransitionStateChange(stage) { | ||
switch (stage) { | ||
case 'expandStart': | ||
case 'expandEnd': | ||
case 'expanding': | ||
case 'collapseStart': | ||
case 'collapseEnd': | ||
case 'collapsing': | ||
// do thing | ||
default: | ||
break; | ||
} | ||
} | ||
}) | ||
``` | ||
|
||
## Other changes | ||
|
||
- Unique IDs for accessibility are now generated with `React.useId` if it's available. | ||
- Styles assigned to the collapse element are now assigned to the DOM element directly via a ref, and no longer require `ReactDOM.flushSync` to update styles in transition. | ||
- Added `role="region"` to collapse. | ||
- Added logic to handle disabling the animation if the user has the prefers reduced motion setting enabled. | ||
- Replaced animation resolution handling to a programmatic timer, instead of the `'transitionend'` event. Should fix #103. | ||
- Improved the types for `getCollapseProps` and `getToggleProps`, so their arguments and return type is more accurately typed. This should help catch cases when props returned by the getters are duplicated on the component (such as `ref` or `style`). | ||
- Changes the props returned by `getToggleProps` depending on the HTML tag of the component. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
registry = https://registry.npmjs.org | ||
public-hoist-pattern[]=*storybook* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
packages/react-collapsed/README.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
dist | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = { | ||
extends: ['collapsed', 'collapsed/react'], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module.exports = { | ||
stories: ['../stories/**/*.stories.@(js|jsx|ts|tsx)'], | ||
addons: [ | ||
'@storybook/addon-links', | ||
'@storybook/addon-essentials', | ||
'@storybook/addon-a11y', | ||
], | ||
core: { | ||
builder: '@storybook/builder-vite', | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export const parameters = { | ||
actions: { argTypesRegex: "^on[A-Z].*" }, | ||
controls: { | ||
matchers: { | ||
color: /(background|color)$/i, | ||
date: /Date$/, | ||
}, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
Changelog has been moved to [the releases tab](https://github.com/roginfarrer/react-collapsed/releases). | ||
|
||
# 2.0.0 | ||
|
||
Complete rewrite using React hooks! | ||
|
||
- Ends support for React versions < 16.8.x | ||
- Library now exports a custom hook in lieu of a render prop component | ||
- Adds support for unmounting the contents of the Collapse element when closed | ||
|
||
```js | ||
import React from 'react' | ||
import useCollapse from 'react-collapsed' | ||
|
||
function Demo() { | ||
const { getCollapseProps, getToggleProps, isOpen } = useCollapse() | ||
|
||
return ( | ||
<> | ||
<button {...getToggleProps()}>{isOpen ? 'Collapse' : 'Expand'}</button> | ||
<section {...getCollapseProps()}>Collapsed content 🙈</section> | ||
</> | ||
) | ||
} | ||
``` | ||
|
||
# 1.0.0 | ||
|
||
Bumped to full release! :) | ||
|
||
- `duration`, `easing`, and `delay` now support taking an object with `in` and `out` keys to configure differing in-and-out transitions | ||
|
||
# 0.2.0 | ||
|
||
### Breaking Changes | ||
|
||
- `getCollapsibleProps` => `getCollapseProps`. Renamed since it's easier to spell 😅 | ||
|
||
### Other | ||
|
||
- Slew of Flow bug fixes | ||
- Improved documentation | ||
|
||
# 0.1.3 | ||
|
||
- ESLINT wasn't working properly - fixed this | ||
- Added `files` key to package.json to improve NPM load |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
# 🙈 react-collapsed (useCollapse) | ||
|
||
[![CI][ci-badge]][ci] | ||
![npm bundle size (version)][minzipped-badge] | ||
[![npm version][npm-badge]][npm-version] | ||
[![Netlify Status](https://api.netlify.com/api/v1/badges/5a5b0e80-d15e-4983-976d-37fe6bdada7a/deploy-status)](https://app.netlify.com/sites/react-collapsed/deploys) | ||
|
||
A React hook for creating accessible expand/collapse components. Animates the height using CSS transitions from `0` to `auto`. | ||
|
||
## Features | ||
|
||
- Handles the height of animations of your elements, `auto` included! | ||
- You control the UI - `useCollapse` provides the necessary props, you control the styles and the elements. | ||
- Accessible out of the box - no need to worry if your collapse/expand component is accessible, since this takes care of it for you! | ||
- No animation framework required! Simply powered by CSS animations | ||
- Written in TypeScript | ||
|
||
## Demo | ||
|
||
[See the demo site!](https://react-collapsed.netlify.app/) | ||
|
||
[CodeSandbox demo](https://codesandbox.io/s/magical-browser-vibv2?file=/src/App.tsx) | ||
|
||
## Installation | ||
|
||
```bash | ||
$ npm i react-collapsed | ||
``` | ||
|
||
## Usage | ||
|
||
### Simple Usage | ||
|
||
```js | ||
import React from 'react' | ||
import { useCollapse } from 'react-collapsed' | ||
|
||
function Demo() { | ||
const { getCollapseProps, getToggleProps, isExpanded } = useCollapse() | ||
|
||
return ( | ||
<div> | ||
<button {...getToggleProps()}> | ||
{isExpanded ? 'Collapse' : 'Expand'} | ||
</button> | ||
<section {...getCollapseProps()}>Collapsed content 🙈</section> | ||
</div> | ||
) | ||
} | ||
``` | ||
|
||
### Control it yourself | ||
|
||
```js | ||
import React, { useState } from 'react' | ||
import { useCollapse } from 'react-collapsed' | ||
|
||
function Demo() { | ||
const [isExpanded, setExpanded] = useState(false) | ||
const { getCollapseProps, getToggleProps } = useCollapse({ isExpanded }) | ||
|
||
return ( | ||
<div> | ||
<button | ||
{...getToggleProps({ | ||
onClick: () => setExpanded((prevExpanded) => !prevExpanded), | ||
})} | ||
> | ||
{isExpanded ? 'Collapse' : 'Expand'} | ||
</button> | ||
<section {...getCollapseProps()}>Collapsed content 🙈</section> | ||
</div> | ||
) | ||
} | ||
``` | ||
|
||
## API | ||
|
||
```js | ||
const { getCollapseProps, getToggleProps, isExpanded, setExpanded } = | ||
useCollapse({ | ||
isExpanded: boolean, | ||
defaultExpanded: boolean, | ||
collapsedHeight: 0, | ||
easing: string, | ||
duration: number, | ||
onTransitionStateChange: func, | ||
}) | ||
``` | ||
|
||
### `useCollapse` Config | ||
|
||
The following are optional properties passed into `useCollapse({ })`: | ||
|
||
| Prop | Type | Default | Description | | ||
| ----------------------- | -------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| isExpanded | boolean | `undefined` | If true, the Collapse is expanded | | ||
| defaultExpanded | boolean | `false` | If true, the Collapse will be expanded when mounted | | ||
| collapsedHeight | number | `0` | The height of the content when collapsed | | ||
| easing | string | `cubic-bezier(0.4, 0, 0.2, 1)` | The transition timing function for the animation | | ||
| duration | number | `undefined` | The duration of the animation in milliseconds. By default, the duration is programmatically calculated based on the height of the collapsed element | | ||
| onTransitionStateChange | function | no-op | Handler called with at each stage of the transition animation | | ||
| hasDisabledAnimation | boolean | false | If true, will disable the animation | | ||
|
||
### What you get | ||
|
||
| Name | Description | | ||
| ---------------- | ----------------------------------------------------------------------------------------------------------- | | ||
| getCollapseProps | Function that returns a prop object, which should be spread onto the collapse element | | ||
| getToggleProps | Function that returns a prop object, which should be spread onto an element that toggles the collapse panel | | ||
| isExpanded | Whether or not the collapse is expanded (if not controlled) | | ||
| setExpanded | Sets the hook's internal isExpanded state | | ||
|
||
## Alternative Solutions | ||
|
||
- [react-spring](https://www.react-spring.io/) - JavaScript animation based library that can potentially have smoother animations. Requires a bit more work to create an accessible collapse component. | ||
- [react-animate-height](https://github.com/Stanko/react-animate-height/) - Another library that uses CSS transitions to animate to any height. It provides components, not a hook. | ||
|
||
## FAQ | ||
|
||
<details> | ||
<summary>When I apply vertical <code>padding</code> to the component that gets <code>getCollapseProps</code>, the animation is janky and it doesn't collapse all the way. What gives?</summary> | ||
|
||
The collapse works by manipulating the `height` property. If an element has vertical padding, that padding expandes the size of the element, even if it has `height: 0; overflow: hidden`. | ||
|
||
To avoid this, simply move that padding from the element to an element directly nested within in. | ||
|
||
```javascript | ||
// from | ||
<div {...getCollapseProps({style: {padding: 20}})} | ||
This will do weird things | ||
</div> | ||
|
||
// to | ||
<div {...getCollapseProps()} | ||
<div style={{padding: 20}}> | ||
Much better! | ||
</div> | ||
</div> | ||
``` | ||
|
||
</details> | ||
|
||
[minzipped-badge]: https://img.shields.io/bundlephobia/minzip/react-collapsed/latest | ||
[npm-badge]: http://img.shields.io/npm/v/react-collapsed.svg?style=flat | ||
[npm-version]: https://npmjs.org/package/react-collapsed 'View this project on npm' | ||
[ci-badge]: https://github.com/roginfarrer/collapsed/workflows/CI/badge.svg | ||
[ci]: https://github.com/roginfarrer/collapsed/actions?query=workflow%3ACI+branch%3Amain | ||
[netlify]: https://app.netlify.com/sites/react-collapsed/deploys | ||
[netlify-badge]: https://api.netlify.com/api/v1/badges/4d285ffc-aa4f-4d32-8549-eb58e00dd2d1/deploy-status |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { defineConfig } from 'cypress' | ||
|
||
export default defineConfig({ | ||
component: { | ||
devServer: { | ||
framework: 'react', | ||
bundler: 'vite', | ||
}, | ||
}, | ||
video: false, | ||
screenshotOnRunFailure: false, | ||
}) |
50 changes: 50 additions & 0 deletions
50
packages/react-collapsed/cypress/component/Controlled.cy.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import * as React from 'react' | ||
import { useCollapse } from '../../src' | ||
|
||
const Collapse = React.forwardRef< | ||
HTMLDivElement, | ||
React.ComponentPropsWithoutRef<'div'> | ||
>(function Collapse(props, ref) { | ||
return ( | ||
<div {...props} ref={ref} data-testid="collapse"> | ||
<div | ||
style={{ | ||
height: 300, | ||
border: '2px solid red', | ||
backgroundColor: 'lightblue', | ||
}} | ||
> | ||
helloooo | ||
</div> | ||
</div> | ||
) | ||
}) | ||
|
||
const Controlled = () => { | ||
const [isExpanded, setOpen] = React.useState<boolean>(true) | ||
const { getToggleProps, getCollapseProps } = useCollapse({ | ||
isExpanded, | ||
}) | ||
|
||
return ( | ||
<div> | ||
<button {...getToggleProps({ onClick: () => setOpen((x) => !x) })}> | ||
{isExpanded ? 'Close' : 'Open'} | ||
</button> | ||
<Collapse {...getCollapseProps()} /> | ||
</div> | ||
) | ||
} | ||
|
||
describe('Controlled', () => { | ||
it('playground', () => { | ||
cy.mount(<Controlled />) | ||
|
||
// getToggleProps | ||
cy.get('button').should('have.text', 'Close') | ||
cy.get('[data-testid="collapse"]').should('be.visible') | ||
cy.get('button').click() | ||
cy.get('button').should('have.text', 'Open') | ||
cy.get('[data-testid="collapse"]').should('not.be.visible') | ||
}) | ||
}) |
Oops, something went wrong.