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+ interface TestProviderAPI {
1011 setCurrentTarget ( focusTarget : null | Element , suppressed ?: ( null | Element ) [ ] ) : void ;
1112}
1213
13- const FakeSingleTabStopNavigationProvider = forwardRef (
14- (
15- { children, navigationActive } : { children : React . ReactNode ; navigationActive : boolean } ,
16- ref : React . Ref < ProviderRef >
17- ) => {
14+ const providerRegistry = new Map < string , TestProviderAPI > ( ) ;
15+
16+ export const TestSingleTabStopNavigationProvider = forwardRef (
17+ ( {
18+ children,
19+ navigationActive,
20+ providerId : explicitProviderId ,
21+ } : {
22+ children : React . ReactNode ;
23+ navigationActive : boolean ;
24+ providerId ?: string ;
25+ } ) => {
1826 const focusablesRef = useRef ( new Set < Element > ( ) ) ;
1927 const focusHandlersRef = useRef ( new Map < Element , FocusableChangeHandler > ( ) ) ;
2028 const registerFocusable = useCallback ( ( focusable : HTMLElement , changeHandler : FocusableChangeHandler ) => {
@@ -26,14 +34,22 @@ const FakeSingleTabStopNavigationProvider = forwardRef(
2634 } ;
2735 } , [ ] ) ;
2836
29- useImperativeHandle ( ref , ( ) => ( {
37+ const providerIdFallback = useUniqueId ( ) ;
38+ const providerId = explicitProviderId ?? providerIdFallback ;
39+ providerRegistry . set ( providerId , {
3040 setCurrentTarget : ( focusTarget : null | Element , suppressed : Element [ ] = [ ] ) => {
3141 focusablesRef . current . forEach ( focusable => {
3242 const handler = focusHandlersRef . current . get ( focusable ) ! ;
3343 handler ( focusTarget === focusable || suppressed . includes ( focusable ) ) ;
3444 } ) ;
3545 } ,
36- } ) ) ;
46+ } ) ;
47+ useEffect (
48+ ( ) => ( ) => {
49+ providerRegistry . delete ( providerId ) ;
50+ } ,
51+ [ providerId ]
52+ ) ;
3753
3854 return (
3955 < SingleTabStopNavigationContext . Provider
@@ -45,24 +61,32 @@ const FakeSingleTabStopNavigationProvider = forwardRef(
4561 }
4662) ;
4763
64+ export function getTestProvider ( providerId ?: string ) : TestProviderAPI {
65+ return {
66+ setCurrentTarget ( focusTarget , suppressed ) {
67+ if ( providerId ) {
68+ providerRegistry . get ( providerId ) ?. setCurrentTarget ( focusTarget , suppressed ) ;
69+ } else {
70+ Array . from ( providerRegistry ) . forEach ( ( [ , provider ] ) => provider . setCurrentTarget ( focusTarget , suppressed ) ) ;
71+ }
72+ } ,
73+ } ;
74+ }
75+
76+ /**
77+ * @deprecated - Use TestSingleTabStopNavigationProvider instead
78+ */
4879export function renderWithSingleTabStopNavigation (
4980 ui : React . ReactNode ,
5081 { navigationActive = true } : { navigationActive ?: boolean } = { }
5182) {
52- const providerRef = createRef < ProviderRef > ( ) ;
5383 const { container, rerender } = render (
54- < FakeSingleTabStopNavigationProvider ref = { providerRef } navigationActive = { navigationActive } >
55- { ui }
56- </ FakeSingleTabStopNavigationProvider >
84+ < TestSingleTabStopNavigationProvider navigationActive = { navigationActive } > { ui } </ TestSingleTabStopNavigationProvider >
5785 ) ;
5886 return {
5987 container,
6088 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- } ,
89+ setCurrentTarget : ( focusTarget : null | Element , suppressed : ( null | Element ) [ ] = [ ] ) =>
90+ getTestProvider ( ) . setCurrentTarget ( focusTarget , suppressed ) ,
6791 } ;
6892}
0 commit comments