Skip to content

Commit c28f731

Browse files
authored
docs: add focus restoration to our examples, and update the docs to reflect authoring requirements (microsoft#33924)
1 parent 69a1d32 commit c28f731

13 files changed

+170
-27
lines changed

packages/react-components/react-drawer/stories/src/Drawer/DrawerBestPractices.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
### Accessibility
1212

13+
**Focus**: If the Drawer has a trigger and can be closed, use the `useRestoreFocusTarget` and `useRestoreFocusSource` hooks to handle focus restoration as shown in our examples. Additionally, the `InlineDrawer` does not take focus by default when it is opened; if this functionality is needed, it should be handled manually.
14+
1315
- `OverlayDrawer`: <br>Please refer to the Dialog component to understand the accessibility recommendations and implications.
1416
- `InlineDrawer`: <br>
1517
**Semantics**: Renders a plain div and do not imply any accessibility semantics by default. It accepts all aria attributes and it should be customized depending on its context within a page. Consider using `role="region"` for large page-level drawers. <br><br>
16-
**Focus**: If the `InlineDrawer` has a trigger and can be closed, use the `useRestoreFocusTarget` and `useRestoreFocusSource` hooks to handle focus restoration as shown in our Default and Inline examples. Additionally, the `InlineDrawer` does not take focus by default when it is opened; if this functionality is needed, it should be handled manually.

packages/react-components/react-drawer/stories/src/Drawer/DrawerCustomSize.stories.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
tokens,
1010
makeStyles,
1111
Input,
12+
useRestoreFocusSource,
13+
useRestoreFocusTarget,
1214
} from '@fluentui/react-components';
1315
import { Dismiss24Regular } from '@fluentui/react-icons';
1416

@@ -31,9 +33,15 @@ export const CustomSize = () => {
3133
const [open, setOpen] = React.useState(false);
3234
const [customSize, setCustomSize] = React.useState(600);
3335

36+
// all Drawers need manual focus restoration attributes
37+
// unless (as in the case of some inline drawers, you do not want automatic focus restoration)
38+
const restoreFocusTargetAttributes = useRestoreFocusTarget();
39+
const restoreFocusSourceAttributes = useRestoreFocusSource();
40+
3441
return (
3542
<div>
3643
<OverlayDrawer
44+
{...restoreFocusSourceAttributes}
3745
open={open}
3846
position="end"
3947
onOpenChange={(_, state) => setOpen(state.open)}
@@ -60,7 +68,7 @@ export const CustomSize = () => {
6068
</OverlayDrawer>
6169

6270
<div className={styles.main}>
63-
<Button appearance="primary" onClick={() => setOpen(true)}>
71+
<Button {...restoreFocusTargetAttributes} appearance="primary" onClick={() => setOpen(true)}>
6472
Open Drawer
6573
</Button>
6674

packages/react-components/react-drawer/stories/src/Drawer/DrawerDefault.stories.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ export const Default = () => {
5454
const [isOpen, setIsOpen] = React.useState(false);
5555
const [type, setType] = React.useState<DrawerType>('overlay');
5656

57-
// Overlay Drawer will handle focus by default, but inline Drawers need manual focus restoration attributes, if applicable
57+
// all Drawers need manual focus restoration attributes
58+
// unless (as in the case of some inline drawers, you do not want automatic focus restoration)
5859
const restoreFocusTargetAttributes = useRestoreFocusTarget();
5960
const restoreFocusSourceAttributes = useRestoreFocusSource();
6061

packages/react-components/react-drawer/stories/src/Drawer/DrawerMotionDisabled.stories.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
makeStyles,
1313
tokens,
1414
useId,
15+
useRestoreFocusSource,
16+
useRestoreFocusTarget,
1517
} from '@fluentui/react-components';
1618
import { Dismiss24Regular } from '@fluentui/react-icons';
1719

@@ -51,12 +53,18 @@ export const MotionDisabled = () => {
5153
const [isOpen, setIsOpen] = React.useState(false);
5254
const [type, setType] = React.useState<DrawerType>('overlay');
5355

56+
// all Drawers need manual focus restoration attributes
57+
// unless (as in the case of some inline drawers, you do not want automatic focus restoration)
58+
const restoreFocusTargetAttributes = useRestoreFocusTarget();
59+
const restoreFocusSourceAttributes = useRestoreFocusSource();
60+
5461
return (
5562
<div className={styles.root}>
5663
<Drawer
5764
backdropMotion={null}
5865
surfaceMotion={null}
5966
type={type}
67+
{...restoreFocusSourceAttributes}
6068
separator
6169
open={isOpen}
6270
onOpenChange={(_, { open }) => setIsOpen(open)}
@@ -82,7 +90,7 @@ export const MotionDisabled = () => {
8290
</Drawer>
8391

8492
<div className={styles.content}>
85-
<Button appearance="primary" onClick={() => setIsOpen(!isOpen)}>
93+
<Button {...restoreFocusTargetAttributes} appearance="primary" onClick={() => setIsOpen(!isOpen)}>
8694
{type === 'inline' ? 'Toggle' : 'Open'}
8795
</Button>
8896

packages/react-components/react-drawer/stories/src/Drawer/DrawerMultipleLevels.stories.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
ToolbarGroup,
1414
ToolbarButton,
1515
makeStyles,
16+
useRestoreFocusSource,
17+
useRestoreFocusTarget,
1618
} from '@fluentui/react-components';
1719
import { Dismiss24Regular, Calendar24Regular, Settings24Regular, ArrowLeft24Regular } from '@fluentui/react-icons';
1820

@@ -96,9 +98,19 @@ export const MultipleLevels = () => {
9698
const [isOpen, setIsOpen] = React.useState(false);
9799
const [level, setLevel] = React.useState<1 | 2>(1);
98100

101+
// all Drawers need manual focus restoration attributes
102+
// unless (as in the case of some inline drawers, you do not want automatic focus restoration)
103+
const restoreFocusTargetAttributes = useRestoreFocusTarget();
104+
const restoreFocusSourceAttributes = useRestoreFocusSource();
105+
99106
return (
100107
<div>
101-
<OverlayDrawer position="start" open={isOpen} onOpenChange={(_, { open }) => setIsOpen(open)}>
108+
<OverlayDrawer
109+
{...restoreFocusSourceAttributes}
110+
position="start"
111+
open={isOpen}
112+
onOpenChange={(_, { open }) => setIsOpen(open)}
113+
>
102114
<DrawerHeader>
103115
<DrawerHeaderNavigation>
104116
<Toolbar className={styles.toolbar}>
@@ -161,7 +173,7 @@ export const MultipleLevels = () => {
161173
</DrawerFooter>
162174
</OverlayDrawer>
163175

164-
<Button appearance="primary" onClick={() => setIsOpen(true)}>
176+
<Button {...restoreFocusTargetAttributes} appearance="primary" onClick={() => setIsOpen(true)}>
165177
Open Drawer
166178
</Button>
167179
</div>

packages/react-components/react-drawer/stories/src/Drawer/DrawerPosition.stories.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {
88
Button,
99
makeStyles,
1010
tokens,
11+
useRestoreFocusSource,
12+
useRestoreFocusTarget,
1113
} from '@fluentui/react-components';
1214
import { Dismiss24Regular } from '@fluentui/react-icons';
1315

@@ -43,9 +45,19 @@ export const Position = () => {
4345
setIsOpen(true);
4446
}, []);
4547

48+
// all Drawers need manual focus restoration attributes
49+
// unless (as in the case of some inline drawers, you do not want automatic focus restoration)
50+
const restoreFocusTargetAttributes = useRestoreFocusTarget();
51+
const restoreFocusSourceAttributes = useRestoreFocusSource();
52+
4653
return (
4754
<div>
48-
<OverlayDrawer position={position} open={isOpen} onOpenChange={(_, { open }) => setIsOpen(open)}>
55+
<OverlayDrawer
56+
position={position}
57+
{...restoreFocusSourceAttributes}
58+
open={isOpen}
59+
onOpenChange={(_, { open }) => setIsOpen(open)}
60+
>
4961
<DrawerHeader>
5062
<DrawerHeaderTitle
5163
action={
@@ -67,15 +79,15 @@ export const Position = () => {
6779
</OverlayDrawer>
6880

6981
<div className={styles.content}>
70-
<Button appearance="primary" onClick={onClickStartButton}>
82+
<Button {...restoreFocusTargetAttributes} appearance="primary" onClick={onClickStartButton}>
7183
Open start
7284
</Button>
7385

74-
<Button appearance="primary" onClick={onClickEndButton}>
86+
<Button {...restoreFocusTargetAttributes} appearance="primary" onClick={onClickEndButton}>
7587
Open end
7688
</Button>
7789

78-
<Button appearance="primary" onClick={onClickBottomButton}>
90+
<Button {...restoreFocusTargetAttributes} appearance="primary" onClick={onClickBottomButton}>
7991
Open Bottom
8092
</Button>
8193
</div>

packages/react-components/react-drawer/stories/src/Drawer/DrawerPreventClose.stories.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
import * as React from 'react';
2-
import { OverlayDrawer, DrawerBody, DrawerHeader, DrawerHeaderTitle, Button } from '@fluentui/react-components';
2+
import {
3+
OverlayDrawer,
4+
DrawerBody,
5+
DrawerHeader,
6+
DrawerHeaderTitle,
7+
Button,
8+
useRestoreFocusSource,
9+
useRestoreFocusTarget,
10+
} from '@fluentui/react-components';
311
import { Dismiss24Regular } from '@fluentui/react-icons';
412

513
export const PreventClose = () => {
614
const [open, setOpen] = React.useState(false);
715

16+
// all Drawers need manual focus restoration attributes
17+
// unless (as in the case of some inline drawers, you do not want automatic focus restoration)
18+
const restoreFocusTargetAttributes = useRestoreFocusTarget();
19+
const restoreFocusSourceAttributes = useRestoreFocusSource();
20+
821
return (
922
<div>
10-
<OverlayDrawer position="end" open={open} modalType="alert">
23+
<OverlayDrawer {...restoreFocusSourceAttributes} position="end" open={open} modalType="alert">
1124
<DrawerHeader>
1225
<DrawerHeaderTitle
1326
action={
@@ -28,7 +41,7 @@ export const PreventClose = () => {
2841
</DrawerBody>
2942
</OverlayDrawer>
3043

31-
<Button appearance="primary" onClick={() => setOpen(true)}>
44+
<Button {...restoreFocusTargetAttributes} appearance="primary" onClick={() => setOpen(true)}>
3245
Open Drawer
3346
</Button>
3447
</div>

packages/react-components/react-drawer/stories/src/Drawer/DrawerResponsive.stories.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {
88
Button,
99
makeStyles,
1010
tokens,
11+
useRestoreFocusSource,
12+
useRestoreFocusTarget,
1113
} from '@fluentui/react-components';
1214
import { Dismiss24Regular } from '@fluentui/react-icons';
1315

@@ -51,9 +53,21 @@ export const Responsive = () => {
5153
return () => match.removeEventListener('change', onMediaQueryChange);
5254
}, [onMediaQueryChange]);
5355

56+
// all Drawers need manual focus restoration attributes
57+
// unless (as in the case of some inline drawers, you do not want automatic focus restoration)
58+
const restoreFocusTargetAttributes = useRestoreFocusTarget();
59+
const restoreFocusSourceAttributes = useRestoreFocusSource();
60+
5461
return (
5562
<div className={styles.root}>
56-
<Drawer type={type} separator position="start" open={isOpen} onOpenChange={(_, { open }) => setIsOpen(open)}>
63+
<Drawer
64+
type={type}
65+
{...restoreFocusSourceAttributes}
66+
separator
67+
position="start"
68+
open={isOpen}
69+
onOpenChange={(_, { open }) => setIsOpen(open)}
70+
>
5771
<DrawerHeader>
5872
<DrawerHeaderTitle
5973
action={
@@ -75,7 +89,7 @@ export const Responsive = () => {
7589
</Drawer>
7690

7791
<div className={styles.content}>
78-
<Button appearance="primary" onClick={() => setIsOpen(!isOpen)}>
92+
<Button {...restoreFocusTargetAttributes} appearance="primary" onClick={() => setIsOpen(!isOpen)}>
7993
Toggle
8094
</Button>
8195

packages/react-components/react-drawer/stories/src/Drawer/DrawerSize.stories.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
useId,
1313
tokens,
1414
makeStyles,
15+
useRestoreFocusSource,
16+
useRestoreFocusTarget,
1517
} from '@fluentui/react-components';
1618
import { Dismiss24Regular } from '@fluentui/react-icons';
1719

@@ -44,9 +46,20 @@ export const Size = () => {
4446
full: 'Full',
4547
};
4648

49+
// all Drawers need manual focus restoration attributes
50+
// unless (as in the case of some inline drawers, you do not want automatic focus restoration)
51+
const restoreFocusTargetAttributes = useRestoreFocusTarget();
52+
const restoreFocusSourceAttributes = useRestoreFocusSource();
53+
4754
return (
4855
<div>
49-
<OverlayDrawer size={size} position="end" open={open} onOpenChange={(_, state) => setOpen(state.open)}>
56+
<OverlayDrawer
57+
size={size}
58+
{...restoreFocusSourceAttributes}
59+
position="end"
60+
open={open}
61+
onOpenChange={(_, state) => setOpen(state.open)}
62+
>
5063
<DrawerHeader>
5164
<DrawerHeaderTitle
5265
action={
@@ -68,7 +81,7 @@ export const Size = () => {
6881
</OverlayDrawer>
6982

7083
<div className={styles.main}>
71-
<Button appearance="primary" onClick={() => setOpen(true)}>
84+
<Button {...restoreFocusTargetAttributes} appearance="primary" onClick={() => setOpen(true)}>
7285
Open Drawer
7386
</Button>
7487

packages/react-components/react-drawer/stories/src/Drawer/DrawerWithNavigation.stories.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
ToolbarGroup,
1111
ToolbarButton,
1212
makeStyles,
13+
useRestoreFocusSource,
14+
useRestoreFocusTarget,
1315
} from '@fluentui/react-components';
1416
import {
1517
Dismiss24Regular,
@@ -30,9 +32,19 @@ export const WithNavigation = () => {
3032

3133
const [isOpen, setIsOpen] = React.useState(false);
3234

35+
// all Drawers need manual focus restoration attributes
36+
// unless (as in the case of some inline drawers, you do not want automatic focus restoration)
37+
const restoreFocusTargetAttributes = useRestoreFocusTarget();
38+
const restoreFocusSourceAttributes = useRestoreFocusSource();
39+
3340
return (
3441
<div>
35-
<OverlayDrawer position="start" open={isOpen} onOpenChange={(_, { open }) => setIsOpen(open)}>
42+
<OverlayDrawer
43+
position="start"
44+
{...restoreFocusSourceAttributes}
45+
open={isOpen}
46+
onOpenChange={(_, { open }) => setIsOpen(open)}
47+
>
3648
<DrawerHeader>
3749
<DrawerHeaderNavigation className={styles.header}>
3850
<Button aria-label="Back" appearance="subtle" icon={<ArrowLeft24Regular />} />
@@ -58,7 +70,7 @@ export const WithNavigation = () => {
5870
</DrawerBody>
5971
</OverlayDrawer>
6072

61-
<Button appearance="primary" onClick={() => setIsOpen(true)}>
73+
<Button {...restoreFocusTargetAttributes} appearance="primary" onClick={() => setIsOpen(true)}>
6274
Open Drawer
6375
</Button>
6476
</div>

0 commit comments

Comments
 (0)