11// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22// SPDX-License-Identifier: Apache-2.0
33
4- import React , { createRef , forwardRef , useCallback , useImperativeHandle , useRef } from 'react' ;
4+ import React , { forwardRef , useCallback , useEffect , useRef } from 'react' ;
55import { render } from '@testing-library/react' ;
66
77import { FocusableChangeHandler , SingleTabStopNavigationContext } from '../' ;
8+ import { useUniqueId } from '../../use-unique-id' ;
89
9- interface ProviderRef {
10- setCurrentTarget ( focusTarget : null | Element , suppressed ?: ( null | Element ) [ ] ) : void ;
10+ type SetTarget = ( focusTarget : null | Element , suppressed ?: ( null | Element ) [ ] ) => void ;
11+
12+ interface TestProviderAPI {
13+ setCurrentTarget : SetTarget ;
1114}
1215
13- const FakeSingleTabStopNavigationProvider = forwardRef (
14- (
15- { children, navigationActive } : { children : React . ReactNode ; navigationActive : boolean } ,
16- ref : React . Ref < ProviderRef >
17- ) => {
16+ const providerRegistry = new Map < string , TestProviderAPI > ( ) ;
17+
18+ export const TestSingleTabStopNavigationProvider = forwardRef (
19+ ( { children, navigationActive } : { children : React . ReactNode ; navigationActive : boolean } ) => {
1820 const focusablesRef = useRef ( new Set < Element > ( ) ) ;
1921 const focusHandlersRef = useRef ( new Map < Element , FocusableChangeHandler > ( ) ) ;
2022 const registerFocusable = useCallback ( ( focusable : HTMLElement , changeHandler : FocusableChangeHandler ) => {
@@ -26,14 +28,21 @@ const FakeSingleTabStopNavigationProvider = forwardRef(
2628 } ;
2729 } , [ ] ) ;
2830
29- useImperativeHandle ( ref , ( ) => ( {
30- setCurrentTarget : ( focusTarget : null | Element , suppressed : Element [ ] = [ ] ) => {
31+ const providerId = useUniqueId ( ) ;
32+ providerRegistry . set ( providerId , {
33+ setCurrentTarget : ( focusTarget , suppressed = [ ] ) => {
3134 focusablesRef . current . forEach ( focusable => {
3235 const handler = focusHandlersRef . current . get ( focusable ) ! ;
3336 handler ( focusTarget === focusable || suppressed . includes ( focusable ) ) ;
3437 } ) ;
3538 } ,
36- } ) ) ;
39+ } ) ;
40+ useEffect (
41+ ( ) => ( ) => {
42+ providerRegistry . delete ( providerId ) ;
43+ } ,
44+ [ providerId ]
45+ ) ;
3746
3847 return (
3948 < SingleTabStopNavigationContext . Provider
@@ -45,24 +54,23 @@ const FakeSingleTabStopNavigationProvider = forwardRef(
4554 }
4655) ;
4756
57+ export const setTestSingleTabStopNavigationTarget : SetTarget = ( focusTarget , suppressed ) => {
58+ Array . from ( providerRegistry ) . forEach ( ( [ , provider ] ) => provider . setCurrentTarget ( focusTarget , suppressed ) ) ;
59+ } ;
60+
61+ /**
62+ * @deprecated - Use TestSingleTabStopNavigationProvider instead
63+ */
4864export function renderWithSingleTabStopNavigation (
4965 ui : React . ReactNode ,
5066 { navigationActive = true } : { navigationActive ?: boolean } = { }
5167) {
52- const providerRef = createRef < ProviderRef > ( ) ;
5368 const { container, rerender } = render (
54- < FakeSingleTabStopNavigationProvider ref = { providerRef } navigationActive = { navigationActive } >
55- { ui }
56- </ FakeSingleTabStopNavigationProvider >
69+ < TestSingleTabStopNavigationProvider navigationActive = { navigationActive } > { ui } </ TestSingleTabStopNavigationProvider >
5770 ) ;
5871 return {
5972 container,
6073 rerender,
61- setCurrentTarget : ( focusTarget : null | Element , suppressed : ( null | Element ) [ ] = [ ] ) => {
62- if ( ! providerRef . current ) {
63- throw new Error ( 'Provider is not ready' ) ;
64- }
65- providerRef . current . setCurrentTarget ( focusTarget , suppressed ) ;
66- } ,
74+ setCurrentTarget : setTestSingleTabStopNavigationTarget ,
6775 } ;
6876}
0 commit comments