From 9a1c244c7d1e0e29665598556627158b4479d7cc Mon Sep 17 00:00:00 2001 From: Xavier Lozano Carreras Date: Tue, 8 Oct 2024 16:26:56 +0200 Subject: [PATCH 1/5] `useKeydownHandler`: Skip handlers if the document active element is not on the tour container --- .../components/keyboard-navigation.tsx | 3 +- .../tour-kit/hooks/use-keydown-handler.ts | 29 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/components/keyboard-navigation.tsx b/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/components/keyboard-navigation.tsx index 7d913d40d3a7f..002a3a3ad050c 100644 --- a/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/components/keyboard-navigation.tsx +++ b/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/components/keyboard-navigation.tsx @@ -34,6 +34,7 @@ const KeyboardNavigation: React.FunctionComponent< Props > = ( { onEscape: onMinimize, onArrowRight: onNextStepProgression, onArrowLeft: onPreviousStepProgression, + tourContainerRef, } ); useFocusTrap( tourContainerRef ); @@ -44,7 +45,7 @@ const KeyboardNavigation: React.FunctionComponent< Props > = ( { * Minimize Tour Nav */ function MinimizedTourNav() { - useKeydownHandler( { onEscape: onDismiss( 'esc-key-minimized' ) } ); + useKeydownHandler( { onEscape: onDismiss( 'esc-key-minimized' ), tourContainerRef } ); return null; } diff --git a/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/hooks/use-keydown-handler.ts b/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/hooks/use-keydown-handler.ts index 1cdf93219b40e..a050053705a0e 100644 --- a/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/hooks/use-keydown-handler.ts +++ b/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/hooks/use-keydown-handler.ts @@ -8,12 +8,25 @@ interface Props { onEscape?: () => void; onArrowRight?: () => void; onArrowLeft?: () => void; + tourContainerRef: React.MutableRefObject< null | HTMLElement >; } /** * A hook the applies the respective callbacks in response to keydown events. */ -const useKeydownHandler = ( { onEscape, onArrowRight, onArrowLeft }: Props ): void => { +const useKeydownHandler = ( { + onEscape, + onArrowRight, + onArrowLeft, + tourContainerRef, +}: Props ): void => { + const isActiveElementOutsideTourContainer = useCallback( (): boolean => { + return !! ( + tourContainerRef.current && + ! tourContainerRef.current.contains( tourContainerRef.current.ownerDocument.activeElement ) + ); + }, [ tourContainerRef ] ); + const handleKeydown = useCallback( ( event: KeyboardEvent ) => { let handled = false; @@ -21,18 +34,30 @@ const useKeydownHandler = ( { onEscape, onArrowRight, onArrowLeft }: Props ): vo switch ( event.key ) { case 'Escape': if ( onEscape ) { + if ( isActiveElementOutsideTourContainer() ) { + return; + } + onEscape(); handled = true; } break; case 'ArrowRight': if ( onArrowRight ) { + if ( isActiveElementOutsideTourContainer() ) { + return; + } + onArrowRight(); handled = true; } break; case 'ArrowLeft': if ( onArrowLeft ) { + if ( isActiveElementOutsideTourContainer() ) { + return; + } + onArrowLeft(); handled = true; } @@ -46,7 +71,7 @@ const useKeydownHandler = ( { onEscape, onArrowRight, onArrowLeft }: Props ): vo event.stopPropagation(); } }, - [ onEscape, onArrowRight, onArrowLeft ] + [ isActiveElementOutsideTourContainer, onEscape, onArrowRight, onArrowLeft ] ); useEffect( () => { From 3bed1d23ae773a0f5ead3e7a8c32f5d14422de00 Mon Sep 17 00:00:00 2001 From: Xavier Lozano Carreras Date: Tue, 8 Oct 2024 17:07:59 +0200 Subject: [PATCH 2/5] Force focus if necessary so keyboard navigation still works --- .../tour-kit/components/tour-kit-frame.tsx | 1 + .../tour-kit/hooks/use-keydown-handler.ts | 28 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/components/tour-kit-frame.tsx b/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/components/tour-kit-frame.tsx index e030a37cdda26..0d5aeb3d5b091 100644 --- a/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/components/tour-kit-frame.tsx +++ b/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/components/tour-kit-frame.tsx @@ -247,6 +247,7 @@ const TourKitFrame: React.FunctionComponent< Props > = ( { config } ) => {
) } > { showArrowIndicator() && ( diff --git a/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/hooks/use-keydown-handler.ts b/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/hooks/use-keydown-handler.ts index a050053705a0e..0278f6d8079b9 100644 --- a/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/hooks/use-keydown-handler.ts +++ b/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/hooks/use-keydown-handler.ts @@ -74,13 +74,39 @@ const useKeydownHandler = ( { [ isActiveElementOutsideTourContainer, onEscape, onArrowRight, onArrowLeft ] ); + const isFocusable = ( element: HTMLElement ) => { + const focusableElements = [ 'A', 'INPUT', 'BUTTON', 'TEXTAREA', 'SELECT' ]; + + // Check if the element is focusable by its tag or has a tabindex >= 0 + return focusableElements.includes( element?.tagName ) || element?.tabIndex >= 0; + }; + + // when clicking on the container, if the target is not a focusable element, + // force focus on the first children so keyboard navigation works + const handleTourContainerClick = useCallback( + ( event: MouseEvent ) => { + if ( isFocusable( event.target as HTMLElement ) ) { + return; + } + + ( + tourContainerRef.current?.querySelector( '.tour-kit-frame__container' ) as HTMLElement + )?.focus(); + }, + [ tourContainerRef ] + ); + useEffect( () => { + const tourContainer = tourContainerRef.current; + document.addEventListener( 'keydown', handleKeydown ); + tourContainer?.addEventListener( 'click', handleTourContainerClick ); return () => { document.removeEventListener( 'keydown', handleKeydown ); + tourContainer?.removeEventListener( 'click', handleTourContainerClick ); }; - }, [ handleKeydown ] ); + }, [ handleKeydown, handleTourContainerClick, tourContainerRef ] ); }; export default useKeydownHandler; From 5df9c04e94e1cf40f705ba88e075503832f4eb9f Mon Sep 17 00:00:00 2001 From: Xavier Lozano Carreras Date: Tue, 8 Oct 2024 17:11:42 +0200 Subject: [PATCH 3/5] changelog --- ...d navigation from hijacking left right keys on the editor | 4 ++++ ...rd-navigation-from-hijacking-left-right-keys-on-the-editor | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/prevent welcome tour keyboard navigation from hijacking left right keys on the editor create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/prevent-welcome-tour-keyboard-navigation-from-hijacking-left-right-keys-on-the-editor diff --git a/projects/packages/jetpack-mu-wpcom/changelog/prevent welcome tour keyboard navigation from hijacking left right keys on the editor b/projects/packages/jetpack-mu-wpcom/changelog/prevent welcome tour keyboard navigation from hijacking left right keys on the editor new file mode 100644 index 0000000000000..7fe09c276bf62 --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/prevent welcome tour keyboard navigation from hijacking left right keys on the editor @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Prevent welcome tour keyboard navigation from hijacking left right keys on the editor diff --git a/projects/packages/jetpack-mu-wpcom/changelog/prevent-welcome-tour-keyboard-navigation-from-hijacking-left-right-keys-on-the-editor b/projects/packages/jetpack-mu-wpcom/changelog/prevent-welcome-tour-keyboard-navigation-from-hijacking-left-right-keys-on-the-editor new file mode 100644 index 0000000000000..b9ff1ac7fed20 --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/prevent-welcome-tour-keyboard-navigation-from-hijacking-left-right-keys-on-the-editor @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Prevent welcome tour keyboard navigation from hijacking left right keys on the editor From 45c71d97594612a9d75d053a3132c00f3501bd0d Mon Sep 17 00:00:00 2001 From: Xavier Lozano Carreras Date: Tue, 8 Oct 2024 18:16:43 +0200 Subject: [PATCH 4/5] Make sure the tour can be dismissed after minimizing --- .../tour-kit/hooks/use-keydown-handler.ts | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/hooks/use-keydown-handler.ts b/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/hooks/use-keydown-handler.ts index 0278f6d8079b9..8d88f2435d4e0 100644 --- a/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/hooks/use-keydown-handler.ts +++ b/projects/packages/jetpack-mu-wpcom/src/common/tour-kit/hooks/use-keydown-handler.ts @@ -27,6 +27,12 @@ const useKeydownHandler = ( { ); }, [ tourContainerRef ] ); + const focusTourContainer = useCallback( () => { + ( + tourContainerRef.current?.querySelector( '.tour-kit-frame__container' ) as HTMLElement + )?.focus(); + }, [ tourContainerRef ] ); + const handleKeydown = useCallback( ( event: KeyboardEvent ) => { let handled = false; @@ -39,6 +45,8 @@ const useKeydownHandler = ( { } onEscape(); + // focus the container after minimizing so the user can dismiss it + focusTourContainer(); handled = true; } break; @@ -71,29 +79,27 @@ const useKeydownHandler = ( { event.stopPropagation(); } }, - [ isActiveElementOutsideTourContainer, onEscape, onArrowRight, onArrowLeft ] + [ onEscape, onArrowRight, onArrowLeft, isActiveElementOutsideTourContainer, focusTourContainer ] ); - const isFocusable = ( element: HTMLElement ) => { - const focusableElements = [ 'A', 'INPUT', 'BUTTON', 'TEXTAREA', 'SELECT' ]; - - // Check if the element is focusable by its tag or has a tabindex >= 0 - return focusableElements.includes( element?.tagName ) || element?.tabIndex >= 0; - }; - // when clicking on the container, if the target is not a focusable element, // force focus on the first children so keyboard navigation works const handleTourContainerClick = useCallback( ( event: MouseEvent ) => { + const isFocusable = ( element: HTMLElement ) => { + const focusableElements = [ 'A', 'INPUT', 'BUTTON', 'TEXTAREA', 'SELECT' ]; + + // Check if the element is focusable by its tag or has a tabindex >= 0 + return focusableElements.includes( element?.tagName ) || element?.tabIndex >= 0; + }; + if ( isFocusable( event.target as HTMLElement ) ) { return; } - ( - tourContainerRef.current?.querySelector( '.tour-kit-frame__container' ) as HTMLElement - )?.focus(); + focusTourContainer(); }, - [ tourContainerRef ] + [ focusTourContainer ] ); useEffect( () => { From 012d3064b7c1554f7e5aba310b45333bb5476dc5 Mon Sep 17 00:00:00 2001 From: Xavier Lozano Carreras Date: Tue, 8 Oct 2024 18:21:10 +0200 Subject: [PATCH 5/5] Rm duplicated changelog --- ...d navigation from hijacking left right keys on the editor | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 projects/packages/jetpack-mu-wpcom/changelog/prevent welcome tour keyboard navigation from hijacking left right keys on the editor diff --git a/projects/packages/jetpack-mu-wpcom/changelog/prevent welcome tour keyboard navigation from hijacking left right keys on the editor b/projects/packages/jetpack-mu-wpcom/changelog/prevent welcome tour keyboard navigation from hijacking left right keys on the editor deleted file mode 100644 index 7fe09c276bf62..0000000000000 --- a/projects/packages/jetpack-mu-wpcom/changelog/prevent welcome tour keyboard navigation from hijacking left right keys on the editor +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fixed - -Prevent welcome tour keyboard navigation from hijacking left right keys on the editor