Skip to content

Commit 5fcca65

Browse files
kyledurandlaurkim
andauthored
[Collapsible] Add transition delay (#12118)
<!-- ☝️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 https://github.com/Shopify/polaris-internal/issues/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>
1 parent b54975a commit 5fcca65

File tree

4 files changed

+73
-13
lines changed

4 files changed

+73
-13
lines changed

.changeset/good-eels-swim.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/polaris': minor
3+
---
4+
5+
Added transition delay to Collapsible

polaris-react/src/components/Collapsible/Collapsible.stories.tsx

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export const Default = {
5757
);
5858
},
5959
};
60+
6061
export const Inline = {
6162
render() {
6263
const [open, setOpen] = useState(true);
@@ -70,14 +71,44 @@ export const Inline = {
7071
<Button
7172
onClick={handleToggle}
7273
ariaExpanded={open}
73-
ariaControls="basic-collapsible"
74+
ariaControls="inline-collapsible"
7475
>
7576
Toggle
7677
</Button>
7778
<Collapsible open={open} id="inline-collapsible" variant="inline">
78-
<p style={{whiteSpace: 'nowrap', backgroundColor: 'red'}}>
79-
Non breaking text
80-
</p>
79+
<p style={{whiteSpace: 'nowrap'}}>Non breaking text</p>
80+
</Collapsible>
81+
</LegacyStack>
82+
</LegacyCard>
83+
</div>
84+
);
85+
},
86+
};
87+
88+
export const WithDelay = {
89+
render() {
90+
const [open, setOpen] = useState(true);
91+
92+
const handleToggle = useCallback(() => setOpen((open) => !open), []);
93+
94+
return (
95+
<div style={{height: '200px'}}>
96+
<LegacyCard sectioned>
97+
<LegacyStack alignment="center">
98+
<Button
99+
onClick={handleToggle}
100+
ariaExpanded={open}
101+
ariaControls="inline-collapsible"
102+
>
103+
Toggle
104+
</Button>
105+
<Collapsible
106+
open={open}
107+
id="inline-collapsible"
108+
variant="inline"
109+
transition={{delay: '500'}}
110+
>
111+
<p style={{whiteSpace: 'nowrap'}}>Non breaking text</p>
81112
</Collapsible>
82113
</LegacyStack>
83114
</LegacyCard>
@@ -119,9 +150,7 @@ export const AnimateIn = {
119150
duration: 'var(--p-motion-duration-250)',
120151
}}
121152
>
122-
<p style={{whiteSpace: 'nowrap', backgroundColor: 'red'}}>
123-
Non breaking text
124-
</p>
153+
<p style={{whiteSpace: 'nowrap'}}>Non breaking text</p>
125154
</Collapsible>
126155

127156
<Button
@@ -141,7 +170,7 @@ export const AnimateIn = {
141170
<Box maxWidth="20%">
142171
<Collapsible
143172
open={open}
144-
id="inline-collapsible"
173+
id="basic-collapsible"
145174
transition={{
146175
animateIn: true,
147176
duration: 'var(--p-motion-duration-250)',

polaris-react/src/components/Collapsible/Collapsible.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import React, {useState, useRef, useEffect, useCallback} from 'react';
2+
import type {ReactNode, TransitionEvent} from 'react';
3+
import {createVar} from '@shopify/polaris-tokens';
4+
import type {MotionDurationScale} from '@shopify/polaris-tokens';
25

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

@@ -7,6 +10,8 @@ import styles from './Collapsible.module.css';
710
interface Transition {
811
/** Expand the collpsible on render. */
912
animateIn?: boolean;
13+
/** Assign a transition delay to the collapsible animation */
14+
delay?: MotionDurationScale;
1015
/** Assign a transition duration to the collapsible animation. */
1116
duration?: string;
1217
/** Assign a transition timing function to the collapsible animation */
@@ -31,7 +36,7 @@ export interface CollapsibleProps {
3136
/** Callback when the animation completes. */
3237
onAnimationEnd?(): void;
3338
/** The content to display inside the collapsible. */
34-
children?: React.ReactNode;
39+
children?: ReactNode;
3540
}
3641

3742
type AnimationState = 'idle' | 'measuring' | 'animating';
@@ -52,7 +57,6 @@ export function Collapsible({
5257
const [animationState, setAnimationState] = useState<AnimationState>(
5358
animateIn ? 'measuring' : 'idle',
5459
);
55-
5660
const isFullyOpen = animationState === 'idle' && open && isOpen;
5761
const isFullyClosed = animationState === 'idle' && !open && !isOpen;
5862
const content = expandOnPrint || !isFullyClosed ? children : null;
@@ -69,6 +73,7 @@ export function Collapsible({
6973
const transitionDisabled = isTransitionDisabled(transition);
7074

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

8994
const handleCompleteAnimation = useCallback(
90-
({target}: React.TransitionEvent<HTMLDivElement>) => {
95+
({target}: TransitionEvent<HTMLDivElement>) => {
9196
if (target === collapsibleContainer.current) {
9297
setAnimationState('idle');
9398
setIsOpen(open);

polaris-react/src/components/Collapsible/tests/Collapsible.test.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,11 @@ describe('<Collapsible />', () => {
152152
<Collapsible id="test-collapsible" open transition={{duration}} />,
153153
);
154154

155-
expect(collapsible).toHaveReactProps({transition: {duration}});
155+
expect(collapsible).toContainReactComponent('div', {
156+
style: expect.objectContaining({
157+
transitionDuration: duration,
158+
}),
159+
});
156160
});
157161

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

168-
expect(collapsible).toHaveReactProps({transition: {timingFunction}});
172+
expect(collapsible).toContainReactComponent('div', {
173+
style: expect.objectContaining({
174+
transitionTimingFunction: timingFunction,
175+
}),
176+
});
177+
});
178+
179+
it('passes a delay property', () => {
180+
const delay = '100';
181+
const collapsible = mountWithApp(
182+
<Collapsible id="test-collapsible" open transition={{delay}} />,
183+
);
184+
185+
expect(collapsible).toContainReactComponent('div', {
186+
style: expect.objectContaining({
187+
transitionDelay: `var(--p-motion-duration-${delay})`,
188+
}),
189+
});
169190
});
170191

171192
const transitionDisabledOptions = [

0 commit comments

Comments
 (0)