Skip to content

Commit

Permalink
[Collapsible] Add transition delay (#12118)
Browse files Browse the repository at this point in the history
<!--
  ☝️How to write a good PR title:
- Prefix it with [ComponentName] (if applicable), for example: [Button]
  - Start with a verb, for example: Add, Delete, Improve, Fix…
  - Give as much context as necessary and as little as possible
  - Open it as a draft if it’s a work in progress
-->

### WHY are these changes introduced?

Part of Shopify/polaris-internal#159

### WHAT is this pull request doing?

Adds a transition delay property. This will be useful while waiting for
the search bar to animate out (top) before collapsing

~~Adds a style prop. Necessary for things like controlling width in a
flex context. I'm not sure we need a full style prop, or classnames, or
just try to limit it to flex grow / shrink. Open to ideas there. This is
a pretty generic container so I don't think allowing a style prop is too
bad.~~

**UPDATE: Opting to override in consumer css instead of adding a style
prop here**

### 🎩 checklist

- [ ] Tested a
[snapshot](https://github.com/Shopify/polaris/blob/main/documentation/Releasing.md#-snapshot-releases)
- [ ] Tested on
[mobile](https://github.com/Shopify/polaris/blob/main/documentation/Tophatting.md#cross-browser-testing)
- [ ] Tested on [multiple
browsers](https://help.shopify.com/en/manual/shopify-admin/supported-browsers)
- [ ] Tested for
[accessibility](https://github.com/Shopify/polaris/blob/main/documentation/Accessibility%20testing.md)
- [ ] Updated the component's `README.md` with documentation changes
- [ ] [Tophatted
documentation](https://github.com/Shopify/polaris/blob/main/documentation/Tophatting%20documentation.md)
changes in the style guide

---------

Co-authored-by: Lo Kim <lo.kim@shopify.com>
  • Loading branch information
kyledurand and laurkim committed May 21, 2024
1 parent b54975a commit 5fcca65
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 13 deletions.
5 changes: 5 additions & 0 deletions .changeset/good-eels-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/polaris': minor
---

Added transition delay to Collapsible
45 changes: 37 additions & 8 deletions polaris-react/src/components/Collapsible/Collapsible.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const Default = {
);
},
};

export const Inline = {
render() {
const [open, setOpen] = useState(true);
Expand All @@ -70,14 +71,44 @@ export const Inline = {
<Button
onClick={handleToggle}
ariaExpanded={open}
ariaControls="basic-collapsible"
ariaControls="inline-collapsible"
>
Toggle
</Button>
<Collapsible open={open} id="inline-collapsible" variant="inline">
<p style={{whiteSpace: 'nowrap', backgroundColor: 'red'}}>
Non breaking text
</p>
<p style={{whiteSpace: 'nowrap'}}>Non breaking text</p>
</Collapsible>
</LegacyStack>
</LegacyCard>
</div>
);
},
};

export const WithDelay = {
render() {
const [open, setOpen] = useState(true);

const handleToggle = useCallback(() => setOpen((open) => !open), []);

return (
<div style={{height: '200px'}}>
<LegacyCard sectioned>
<LegacyStack alignment="center">
<Button
onClick={handleToggle}
ariaExpanded={open}
ariaControls="inline-collapsible"
>
Toggle
</Button>
<Collapsible
open={open}
id="inline-collapsible"
variant="inline"
transition={{delay: '500'}}
>
<p style={{whiteSpace: 'nowrap'}}>Non breaking text</p>
</Collapsible>
</LegacyStack>
</LegacyCard>
Expand Down Expand Up @@ -119,9 +150,7 @@ export const AnimateIn = {
duration: 'var(--p-motion-duration-250)',
}}
>
<p style={{whiteSpace: 'nowrap', backgroundColor: 'red'}}>
Non breaking text
</p>
<p style={{whiteSpace: 'nowrap'}}>Non breaking text</p>
</Collapsible>

<Button
Expand All @@ -141,7 +170,7 @@ export const AnimateIn = {
<Box maxWidth="20%">
<Collapsible
open={open}
id="inline-collapsible"
id="basic-collapsible"
transition={{
animateIn: true,
duration: 'var(--p-motion-duration-250)',
Expand Down
11 changes: 8 additions & 3 deletions polaris-react/src/components/Collapsible/Collapsible.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import React, {useState, useRef, useEffect, useCallback} from 'react';
import type {ReactNode, TransitionEvent} from 'react';
import {createVar} from '@shopify/polaris-tokens';
import type {MotionDurationScale} from '@shopify/polaris-tokens';

import {classNames} from '../../utilities/css';

Expand All @@ -7,6 +10,8 @@ import styles from './Collapsible.module.css';
interface Transition {
/** Expand the collpsible on render. */
animateIn?: boolean;
/** Assign a transition delay to the collapsible animation */
delay?: MotionDurationScale;
/** Assign a transition duration to the collapsible animation. */
duration?: string;
/** Assign a transition timing function to the collapsible animation */
Expand All @@ -31,7 +36,7 @@ export interface CollapsibleProps {
/** Callback when the animation completes. */
onAnimationEnd?(): void;
/** The content to display inside the collapsible. */
children?: React.ReactNode;
children?: ReactNode;
}

type AnimationState = 'idle' | 'measuring' | 'animating';
Expand All @@ -52,7 +57,6 @@ export function Collapsible({
const [animationState, setAnimationState] = useState<AnimationState>(
animateIn ? 'measuring' : 'idle',
);

const isFullyOpen = animationState === 'idle' && open && isOpen;
const isFullyClosed = animationState === 'idle' && !open && !isOpen;
const content = expandOnPrint || !isFullyClosed ? children : null;
Expand All @@ -69,6 +73,7 @@ export function Collapsible({
const transitionDisabled = isTransitionDisabled(transition);

const transitionStyles = typeof transition === 'object' && {
transitionDelay: createVar(`motion-duration-${transition.delay ?? '0'}`),
transitionDuration: transition.duration,
transitionTimingFunction: transition.timingFunction,
};
Expand All @@ -87,7 +92,7 @@ export function Collapsible({
};

const handleCompleteAnimation = useCallback(
({target}: React.TransitionEvent<HTMLDivElement>) => {
({target}: TransitionEvent<HTMLDivElement>) => {
if (target === collapsibleContainer.current) {
setAnimationState('idle');
setIsOpen(open);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,11 @@ describe('<Collapsible />', () => {
<Collapsible id="test-collapsible" open transition={{duration}} />,
);

expect(collapsible).toHaveReactProps({transition: {duration}});
expect(collapsible).toContainReactComponent('div', {
style: expect.objectContaining({
transitionDuration: duration,
}),
});
});

it('passes a timingFunction property', () => {
Expand All @@ -165,7 +169,24 @@ describe('<Collapsible />', () => {
/>,
);

expect(collapsible).toHaveReactProps({transition: {timingFunction}});
expect(collapsible).toContainReactComponent('div', {
style: expect.objectContaining({
transitionTimingFunction: timingFunction,
}),
});
});

it('passes a delay property', () => {
const delay = '100';
const collapsible = mountWithApp(
<Collapsible id="test-collapsible" open transition={{delay}} />,
);

expect(collapsible).toContainReactComponent('div', {
style: expect.objectContaining({
transitionDelay: `var(--p-motion-duration-${delay})`,
}),
});
});

const transitionDisabledOptions = [
Expand Down

0 comments on commit 5fcca65

Please sign in to comment.