Skip to content

Commit

Permalink
feat: add style and transition delay to Collapsible
Browse files Browse the repository at this point in the history
  • Loading branch information
kyledurand committed May 21, 2024
1 parent e7cb7a6 commit 2bc1711
Show file tree
Hide file tree
Showing 4 changed files with 76 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 style prop and 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: '5002'}}
>
<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
14 changes: 11 additions & 3 deletions polaris-react/src/components/Collapsible/Collapsible.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React, {useState, useRef, useEffect, useCallback} from 'react';
import type {ReactNode, CSSProperties, TransitionEvent} from 'react';
import type {MotionDurationScale} from '@shopify/polaris-tokens';

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

Expand All @@ -7,6 +9,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 @@ -30,8 +34,10 @@ export interface CollapsibleProps {
transition?: boolean | Transition;
/** Callback when the animation completes. */
onAnimationEnd?(): void;
/** Overriding styles on the collapsible element */
style?: CSSProperties;
/** The content to display inside the collapsible. */
children?: React.ReactNode;
children?: ReactNode;
}

type AnimationState = 'idle' | 'measuring' | 'animating';
Expand All @@ -43,6 +49,7 @@ export function Collapsible({
variant = 'block',
transition = true,
children,
style,
onAnimationEnd,
}: CollapsibleProps) {
const [size, setSize] = useState(0);
Expand All @@ -52,7 +59,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,11 +75,13 @@ export function Collapsible({
const transitionDisabled = isTransitionDisabled(transition);

const transitionStyles = typeof transition === 'object' && {
transitionDelay: `var(--p-motion-duration-${transition.delay})`,
transitionDuration: transition.duration,
transitionTimingFunction: transition.timingFunction,
};

const collapsibleStyles = {
...style,
...transitionStyles,
...(vertical
? {
Expand All @@ -87,7 +95,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 2bc1711

Please sign in to comment.