Skip to content

Commit 033fe43

Browse files
authored
docs(react-motion): document motion slot customization APIs more thoroughly (#35922)
1 parent 5b9ec13 commit 033fe43

30 files changed

Lines changed: 1826 additions & 201 deletions
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "feat: re-export motionSlot and presenceMotionSlot from @fluentui/react-motion",
4+
"packageName": "@fluentui/react-components",
5+
"email": "robertpenner@microsoft.com",
6+
"dependentChangeType": "patch"
7+
}

packages/react-components/react-components/etc/react-components.api.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,8 @@ import { MessageBarTitleState } from '@fluentui/react-message-bar';
679679
import { MessageBarTransitionContextValue } from '@fluentui/react-message-bar';
680680
import { MotionComponentProps } from '@fluentui/react-motion';
681681
import { MotionImperativeRef } from '@fluentui/react-motion';
682+
import { motionSlot } from '@fluentui/react-motion';
683+
import { MotionSlotProps } from '@fluentui/react-motion';
682684
import { motionTokens } from '@fluentui/react-motion';
683685
import { Nav } from '@fluentui/react-nav';
684686
import { NavButtonRenderFunction } from '@fluentui/react-carousel';
@@ -834,6 +836,8 @@ import { presenceDndRegular } from '@fluentui/react-badge';
834836
import { PresenceGroup } from '@fluentui/react-motion';
835837
import { PresenceMotion } from '@fluentui/react-motion';
836838
import { PresenceMotionFn } from '@fluentui/react-motion';
839+
import { presenceMotionSlot } from '@fluentui/react-motion';
840+
import { PresenceMotionSlotProps } from '@fluentui/react-motion';
837841
import { presenceOfflineRegular } from '@fluentui/react-badge';
838842
import { presenceOofRegular } from '@fluentui/react-badge';
839843
import { presenceUnknownRegular } from '@fluentui/react-badge';
@@ -3377,6 +3381,10 @@ export { MotionComponentProps }
33773381

33783382
export { MotionImperativeRef }
33793383

3384+
export { motionSlot }
3385+
3386+
export { MotionSlotProps }
3387+
33803388
export { motionTokens }
33813389

33823390
export { Nav }
@@ -3687,6 +3695,10 @@ export { PresenceMotion }
36873695

36883696
export { PresenceMotionFn }
36893697

3698+
export { presenceMotionSlot }
3699+
3700+
export { PresenceMotionSlotProps }
3701+
36903702
export { presenceOfflineRegular }
36913703

36923704
export { presenceOofRegular }

packages/react-components/react-components/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,16 +1912,20 @@ export {
19121912
createMotionComponentVariant,
19131913
createPresenceComponent,
19141914
createPresenceComponentVariant,
1915+
motionSlot,
1916+
presenceMotionSlot,
19151917
PresenceGroup,
19161918
} from '@fluentui/react-motion';
19171919
export type {
19181920
AtomMotion,
19191921
AtomMotionFn,
19201922
MotionComponentProps,
19211923
MotionImperativeRef,
1924+
MotionSlotProps,
19221925
PresenceComponent,
19231926
PresenceMotion,
19241927
PresenceMotionFn,
1928+
PresenceMotionSlotProps,
19251929
PresenceComponentProps,
19261930
} from '@fluentui/react-motion';
19271931

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
`Dialog`'s `surfaceMotion` slot accepts `Scale` params directly, such as `duration`, `outScale`, `easing`, and `animateOpacity`. `DialogSurface`'s `backdropMotion` slot accepts `Fade` params.
2+
3+
Both slots also support the `children` render function, which allows replacing the default motion with a custom implementation. This story demonstrates the simpler direct prop approach:
4+
5+
```tsx
6+
<Dialog surfaceMotion={{ duration, outScale, easing, animateOpacity }}>
7+
<DialogSurface backdropMotion={{ duration: backdropDuration }}>…</DialogSurface>
8+
</Dialog>
9+
```
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import * as React from 'react';
2+
import type { JSXElement } from '@fluentui/react-components';
3+
import {
4+
Button,
5+
Dialog,
6+
DialogActions,
7+
DialogBody,
8+
DialogContent,
9+
DialogSurface,
10+
DialogTitle,
11+
DialogTrigger,
12+
Field,
13+
makeStyles,
14+
motionTokens,
15+
Slider,
16+
Switch,
17+
tokens,
18+
} from '@fluentui/react-components';
19+
import description from './DialogMotionCustom.stories.md';
20+
21+
const useStyles = makeStyles({
22+
controls: {
23+
display: 'flex',
24+
flexDirection: 'column',
25+
gap: tokens.spacingVerticalS,
26+
marginBottom: tokens.spacingVerticalL,
27+
padding: tokens.spacingVerticalM,
28+
border: `${tokens.strokeWidthThicker} solid ${tokens.colorNeutralForeground3}`,
29+
borderRadius: tokens.borderRadiusMedium,
30+
},
31+
});
32+
33+
export const MotionCustom = (): JSXElement => {
34+
const classes = useStyles();
35+
const [duration, setDuration] = React.useState(600);
36+
const [outScale, setOutScale] = React.useState(0.5);
37+
const [animateOpacity, setAnimateOpacity] = React.useState(true);
38+
const [backdropDuration, setBackdropDuration] = React.useState(300);
39+
40+
const easing = motionTokens.curveDecelerateMid;
41+
42+
return (
43+
<>
44+
<div className={classes.controls}>
45+
<Field label={`Surface duration: ${duration}ms`}>
46+
<Slider min={100} max={2000} step={50} value={duration} onChange={(_, data) => setDuration(data.value)} />
47+
</Field>
48+
<Field label={`Surface outScale: ${outScale.toFixed(2)}`}>
49+
<Slider min={0} max={1} step={0.05} value={outScale} onChange={(_, data) => setOutScale(data.value)} />
50+
</Field>
51+
<Field label={`Backdrop duration: ${backdropDuration}ms`}>
52+
<Slider
53+
min={0}
54+
max={1000}
55+
step={50}
56+
value={backdropDuration}
57+
onChange={(_, data) => setBackdropDuration(data.value)}
58+
/>
59+
</Field>
60+
<Switch
61+
label="Surface animateOpacity"
62+
checked={animateOpacity}
63+
onChange={(_, data) => setAnimateOpacity(data.checked)}
64+
/>
65+
</div>
66+
67+
<Dialog surfaceMotion={{ duration, outScale, easing, animateOpacity }}>
68+
<DialogTrigger disableButtonEnhancement>
69+
<Button>Open Dialog</Button>
70+
</DialogTrigger>
71+
<DialogSurface backdropMotion={{ duration: backdropDuration }}>
72+
<DialogBody>
73+
<DialogTitle>Dialog with custom motion params</DialogTitle>
74+
<DialogContent>
75+
This dialog's surface animation is driven by direct <code>surfaceMotion</code> params (`duration`,
76+
`outScale`, `easing`, `animateOpacity`). Its backdrop fade is tuned independently via{' '}
77+
<code>backdropMotion</code> (`duration`).
78+
</DialogContent>
79+
<DialogActions>
80+
<DialogTrigger disableButtonEnhancement>
81+
<Button appearance="secondary">Close</Button>
82+
</DialogTrigger>
83+
</DialogActions>
84+
</DialogBody>
85+
</DialogSurface>
86+
</Dialog>
87+
</>
88+
);
89+
};
90+
91+
MotionCustom.parameters = {
92+
docs: {
93+
description: {
94+
story: description,
95+
},
96+
},
97+
};

packages/react-components/react-dialog/stories/src/Dialog/index.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export { WithForm } from './DialogWithForm.stories';
2222
export { TitleCustomAction } from './DialogTitleCustomAction.stories';
2323
export { TitleNoAction } from './DialogTitleNoAction.stories';
2424
export { Confirmation } from './DialogConfirmation.stories';
25+
export { MotionCustom } from './DialogMotionCustom.stories';
2526

2627
// Typing with Meta<typeof Dialog> generates a type error for the `subcomponents` property.
2728
// https://github.com/storybookjs/storybook/issues/27535
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
You can replace a component's default animation by providing a `children` render function to a motion slot. The render function receives the default motion component and its props, and you return your own animation component built with [createPresenceComponent](?path=/docs/motion-apis-createpresencecomponent--docs).
2+
3+
**Dialog** has two customizable motion slots:
4+
5+
- `surfaceMotion` on `<Dialog>` — the surface enter/exit animation
6+
- `backdropMotion` on `<DialogSurface>` — the backdrop overlay animation
7+
8+
**Drawer** also supports this via:
9+
10+
- `surfaceMotion` — the slide animation of the drawer panel

0 commit comments

Comments
 (0)