forked from microsoft/fluentui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: use tabster restorer API (microsoft#28530)
* feat: use tabster restorer API * custom restorer hook * update patch * update snapshots * update patch * fix enums * update patch * bump tabster * update md * add docs * changefiles * add stories prefix * remove changes * update documentation example * rename useMenuTrigger * pr fixes * update docs * add popover doc example
- Loading branch information
Showing
37 changed files
with
483 additions
and
60 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-components-5b15aaa9-20f7-44b3-a369-8096b4cdf7eb.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "minor", | ||
"comment": "feat: Export useRestoreFocusTarget and useRestoreFocusSource", | ||
"packageName": "@fluentui/react-components", | ||
"email": "lingfan.gao@microsoft.com", | ||
"dependentChangeType": "patch" | ||
} |
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-dialog-6ac1e0ff-b5c6-4471-836a-49470b24f8c3.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "patch", | ||
"comment": "fix: Focus should restore to a DialogTrigger outside of a Dialog", | ||
"packageName": "@fluentui/react-dialog", | ||
"email": "lingfan.gao@microsoft.com", | ||
"dependentChangeType": "patch" | ||
} |
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-menu-fdccbc10-8c41-442c-bac0-f034e5e3c266.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "patch", | ||
"comment": "refactor: Remove custom focus code in favour of useRestoreFocus hooks", | ||
"packageName": "@fluentui/react-menu", | ||
"email": "lingfan.gao@microsoft.com", | ||
"dependentChangeType": "patch" | ||
} |
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-tabster-c0ff6814-1ae9-4231-a304-d985128bdc9b.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "minor", | ||
"comment": "feat: Implement useRestoreFocusSource and useRestoreFocusTarget based on the tabster restorer API", | ||
"packageName": "@fluentui/react-tabster", | ||
"email": "lingfan.gao@microsoft.com", | ||
"dependentChangeType": "patch" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
...onents/stories/Concepts/FocusManagement/useRestoreFocusSource/Default.stories.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import * as React from 'react'; | ||
import { ThumbLikeRegular, ThumbDislikeRegular } from '@fluentui/react-icons'; | ||
import { | ||
useRestoreFocusSource, | ||
useRestoreFocusTarget, | ||
Button, | ||
makeStyles, | ||
Textarea, | ||
Field, | ||
} from '@fluentui/react-components'; | ||
|
||
const useStyles = makeStyles({ | ||
feedback: { | ||
display: 'flex', | ||
alignItems: 'center', | ||
}, | ||
|
||
field: { | ||
width: '300px', | ||
}, | ||
}); | ||
|
||
export const Default = () => { | ||
const styles = useStyles(); | ||
const restoreFocusSourceAttribute = useRestoreFocusSource(); | ||
const restoreFocusTargetAttribute = useRestoreFocusTarget(); | ||
const [feedbackSent, setFeedbackSent] = React.useState(false); | ||
|
||
React.useEffect(() => { | ||
if (feedbackSent) { | ||
const timeout = setTimeout(() => setFeedbackSent(false), 5000); | ||
return () => clearTimeout(timeout); | ||
} | ||
}, [feedbackSent]); | ||
|
||
return ( | ||
<div> | ||
<Field label="Compose message" className={styles.field}> | ||
<Textarea /> | ||
</Field> | ||
<br /> | ||
<Button {...restoreFocusTargetAttribute}>Send message</Button> | ||
{!feedbackSent ? ( | ||
<div {...restoreFocusSourceAttribute} className={styles.feedback}> | ||
How was your experience completing this task? | ||
<Button appearance="subtle" onClick={() => setFeedbackSent(true)} icon={<ThumbLikeRegular />} /> | ||
<Button appearance="subtle" onClick={() => setFeedbackSent(true)} icon={<ThumbDislikeRegular />} /> | ||
</div> | ||
) : ( | ||
<div>Thanks for submitting feedback!</div> | ||
)} | ||
</div> | ||
); | ||
}; |
99 changes: 99 additions & 0 deletions
99
...ts/stories/Concepts/FocusManagement/useRestoreFocusSource/FocusRestoreHistory.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import * as React from 'react'; | ||
import { ThumbLikeRegular, ThumbDislikeRegular } from '@fluentui/react-icons'; | ||
import { | ||
useRestoreFocusSource, | ||
useRestoreFocusTarget, | ||
Button, | ||
makeStyles, | ||
Textarea, | ||
Field, | ||
} from '@fluentui/react-components'; | ||
|
||
const useStyles = makeStyles({ | ||
feedback: { | ||
display: 'flex', | ||
alignItems: 'center', | ||
}, | ||
|
||
field: { | ||
width: '300px', | ||
}, | ||
}); | ||
|
||
export const FocusRestoreHistory = () => { | ||
const styles = useStyles(); | ||
const restoreFocusSourceAttribute = useRestoreFocusSource(); | ||
const restoreFocusTargetAttribute = useRestoreFocusTarget(); | ||
const [experienceFeedbackSent, setExperienceFeedbackSent] = React.useState(false); | ||
const [deliveryFeedbackSent, setDeliveryFeedbackSent] = React.useState(false); | ||
|
||
React.useEffect(() => { | ||
// reset example | ||
if (experienceFeedbackSent) { | ||
const timeout = setTimeout(() => setExperienceFeedbackSent(false), 5000); | ||
return () => clearTimeout(timeout); | ||
} | ||
}, [experienceFeedbackSent]); | ||
|
||
React.useEffect(() => { | ||
// reset example | ||
if (deliveryFeedbackSent) { | ||
const timeout = setTimeout(() => setDeliveryFeedbackSent(false), 5000); | ||
return () => clearTimeout(timeout); | ||
} | ||
}, [deliveryFeedbackSent]); | ||
|
||
return ( | ||
<div> | ||
<Field label="Compose message" className={styles.field}> | ||
<Textarea /> | ||
</Field> | ||
<br /> | ||
<Button {...restoreFocusTargetAttribute}>Send message</Button> | ||
{!experienceFeedbackSent ? ( | ||
<> | ||
<div {...restoreFocusSourceAttribute} className={styles.feedback}> | ||
How was your experience completing this task? | ||
<Button | ||
{...restoreFocusTargetAttribute} | ||
appearance="subtle" | ||
onClick={() => setExperienceFeedbackSent(true)} | ||
icon={<ThumbLikeRegular />} | ||
/> | ||
<Button | ||
appearance="subtle" | ||
onClick={() => setExperienceFeedbackSent(true)} | ||
icon={<ThumbDislikeRegular />} | ||
/> | ||
</div> | ||
</> | ||
) : ( | ||
<div>Thanks for submitting feedback!</div> | ||
)} | ||
{!deliveryFeedbackSent ? ( | ||
<> | ||
<div {...restoreFocusSourceAttribute} className={styles.feedback}> | ||
Was your message delivered successfully? | ||
<Button appearance="subtle" onClick={() => setDeliveryFeedbackSent(true)} icon={<ThumbLikeRegular />} /> | ||
<Button appearance="subtle" onClick={() => setDeliveryFeedbackSent(true)} icon={<ThumbDislikeRegular />} /> | ||
</div> | ||
</> | ||
) : ( | ||
<div>Thanks for submitting feedback!</div> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
FocusRestoreHistory.parameters = { | ||
docs: { | ||
description: { | ||
story: [ | ||
'Target elements are stored in a limited history. In this example try to submit the feedback in reverse order.', | ||
'The first feedback button is a restore target, so once the second feedback is submitted focus is restored', | ||
'to the first feedback button. Likewise once the first feedback is submitted, focus will be restored to the', | ||
'send button.', | ||
].join('\n'), | ||
}, | ||
}, | ||
}; |
105 changes: 105 additions & 0 deletions
105
...ories/Concepts/FocusManagement/useRestoreFocusSource/UserRestoreFocus.stories.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import * as React from 'react'; | ||
import { ThumbLikeRegular, ThumbDislikeRegular } from '@fluentui/react-icons'; | ||
import { | ||
useRestoreFocusSource, | ||
useRestoreFocusTarget, | ||
Button, | ||
makeStyles, | ||
Textarea, | ||
Field, | ||
} from '@fluentui/react-components'; | ||
|
||
const useStyles = makeStyles({ | ||
feedback: { | ||
display: 'flex', | ||
alignItems: 'center', | ||
}, | ||
|
||
field: { | ||
width: '300px', | ||
}, | ||
}); | ||
|
||
export const UserRestoreFocus = () => { | ||
const styles = useStyles(); | ||
const restoreFocusSourceAttribute = useRestoreFocusSource(); | ||
const restoreFocusTargetAttribute = useRestoreFocusTarget(); | ||
const sendButtonRef = React.useRef<HTMLButtonElement | null>(null); | ||
const [experienceFeedbackSent, setExperienceFeedbackSent] = React.useState(false); | ||
const [deliveryFeedbackSent, setDeliveryFeedbackSent] = React.useState(false); | ||
|
||
React.useEffect(() => { | ||
// reset example | ||
if (experienceFeedbackSent) { | ||
const timeout = setTimeout(() => setExperienceFeedbackSent(false), 5000); | ||
return () => clearTimeout(timeout); | ||
} | ||
}, [experienceFeedbackSent]); | ||
|
||
React.useEffect(() => { | ||
// reset example | ||
if (deliveryFeedbackSent) { | ||
sendButtonRef.current?.focus(); | ||
const timeout = setTimeout(() => setDeliveryFeedbackSent(false), 5000); | ||
return () => clearTimeout(timeout); | ||
} | ||
}, [deliveryFeedbackSent]); | ||
|
||
return ( | ||
<div> | ||
<Field label="Compose message" className={styles.field}> | ||
<Textarea /> | ||
</Field> | ||
<br /> | ||
<Button ref={sendButtonRef} {...restoreFocusTargetAttribute}> | ||
Send message | ||
</Button> | ||
{!experienceFeedbackSent ? ( | ||
<> | ||
<div {...restoreFocusSourceAttribute} className={styles.feedback}> | ||
How was your experience completing this task? | ||
<Button | ||
{...restoreFocusTargetAttribute} | ||
appearance="subtle" | ||
onClick={() => setExperienceFeedbackSent(true)} | ||
icon={<ThumbLikeRegular />} | ||
/> | ||
<Button | ||
appearance="subtle" | ||
onClick={() => setExperienceFeedbackSent(true)} | ||
icon={<ThumbDislikeRegular />} | ||
/> | ||
</div> | ||
</> | ||
) : ( | ||
<div>Thanks for submitting feedback!</div> | ||
)} | ||
{!deliveryFeedbackSent ? ( | ||
<> | ||
<div {...restoreFocusSourceAttribute} className={styles.feedback}> | ||
Was your message delivered successfully? | ||
<Button appearance="subtle" onClick={() => setDeliveryFeedbackSent(true)} icon={<ThumbLikeRegular />} /> | ||
<Button appearance="subtle" onClick={() => setDeliveryFeedbackSent(true)} icon={<ThumbDislikeRegular />} /> | ||
</div> | ||
</> | ||
) : ( | ||
<div>Thanks for submitting feedback!</div> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
UserRestoreFocus.parameters = { | ||
docs: { | ||
description: { | ||
story: [ | ||
'If the user manually moves focus to a desired element, then the utility **will not move focus**.', | ||
'The focus will only be restored if it is lost to the `document body`.', | ||
'', | ||
'This example is similar to the previous. However, submitting the second feedback will manually move', | ||
"focus to the 'Send message' button. This bypasses the restore focus history, which should restore", | ||
'focus to the first feedback button.', | ||
].join('\n'), | ||
}, | ||
}, | ||
}; |
18 changes: 18 additions & 0 deletions
18
...react-components/stories/Concepts/FocusManagement/useRestoreFocusSource/index.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { useRestoreFocusSource } from '@fluentui/react-components'; | ||
import descriptionMd from './useRestoreFocusSourceDescription.md'; | ||
|
||
export { Default } from './Default.stories.stories'; | ||
export { FocusRestoreHistory } from './FocusRestoreHistory.stories'; | ||
export { UserRestoreFocus } from './UserRestoreFocus.stories.stories'; | ||
|
||
export default { | ||
title: 'Utilities/Focus Management/useRestoreFocusSource', | ||
component: useRestoreFocusSource, | ||
parameters: { | ||
docs: { | ||
description: { | ||
component: [descriptionMd].join('\n'), | ||
}, | ||
}, | ||
}, | ||
}; |
10 changes: 10 additions & 0 deletions
10
...cepts/FocusManagement/useRestoreFocusSource/useRestoreFocusSourceDescription.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
The hooks `useRestoreFocusSource` and `useRestoreFocusTarget` are intended to be used together, but without tight | ||
coupling. | ||
|
||
When the attribute returned by `useRestoreFocusSource` is applied to an element, it will be ready to restore focus | ||
to the last 'bookmarked' element that was set using `useRestoreFocusTarget`. The restore focus target | ||
**needs to be focused** before focus is lost from a source. This is to prevent focus randomly jumping across | ||
an application but being restored to the an element at the closest point in time. | ||
|
||
The examples below simulate a feedback experience. One a user submits feedback, the control will be removed from | ||
the page and the focus will need to revert from the body (since the focused element was removed). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.