From e851bd1fcb48bcf547db655b0dbd11244bf76070 Mon Sep 17 00:00:00 2001 From: Lingfan Gao Date: Wed, 3 Aug 2022 11:36:38 +0200 Subject: [PATCH 1/9] feat: use browser `:focus-visible` if supported --- .../src/focus/createCustomFocusIndicatorStyle.ts | 2 ++ .../react-tabster/src/focus/focusVisiblePolyfill.ts | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-tabster/src/focus/createCustomFocusIndicatorStyle.ts b/packages/react-components/react-tabster/src/focus/createCustomFocusIndicatorStyle.ts index 31c1e4564f2bd..86dbf3c3f9a4e 100644 --- a/packages/react-components/react-tabster/src/focus/createCustomFocusIndicatorStyle.ts +++ b/packages/react-components/react-tabster/src/focus/createCustomFocusIndicatorStyle.ts @@ -28,6 +28,8 @@ export const createCustomFocusIndicatorStyle = ( [`:global(.fui-FluentProvider)`]: { [`& .${FOCUS_VISIBLE_CLASS}`]: style, }, + + ':focus-visible': style, }), ...(selector === 'focus-within' && { [`:global(.fui-FluentProvider)`]: { diff --git a/packages/react-components/react-tabster/src/focus/focusVisiblePolyfill.ts b/packages/react-components/react-tabster/src/focus/focusVisiblePolyfill.ts index 28a62fa8f32f2..79f81be18991a 100644 --- a/packages/react-components/react-tabster/src/focus/focusVisiblePolyfill.ts +++ b/packages/react-components/react-tabster/src/focus/focusVisiblePolyfill.ts @@ -20,8 +20,7 @@ type HTMLElementWithFocusVisibleScope = { } & HTMLElement; export function applyFocusVisiblePolyfill(scope: HTMLElement, win: Window): () => void { - if (alreadyInScope(scope)) { - // Focus visible polyfill already applied at this scope + if (alreadyInScope(scope) || browserSupportsFocusVisible()) { return () => undefined; } @@ -103,3 +102,7 @@ function alreadyInScope(el: HTMLElement | null | undefined): boolean { return alreadyInScope(el?.parentElement); } + +function browserSupportsFocusVisible() { + return CSS.supports('selector(:focus-visible)'); +} From 56acea29542c30ccaa5631b7dad18879d8f4400d Mon Sep 17 00:00:00 2001 From: Lingfan Gao Date: Wed, 3 Aug 2022 12:12:06 +0200 Subject: [PATCH 2/9] changefile --- ...react-tabster-8d4667d2-30a7-4c68-bbcc-7b6331016905.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-tabster-8d4667d2-30a7-4c68-bbcc-7b6331016905.json diff --git a/change/@fluentui-react-tabster-8d4667d2-30a7-4c68-bbcc-7b6331016905.json b/change/@fluentui-react-tabster-8d4667d2-30a7-4c68-bbcc-7b6331016905.json new file mode 100644 index 0000000000000..8153dc3030762 --- /dev/null +++ b/change/@fluentui-react-tabster-8d4667d2-30a7-4c68-bbcc-7b6331016905.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "feat: use browser `:focus-visible` if supported", + "packageName": "@fluentui/react-tabster", + "email": "lingfangao@hotmail.com", + "dependentChangeType": "patch" +} From eb138db4f7a0d61773165542072ff063bada97c5 Mon Sep 17 00:00:00 2001 From: Lingfan Gao Date: Wed, 3 Aug 2022 14:34:58 +0200 Subject: [PATCH 3/9] Add SSR checks --- .../react-tabster/src/hooks/useFocusVisible.ts | 3 ++- .../react-components/react-tabster/src/hooks/useFocusWithin.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts b/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts index f32d148eef862..0af82aee0e8bc 100644 --- a/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts +++ b/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts @@ -1,4 +1,5 @@ import * as React from 'react'; +import { canUseDOM } from '@fluentui/react-utilities'; import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; import { applyFocusVisiblePolyfill } from '../focus/focusVisiblePolyfill'; @@ -7,7 +8,7 @@ export function useFocusVisible() { const scopeRef = React.useRef(null); React.useEffect(() => { - if (targetDocument?.defaultView && scopeRef.current) { + if (targetDocument?.defaultView && scopeRef.current && canUseDOM()) { return applyFocusVisiblePolyfill(scopeRef.current, targetDocument.defaultView); } }, [scopeRef, targetDocument]); diff --git a/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts b/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts index 8e3a20077ffeb..4dca70b25e715 100644 --- a/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts +++ b/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts @@ -1,4 +1,5 @@ import * as React from 'react'; +import { canUseDOM } from '@fluentui/react-utilities'; import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; import { applyFocusWithinPolyfill } from '../focus/focusWithinPolyfill'; @@ -12,7 +13,7 @@ export function useFocusWithin() { const elementRef = React.useRef(null); React.useEffect(() => { - if (targetDocument?.defaultView && elementRef.current) { + if (targetDocument?.defaultView && elementRef.current && canUseDOM()) { return applyFocusWithinPolyfill(elementRef.current, targetDocument.defaultView); } }, [elementRef, targetDocument]); From a46af3303b33ea7f90ec6042a0463449c4ac026d Mon Sep 17 00:00:00 2001 From: Lingfan Gao Date: Wed, 3 Aug 2022 14:36:04 +0200 Subject: [PATCH 4/9] mark as internal --- .../react-tabster/src/hooks/useFocusVisible.ts | 4 ++++ .../react-tabster/src/hooks/useFocusWithin.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts b/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts index 0af82aee0e8bc..63d053723659f 100644 --- a/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts +++ b/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts @@ -3,6 +3,10 @@ import { canUseDOM } from '@fluentui/react-utilities'; import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; import { applyFocusVisiblePolyfill } from '../focus/focusVisiblePolyfill'; +/** + * @internal + * @returns ref to the container element whose children have `:focus-visible` styles + */ export function useFocusVisible() { const { targetDocument } = useFluent(); const scopeRef = React.useRef(null); diff --git a/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts b/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts index 4dca70b25e715..4d236a43b2abc 100644 --- a/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts +++ b/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts @@ -4,6 +4,7 @@ import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts import { applyFocusWithinPolyfill } from '../focus/focusWithinPolyfill'; /** + * @internal * A ponyfill that allows `:focus-within` to support visibility based on keyboard/mouse navigation * like `:focus-visible` https://github.com/WICG/focus-visible/issues/151 * @returns ref to the element that uses `:focus-within` styles From 9f8338b4bf12611dc9ab58ce8dd69fdcda756786 Mon Sep 17 00:00:00 2001 From: Lingfan Gao Date: Wed, 3 Aug 2022 14:47:52 +0200 Subject: [PATCH 5/9] update md --- .../react-tabster/etc/react-tabster.api.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-tabster/etc/react-tabster.api.md b/packages/react-components/react-tabster/etc/react-tabster.api.md index ac4ae061269cc..83014d0a75f8c 100644 --- a/packages/react-components/react-tabster/etc/react-tabster.api.md +++ b/packages/react-components/react-tabster/etc/react-tabster.api.md @@ -67,10 +67,14 @@ export const useFocusFinders: () => { findPrevFocusable: (currentElement: HTMLElement, options?: Pick) => HTMLElement | null | undefined; }; -// @public (undocumented) +// Warning: (ae-internal-missing-underscore) The name "useFocusVisible" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) export function useFocusVisible(): React_2.RefObject; -// @public +// Warning: (ae-internal-missing-underscore) The name "useFocusWithin" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal export function useFocusWithin(): React_2.RefObject; // @public From 1d9a1eb7900581c0b17033b2e5baf2844f7d5346 Mon Sep 17 00:00:00 2001 From: ling1726 Date: Wed, 3 Aug 2022 15:07:21 +0200 Subject: [PATCH 6/9] Update packages/react-components/react-tabster/src/hooks/useFocusVisible.ts --- .../react-components/react-tabster/src/hooks/useFocusVisible.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts b/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts index 63d053723659f..3ebc45aa4f4b9 100644 --- a/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts +++ b/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts @@ -1,5 +1,4 @@ import * as React from 'react'; -import { canUseDOM } from '@fluentui/react-utilities'; import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; import { applyFocusVisiblePolyfill } from '../focus/focusVisiblePolyfill'; From aae3965b28c0c17d9db6925c38a3e3920fc978a1 Mon Sep 17 00:00:00 2001 From: ling1726 Date: Wed, 3 Aug 2022 15:07:26 +0200 Subject: [PATCH 7/9] Update packages/react-components/react-tabster/src/hooks/useFocusVisible.ts --- .../react-components/react-tabster/src/hooks/useFocusVisible.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts b/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts index 3ebc45aa4f4b9..3b62a5058775e 100644 --- a/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts +++ b/packages/react-components/react-tabster/src/hooks/useFocusVisible.ts @@ -11,7 +11,7 @@ export function useFocusVisible() { const scopeRef = React.useRef(null); React.useEffect(() => { - if (targetDocument?.defaultView && scopeRef.current && canUseDOM()) { + if (targetDocument?.defaultView && scopeRef.current) { return applyFocusVisiblePolyfill(scopeRef.current, targetDocument.defaultView); } }, [scopeRef, targetDocument]); From 7764376ef8e85e27deb2a8cd60295d2062776fdd Mon Sep 17 00:00:00 2001 From: ling1726 Date: Wed, 3 Aug 2022 15:07:31 +0200 Subject: [PATCH 8/9] Update packages/react-components/react-tabster/src/hooks/useFocusWithin.ts --- .../react-components/react-tabster/src/hooks/useFocusWithin.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts b/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts index 4d236a43b2abc..214c266215319 100644 --- a/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts +++ b/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts @@ -1,5 +1,4 @@ import * as React from 'react'; -import { canUseDOM } from '@fluentui/react-utilities'; import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; import { applyFocusWithinPolyfill } from '../focus/focusWithinPolyfill'; From bc2f8cbb35aee0e45dd1035e7aec1788faac40e4 Mon Sep 17 00:00:00 2001 From: ling1726 Date: Wed, 3 Aug 2022 15:07:36 +0200 Subject: [PATCH 9/9] Update packages/react-components/react-tabster/src/hooks/useFocusWithin.ts --- .../react-components/react-tabster/src/hooks/useFocusWithin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts b/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts index 214c266215319..ab52d3435befb 100644 --- a/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts +++ b/packages/react-components/react-tabster/src/hooks/useFocusWithin.ts @@ -13,7 +13,7 @@ export function useFocusWithin() { const elementRef = React.useRef(null); React.useEffect(() => { - if (targetDocument?.defaultView && elementRef.current && canUseDOM()) { + if (targetDocument?.defaultView && elementRef.current) { return applyFocusWithinPolyfill(elementRef.current, targetDocument.defaultView); } }, [elementRef, targetDocument]);