Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion src/internal/single-tab-stop/__tests__/context.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import {
SingleTabStopNavigationReset,
useSingleTabStopNavigation,
} from '../';
import { renderWithSingleTabStopNavigation } from './utils';
import {
setTestSingleTabStopNavigationTarget,
renderWithSingleTabStopNavigation,
TestSingleTabStopNavigationProvider,
} from '../test-helpers';

// Simple STSN subscriber component
function Button(props: React.HTMLAttributes<HTMLButtonElement>) {
Expand Down Expand Up @@ -56,6 +60,15 @@ function findGroupButton(groupId: string, buttonIndex: number) {
}

test('does not override tab index when keyboard navigation is not active', () => {
render(
<TestSingleTabStopNavigationProvider navigationActive={false}>
<Button id="button" />
</TestSingleTabStopNavigationProvider>
);
expect(document.querySelector('#button')).not.toHaveAttribute('tabIndex');
});

test('(legacy coverage) does not override tab index when keyboard navigation is not active', () => {
renderWithSingleTabStopNavigation(<Button id="button" />, { navigationActive: false });
expect(document.querySelector('#button')).not.toHaveAttribute('tabIndex');
});
Expand Down Expand Up @@ -84,6 +97,18 @@ test('does not override tab index for suppressed elements', () => {
});

test('overrides tab index when keyboard navigation is active', () => {
render(
<TestSingleTabStopNavigationProvider navigationActive={true}>
<Button id="button1" />
<Button id="button2" />
</TestSingleTabStopNavigationProvider>
);
setTestSingleTabStopNavigationTarget(document.querySelector('#button1'));
expect(document.querySelector('#button1')).toHaveAttribute('tabIndex', '0');
expect(document.querySelector('#button2')).toHaveAttribute('tabIndex', '-1');
});

test('(legacy coverage) overrides tab index when keyboard navigation is active', () => {
const { setCurrentTarget } = renderWithSingleTabStopNavigation(
<div>
<Button id="button1" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import React, { createRef, forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
import React, { forwardRef, useCallback, useEffect, useRef } from 'react';
import { render } from '@testing-library/react';

import { FocusableChangeHandler, SingleTabStopNavigationContext } from '../';
import { useUniqueId } from '../../use-unique-id';

interface ProviderRef {
setCurrentTarget(focusTarget: null | Element, suppressed?: (null | Element)[]): void;
type SetTarget = (focusTarget: null | Element, suppressed?: (null | Element)[]) => void;

interface TestProviderAPI {
setCurrentTarget: SetTarget;
}

const FakeSingleTabStopNavigationProvider = forwardRef(
(
{ children, navigationActive }: { children: React.ReactNode; navigationActive: boolean },
ref: React.Ref<ProviderRef>
) => {
const providerRegistry = new Map<string, TestProviderAPI>();

export const TestSingleTabStopNavigationProvider = forwardRef(
({ children, navigationActive }: { children: React.ReactNode; navigationActive: boolean }) => {
const focusablesRef = useRef(new Set<Element>());
const focusHandlersRef = useRef(new Map<Element, FocusableChangeHandler>());
const registerFocusable = useCallback((focusable: HTMLElement, changeHandler: FocusableChangeHandler) => {
Expand All @@ -26,14 +28,21 @@ const FakeSingleTabStopNavigationProvider = forwardRef(
};
}, []);

useImperativeHandle(ref, () => ({
setCurrentTarget: (focusTarget: null | Element, suppressed: Element[] = []) => {
const providerId = useUniqueId();
providerRegistry.set(providerId, {
setCurrentTarget: (focusTarget, suppressed = []) => {
focusablesRef.current.forEach(focusable => {
const handler = focusHandlersRef.current.get(focusable)!;
handler(focusTarget === focusable || suppressed.includes(focusable));
});
},
}));
});
useEffect(
() => () => {
providerRegistry.delete(providerId);
},
[providerId]
);

return (
<SingleTabStopNavigationContext.Provider
Expand All @@ -45,24 +54,23 @@ const FakeSingleTabStopNavigationProvider = forwardRef(
}
);

export const setTestSingleTabStopNavigationTarget: SetTarget = (focusTarget, suppressed) => {
Array.from(providerRegistry).forEach(([, provider]) => provider.setCurrentTarget(focusTarget, suppressed));
};

/**
* @deprecated - Use TestSingleTabStopNavigationProvider instead
*/
export function renderWithSingleTabStopNavigation(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you re-write this test-case to a non-deprecated version to prove that it works?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated two tests to make sure both the provider and the helper util work as expected. Once components code is migrated, I will remove the renderWithSingleTabStopNavigation and migrate the rest of the tests.

ui: React.ReactNode,
{ navigationActive = true }: { navigationActive?: boolean } = {}
) {
const providerRef = createRef<ProviderRef>();
const { container, rerender } = render(
<FakeSingleTabStopNavigationProvider ref={providerRef} navigationActive={navigationActive}>
{ui}
</FakeSingleTabStopNavigationProvider>
<TestSingleTabStopNavigationProvider navigationActive={navigationActive}>{ui}</TestSingleTabStopNavigationProvider>
);
return {
container,
rerender,
setCurrentTarget: (focusTarget: null | Element, suppressed: (null | Element)[] = []) => {
if (!providerRef.current) {
throw new Error('Provider is not ready');
}
providerRef.current.setCurrentTarget(focusTarget, suppressed);
},
setCurrentTarget: setTestSingleTabStopNavigationTarget,
};
}
6 changes: 5 additions & 1 deletion src/internal/testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ export { clearOneTimeMetricsCache } from './base-component/metrics/metrics';
export { clearMessageCache } from './logging';
export { setGlobalFlag } from './global-flags';
export { clearVisualRefreshState } from './visual-mode';
export { renderWithSingleTabStopNavigation } from './single-tab-stop/__tests__/utils';
export {
renderWithSingleTabStopNavigation,
TestSingleTabStopNavigationProvider,
setTestSingleTabStopNavigationTarget,
} from './single-tab-stop/test-helpers';
Loading