Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Stories,
type DocsContextProps,
} from '@storybook/addon-docs';
import type { PreparedStory, Renderer } from '@storybook/types';
import type { SBEnumType } from '@storybook/csf';
import { makeStyles, shorthands, tokens, Link, Text } from '@fluentui/react-components';
import { InfoFilled } from '@fluentui/react-icons';
Expand All @@ -18,6 +19,8 @@ import { DirSwitch } from './DirSwitch.stories';
import { ThemePicker } from './ThemePicker.stories';
import { Toc, nameToHash } from './Toc.stories';

type PrimaryStory = PreparedStory<Renderer>;

const useStyles = makeStyles({
divider: {
height: '1px',
Expand Down Expand Up @@ -130,6 +133,49 @@ const getNativeElementsList = (elements: SBEnumType['value']): JSX.Element => {
);
};

const RenderArgsTable = ({ hideArgsTable, primaryStory }: { primaryStory: PrimaryStory; hideArgsTable: boolean }) => {
const styles = useStyles();
return hideArgsTable ? null : (
<>
<ArgsTable of={primaryStory.component} />
{primaryStory.argTypes.as && primaryStory.argTypes.as?.type?.name === 'enum' && (
<div className={styles.nativeProps}>
<InfoFilled className={styles.nativePropsIcon} />
<div className={styles.nativePropsMessage}>
<b>
Native props are supported <span role="presentation">🙌</span>
</b>
<span>
All HTML attributes native to the {getNativeElementsList(primaryStory.argTypes.as.type.value)}, including
all <code>aria-*</code> and <code>data-*</code> attributes, can be applied as native props on this
component.
</span>
</div>
</div>
)}
</>
);
};

const RenderPrimaryStory = ({
primaryStory,
skipPrimaryStory,
}: {
primaryStory: PrimaryStory;
skipPrimaryStory: boolean;
}) => {
const styles = useStyles();
return skipPrimaryStory ? null : (
<>
<hr className={styles.divider} />
<HeaderMdx as="h3" id={nameToHash(primaryStory.name)}>
{primaryStory.name}
</HeaderMdx>
<Primary />
</>
);
};

export const FluentDocsPage = () => {
const context = React.useContext(DocsContext);
const stories = context.componentStories();
Expand All @@ -141,6 +187,9 @@ export const FluentDocsPage = () => {
const dir = primaryStoryContext.parameters?.dir ?? primaryStoryContext.globals?.[DIR_ID] ?? 'ltr';
const selectedTheme = themes.find(theme => theme.id === primaryStoryContext.globals![THEME_ID]);

const hideArgsTable = Boolean(primaryStoryContext.parameters?.docs?.hideArgsTable);
const skipPrimaryStory = Boolean(primaryStoryContext.parameters?.docs?.skipPrimaryStory);

const videos = primaryStoryContext.parameters?.videos ?? null;
const styles = useStyles();
// DEBUG
Expand Down Expand Up @@ -169,27 +218,8 @@ export const FluentDocsPage = () => {
<Description />
{videos && <VideoPreviews videos={videos} />}
</div>
<hr className={styles.divider} />
<HeaderMdx as="h3" id={nameToHash(primaryStory.name)}>
{primaryStory.name}
</HeaderMdx>
<Primary />
<ArgsTable of={primaryStory.component} />
{primaryStory.argTypes.as && primaryStory.argTypes.as?.type?.name === 'enum' && (
<div className={styles.nativeProps}>
<InfoFilled className={styles.nativePropsIcon} />
<div className={styles.nativePropsMessage}>
<b>
Native props are supported <span role="presentation">🙌</span>
</b>
<span>
All HTML attributes native to the {getNativeElementsList(primaryStory.argTypes.as.type.value)},
including all <code>aria-*</code> and <code>data-*</code> attributes, can be applied as native props
on this component.
</span>
</div>
</div>
)}
<RenderPrimaryStory primaryStory={primaryStory} skipPrimaryStory={skipPrimaryStory} />
<RenderArgsTable primaryStory={primaryStory} hideArgsTable={hideArgsTable} />
<Stories />
</div>
<div className={styles.toc}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "docs: add link to motion docs page",
"packageName": "@fluentui/react-dialog",
"email": "viktorgenaev@microsoft.com",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "docs: add link to motion docs page",
"packageName": "@fluentui/react-drawer",
"email": "viktorgenaev@microsoft.com",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import type { DialogContextValue, DialogSurfaceContextValue } from '../../contex
import type { DialogSurfaceElement } from '../DialogSurface/DialogSurface.types';

export type DialogSlots = {
/**
* For more information refer to the [Motion docs page](https://react.fluentui.dev/?path=/docs/motion-motion-slot--docs).
*
*/
surfaceMotion: Slot<PresenceMotionSlotProps>;
};

export type InternalDialogSlots = {
// motion slots cannot be nullable
surfaceMotion: NonNullable<Slot<PresenceMotionSlotProps>>;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ export type DialogSurfaceSlots = {
*/
backdrop?: Slot<'div'>;
root: Slot<'div'>;

/**
* For more information refer to the [Motion docs page](https://react.fluentui.dev/?path=/docs/motion-motion-slot--docs).
*
*/
backdropMotion: Slot<PresenceMotionSlotProps>;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@ export type OverlayDrawerSlots = {
* Slot for the root element.
*/
root: Slot<OverlayDrawerSurfaceProps>;

/**
* For more information refer to the [Motion docs page](https://react.fluentui.dev/?path=/docs/motion-motion-slot--docs).
*
*/
backdropMotion?: Slot<PresenceMotionSlotProps<OverlayDrawerSurfaceMotionParams>>;
/**
* For more information refer to the [Motion docs page](https://react.fluentui.dev/?path=/docs/motion-motion-slot--docs).
*
*/
surfaceMotion?: Slot<PresenceMotionSlotProps<DrawerMotionParams>>;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createMotionComponent, makeStyles, tokens } from '@fluentui/react-components';
import { createMotionComponent, MotionComponentProps, makeStyles, tokens } from '@fluentui/react-components';
import * as React from 'react';

import description from './CreateMotionComponent.stories.md';
Expand Down Expand Up @@ -47,7 +47,7 @@ const DropIn = createMotionComponent({
iterations: Infinity,
});

export const CreateMotionComponent = () => {
export const CreateMotionComponent = (props: MotionComponentProps) => {
const classes = useClasses();

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Meta } from '@storybook/react';
import CreateMotionComponentDescription from './CreateMotionComponentDescription.md';
import { CreateMotionComponent } from './CreateMotionComponent.stories';

export { CreateMotionComponentDefault as Default } from './CreateMotionComponentDefault.stories';

Expand All @@ -15,6 +16,7 @@ export { MotionFunctionParams as functionParams } from './MotionFunctionParams.s

export default {
title: 'Motion/APIs/createMotionComponent',
component: CreateMotionComponent,
parameters: {
docs: {
description: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Field,
makeStyles,
type MotionImperativeRef,
PresenceComponentProps,
tokens,
Switch,
} from '@fluentui/react-components';
Expand Down Expand Up @@ -67,7 +68,7 @@ const DropIn = createPresenceComponent({
},
});

export const CreatePresenceComponent = () => {
export const CreatePresenceComponent = (props: PresenceComponentProps) => {
const classes = useClasses();

const motionRef = React.useRef<MotionImperativeRef>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Meta } from '@storybook/react';
import CreatePresenceComponentDescription from './CreatePresenceComponentDescription.md';
import { CreatePresenceComponent } from './CreatePresenceComponent.stories';

export { CreatePresenceComponentDefault as Default } from './CreatePresenceComponentDefault.stories';

Expand All @@ -15,6 +16,7 @@ export { PresenceMotionFunctionParams as functionParams } from './PresenceMotion

export default {
title: 'Motion/APIs/createPresenceComponent',
component: CreatePresenceComponent,
parameters: {
docs: {
description: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import * as React from 'react';
import {
Dialog,
tokens,
DialogTrigger,
DialogSurface,
makeStyles,
DialogTitle,
DialogBody,
DialogActions,
DialogContent,
Button,
Link,
createPresenceComponent,
motionTokens,
} from '@fluentui/react-components';

const useStyles = makeStyles({
wrapper: {
display: 'flex',
gap: '20px',
},
});

const SlideDialogMotion = createPresenceComponent(() => {
const keyframes = [
{ opacity: 0, transform: 'translateX(-100%)', boxShadow: '0px 0px 0px 0px rgba(0, 0, 0, 0.1)' },
{ opacity: 1, transform: 'translateX(0)', boxShadow: tokens.shadow64 },
];

return {
enter: {
keyframes,
easing: motionTokens.curveDecelerateMax,
duration: motionTokens.durationGentle,
},
exit: {
keyframes: [...keyframes].reverse(),
easing: motionTokens.curveAccelerateMid,
duration: motionTokens.durationGentle,
},
};
});

const RadialBackdropMotion = createPresenceComponent(() => {
const keyframes = [
{
opacity: 0,
background: 'radial-gradient(circle at center, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 100%)',
transform: 'scale(0)',
},
{
opacity: 1,
background: 'radial-gradient(circle at center, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 100%)',
transform: 'scale(1)',
},
];

return {
enter: {
keyframes: keyframes,
easing: motionTokens.curveDecelerateMax,
duration: motionTokens.durationGentle,
},
exit: {
keyframes: [...keyframes].reverse(),
easing: motionTokens.curveAccelerateMax,
duration: motionTokens.durationGentle,
},
};
});

export const CustomMotion = () => {
const classes = useStyles();

return (
<div className={classes.wrapper}>
<Dialog
surfaceMotion={{
children: (_, props) => <SlideDialogMotion {...props}>{props.children}</SlideDialogMotion>,
}}
>
<DialogTrigger disableButtonEnhancement>
<Button>Custom surface motion</Button>
</DialogTrigger>
<DialogSurface>
<DialogBody>
<DialogTitle>Custom surface motion</DialogTitle>
<DialogContent>
If you need to use a custom animation, you can use a render function inside <b>surfaceMotion</b> slot and
apply your custom animation built with{' '}
<Link href="https://react.fluentui.dev/?path=/docs/motion-apis-createpresencecomponent--docs">
createPresenceComponent
</Link>
</DialogContent>
<DialogActions>
<DialogTrigger disableButtonEnhancement>
<Button appearance="secondary">Close</Button>
</DialogTrigger>
<Button appearance="primary">Do Something</Button>
</DialogActions>
</DialogBody>
</DialogSurface>
</Dialog>
<Dialog>
<DialogTrigger disableButtonEnhancement>
<Button>Custom backdrop</Button>
</DialogTrigger>
<DialogSurface
backdropMotion={{
children: (_, props) => <RadialBackdropMotion {...props}>{props.children}</RadialBackdropMotion>,
}}
>
<DialogBody>
<DialogTitle>Custom backdrop</DialogTitle>
<DialogContent>Customized dimmed background motion</DialogContent>
<DialogActions>
<DialogTrigger disableButtonEnhancement>
<Button appearance="secondary">Close</Button>
</DialogTrigger>
<Button appearance="primary">Do Something</Button>
</DialogActions>
</DialogBody>
</DialogSurface>
</Dialog>
</div>
);
};

CustomMotion.parameters = {
docs: {
description: {
story: `If you need to use a custom animation, you can use a render function inside a slot and
apply your custom animation built with [createPresenceComponent](https://react.fluentui.dev/?path=/docs/motion-apis-createpresencecomponent--docs").`,
},
},
};
Loading