Skip to content

Commit 80d288f

Browse files
authored
docs: Add usable documentation (#28155)
* docs: Add usable documentation Updates all the documentation examples for Toast so that they are human readable. Also adds docstring to public API * update api md file * Add unstable notice * props table, better offset story * hack to add toast stories to storybook * remove console.log * remove hack * fix toaster limit
1 parent 6aa34e2 commit 80d288f

21 files changed

Lines changed: 456 additions & 86 deletions

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ export type ToastId = string;
101101
// @public (undocumented)
102102
export type ToastOffset = Partial<Record<ToastPosition, ToastOffsetObject>> | ToastOffsetObject;
103103

104+
// @public (undocumented)
105+
export type ToastPoliteness = 'assertive' | 'polite';
106+
104107
// @public (undocumented)
105108
export type ToastPosition = 'top-end' | 'top-start' | 'bottom-end' | 'bottom-start';
106109

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export { useToastController } from './state';
2-
export type { ToastPosition, ToastId, ToastOffset } from './state';
2+
export type { ToastPosition, ToastId, ToastOffset, ToastPoliteness } from './state';
33

44
export { ToastTrigger } from './ToastTrigger';
55
export type { ToastTriggerChildProps, ToastTriggerProps, ToastTriggerState } from './ToastTrigger';

packages/react-components/react-toast/src/state/types.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,51 @@ export type ToastId = string;
55
export type ToasterId = string;
66

77
export type ToastPosition = 'top-end' | 'top-start' | 'bottom-end' | 'bottom-start';
8+
export type ToastPoliteness = 'assertive' | 'polite';
89

910
export interface ToastOptions<TData = object> {
11+
/**
12+
* Uniquely identifies a toast, used for update and dismiss operations
13+
*/
1014
toastId: ToastId;
15+
/**
16+
* The position the toast should render to
17+
*/
1118
position: ToastPosition;
19+
/**
20+
* Toast content
21+
*/
1222
content: unknown;
23+
/**
24+
* Auto dismiss timeout in milliseconds
25+
* @default 3000
26+
*/
1327
timeout: number;
28+
/**
29+
* Toast timeout pauses while focus is on another window
30+
* @default false
31+
*/
1432
pauseOnWindowBlur: boolean;
33+
/**
34+
* Toast timeout pauses while user cursor is on the toast
35+
* @default false
36+
*/
1537
pauseOnHover: boolean;
38+
/**
39+
* Toast belongs to a specific toaster
40+
*/
1641
toasterId: ToasterId | undefined;
42+
/**
43+
* Higher priority toasts will be rendered before lower priority toasts
44+
*/
1745
priority: number;
18-
politeness: 'assertive' | 'polite';
46+
/**
47+
* Used to determine [aria-live](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions) narration
48+
*/
49+
politeness: ToastPoliteness;
50+
/**
51+
* Additional data that needs to be passed to the toast
52+
*/
1953
data: TData;
2054
}
2155

@@ -34,9 +68,21 @@ export interface ToasterOptions
3468
}
3569

3670
export interface Toast<TData = object> extends ToastOptions<TData> {
71+
/**
72+
* Determines the visiblity of a toast
73+
*/
3774
close: () => void;
75+
/**
76+
* Removes a toast completely
77+
*/
3878
remove: () => void;
79+
/**
80+
* A number used to track updates immutably
81+
*/
3982
updateId: number;
83+
/**
84+
* Used to determine default priority when the user does not set one
85+
*/
4086
dispatchedAt: number;
4187
}
4288

packages/react-components/react-toast/src/state/useToastController.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import { DispatchToastOptions, ToastId, ToasterId, UpdateToastOptions } from './
1010

1111
const noop = () => undefined;
1212

13+
/**
14+
* @param toasterId - If an id is provided all imperative methods control that specific toaster
15+
* @returns Imperative methods to control toasts
16+
*/
1317
export function useToastController(toasterId?: ToasterId) {
1418
const { targetDocument } = useFluent();
1519

packages/react-components/react-toast/src/state/vanilla/toaster.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ export class Toaster {
160160

161161
const remove = () => {
162162
this.toasts.delete(toastId);
163-
if (this.queue.peek()) {
163+
if (this.visibleToasts.size < this.limit && this.queue.peek()) {
164164
const nextToast = this.queue.dequeue();
165165
this.toasts.set(nextToast.toastId, nextToast);
166166
this.visibleToasts.add(nextToast.toastId);

packages/react-components/react-toast/stories/Toast/CustomAnnounce.stories.tsx

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
2-
import { Toaster, useToastController, Toast, ToastTitle, ToasterProps } from '@fluentui/react-toast';
3-
import { useId, Link, makeStyles, shorthands } from '@fluentui/react-components';
2+
import { Toaster, useToastController, Toast, ToastTitle, ToasterProps, ToastPoliteness } from '@fluentui/react-toast';
3+
import { useId, makeStyles, shorthands, Button, Field, RadioGroup, Radio } from '@fluentui/react-components';
44

55
const useStyles = makeStyles({
66
visuallyHidden: {
@@ -20,26 +20,17 @@ export const CustomAnnounce = () => {
2020
const styles = useStyles();
2121
const [alert, setAlert] = React.useState('');
2222
const [status, setStatus] = React.useState('');
23+
const [politeness, setPoliteness] = React.useState<ToastPoliteness>('polite');
2324
const toasterId = useId('toaster');
2425
const { dispatchToast } = useToastController(toasterId);
25-
const dispatchAlert = () =>
26+
const notify = () =>
2627
dispatchToast(
2728
<Toast>
28-
<ToastTitle intent="success" action={<Link>Undo</Link>}>
29-
Assertive toast {counter++}
29+
<ToastTitle intent="success">
30+
{politeness === 'polite' ? 'Polite' : 'Assertive'} toast {counter++}
3031
</ToastTitle>
3132
</Toast>,
32-
{ politeness: 'assertive' },
33-
);
34-
35-
const dispatchStatus = () =>
36-
dispatchToast(
37-
<Toast>
38-
<ToastTitle intent="success" action={<Link>Undo</Link>}>
39-
Polite toast {counter++}
40-
</ToastTitle>
41-
</Toast>,
42-
{ politeness: 'polite' },
33+
{ politeness },
4334
);
4435

4536
const announce: ToasterProps['announce'] = (msg, options) => {
@@ -54,9 +45,27 @@ export const CustomAnnounce = () => {
5445
<div role="alert" className={styles.visuallyHidden}>
5546
{alert}
5647
</div>
48+
<Field label="Toast politeness">
49+
<RadioGroup value={politeness} onChange={(e, data) => setPoliteness(data.value as ToastPoliteness)}>
50+
<Radio label="Polite" value="polite" />
51+
<Radio label="Assertive" value="assertive" />
52+
</RadioGroup>
53+
</Field>
54+
<br />
5755
<Toaster announce={announce} toasterId={toasterId} />
58-
<button onClick={dispatchAlert}>Dispatch assertive</button>
59-
<button onClick={dispatchStatus}>Dispatch polite</button>
56+
<Button onClick={notify}>Make toast</Button>
6057
</>
6158
);
6259
};
60+
61+
CustomAnnounce.parameters = {
62+
docs: {
63+
description: {
64+
story: [
65+
'The `Toaster` manages an [aria-live region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions)',
66+
"internally so that toasts are announced to screen readers on render. It's possible to opt-out of this default",
67+
'behaviour by providing a custom `announce` callback.',
68+
].join('\n'),
69+
},
70+
},
71+
};
Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,59 @@
11
import * as React from 'react';
2-
import { Toaster, useToastController, ToastTitle, Toast } from '@fluentui/react-toast';
3-
import { useId } from '@fluentui/react-components';
2+
import { Toaster, useToastController, ToastTitle, Toast, ToastTrigger } from '@fluentui/react-toast';
3+
import { useId, Button, Link, SpinButton, Field } from '@fluentui/react-components';
44

55
export const CustomTimeout = () => {
6+
const [timeout, setDismissTimeout] = React.useState(1000);
67
const toasterId = useId('toaster');
78
const { dispatchToast } = useToastController(toasterId);
89
const notify = () =>
910
dispatchToast(
1011
<Toast>
11-
<ToastTitle intent="info">Custom timeout 1000ms</ToastTitle>
12+
<ToastTitle
13+
action={
14+
<ToastTrigger>
15+
<Link>Dismiss</Link>
16+
</ToastTrigger>
17+
}
18+
intent="info"
19+
>
20+
{timeout >= 0 ? `Custom timeout ${timeout}ms` : `Dismiss manually`}
21+
</ToastTitle>
1222
</Toast>,
13-
{ timeout: 1000 },
23+
{ timeout },
1424
);
1525

1626
return (
1727
<>
28+
<Field label="Timeout" hint="Timeout is in milliseconds">
29+
<SpinButton
30+
value={timeout}
31+
onChange={(e, data) => {
32+
if (data.value) {
33+
setDismissTimeout(data.value);
34+
} else if (data.displayValue !== undefined) {
35+
const newValue = parseFloat(data.displayValue);
36+
if (!Number.isNaN(newValue)) {
37+
setDismissTimeout(newValue);
38+
}
39+
}
40+
}}
41+
/>
42+
</Field>
43+
<br />
1844
<Toaster toasterId={toasterId} />
19-
<button onClick={notify}>Make toast</button>
45+
<Button onClick={notify}>Make toast</Button>
2046
</>
2147
);
2248
};
49+
50+
CustomTimeout.parameters = {
51+
docs: {
52+
description: {
53+
story: [
54+
'The timeout of toasts can be customized in milliseconds. Using a negative timeout value results in the toast',
55+
'never being auto-dismissed.',
56+
].join('\n'),
57+
},
58+
},
59+
};

packages/react-components/react-toast/stories/Toast/Default.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
22
import { Toaster, useToastController, Toast, ToastTitle, ToastBody, ToastFooter } from '@fluentui/react-toast';
3-
import { useId, Link } from '@fluentui/react-components';
3+
import { useId, Link, Button } from '@fluentui/react-components';
44

55
export const Default = () => {
66
const toasterId = useId('toaster');
@@ -22,7 +22,7 @@ export const Default = () => {
2222
return (
2323
<>
2424
<Toaster toasterId={toasterId} />
25-
<button onClick={notify}>Make toast</button>
25+
<Button onClick={notify}>Make toast</Button>
2626
</>
2727
);
2828
};

packages/react-components/react-toast/stories/Toast/DefaultToastOptions.stories.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
22
import { Toaster, useToastController, ToastTitle, Toast } from '@fluentui/react-toast';
3-
import { useId } from '@fluentui/react-components';
3+
import { useId, Button } from '@fluentui/react-components';
44

55
export const DefaultToastOptions = () => {
66
const toasterId = useId('toaster');
@@ -15,7 +15,18 @@ export const DefaultToastOptions = () => {
1515
return (
1616
<>
1717
<Toaster toasterId={toasterId} position="top-end" pauseOnHover pauseOnWindowBlur timeout={1000} />
18-
<button onClick={notify}>Make toast</button>
18+
<Button onClick={notify}>Make toast</Button>
1919
</>
2020
);
2121
};
22+
23+
DefaultToastOptions.parameters = {
24+
docs: {
25+
description: {
26+
story: [
27+
'Default options for all toasts can be configured on the `Toaster`.',
28+
'These options are only defaults and can be overriden using `dispatchToast',
29+
].join('\n'),
30+
},
31+
},
32+
};

packages/react-components/react-toast/stories/Toast/DismissAll.stories.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
22
import { Toaster, useToastController, ToastTitle, Toast } from '@fluentui/react-toast';
3-
import { useId } from '@fluentui/react-components';
3+
import { useId, Button } from '@fluentui/react-components';
44

55
export const DismissAll = () => {
66
const toasterId = useId('toaster');
@@ -16,8 +16,16 @@ export const DismissAll = () => {
1616
return (
1717
<>
1818
<Toaster toasterId={toasterId} />
19-
<button onClick={notify}>Make toast</button>
20-
<button onClick={dismissAll}>Dismiss all</button>
19+
<Button onClick={notify}>Make toast</Button>
20+
<Button onClick={dismissAll}>Dismiss all toasts</Button>
2121
</>
2222
);
2323
};
24+
25+
DismissAll.parameters = {
26+
docs: {
27+
description: {
28+
story: ['The `dismissAllToasts imperative API will dismiss all rendered Toasts.'].join('\n'),
29+
},
30+
},
31+
};

0 commit comments

Comments
 (0)