Skip to content

Commit 3c85178

Browse files
committed
fix: restore focus to header when exiting tutorial
1 parent 36ebfc0 commit 3c85178

File tree

3 files changed

+34
-5
lines changed

3 files changed

+34
-5
lines changed

src/tutorial-panel/__tests__/tutorial-panel.test.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33
import React from 'react';
4-
import { render } from '@testing-library/react';
4+
import { render, waitFor } from '@testing-library/react';
55

66
import {
77
HotspotContext as HotspotContextType,
@@ -195,6 +195,20 @@ describe('tutorial detail view', () => {
195195
expect(completedScreen).toHaveTextContent('COMPLETION_SCREEN_TITLE');
196196
expect(completedScreen).toHaveTextContent('COMPLETED_SCREEN_DESCRIPTION_TEST');
197197
});
198+
199+
test('restores focus to panel header when exiting tutorial', async () => {
200+
const { container, rerender } = renderTutorialPanelWithContext({}, { currentTutorial: getTutorials()[0] });
201+
202+
rerender(
203+
<HotspotContext.Provider value={getContext({ currentTutorial: null })}>
204+
<TutorialPanel i18nStrings={i18nStrings} downloadUrl="DOWNLOAD_URL" tutorials={getTutorials()} />
205+
</HotspotContext.Provider>
206+
);
207+
208+
await waitFor(() => {
209+
expect(container.querySelector('[tabindex="-1"]')).toHaveFocus();
210+
});
211+
});
198212
});
199213

200214
describe('URL sanitization', () => {

src/tutorial-panel/components/tutorial-list/index.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ interface TutorialListProps {
2828
onStartTutorial: HotspotContext['onStartTutorial'];
2929
i18nStrings: TutorialPanelProps['i18nStrings'];
3030
downloadUrl: TutorialPanelProps['downloadUrl'];
31+
headerRef?: React.MutableRefObject<HTMLHeadingElement | null>;
3132
}
3233

3334
export default function TutorialList({
@@ -36,6 +37,7 @@ export default function TutorialList({
3637
loading = false,
3738
onStartTutorial,
3839
downloadUrl,
40+
headerRef,
3941
}: TutorialListProps) {
4042
checkSafeUrl('TutorialPanel', downloadUrl);
4143

@@ -45,9 +47,11 @@ export default function TutorialList({
4547
<>
4648
<InternalSpaceBetween size="s">
4749
<InternalSpaceBetween size="m">
48-
<InternalBox variant="h2" fontSize={isRefresh ? 'heading-m' : 'heading-l'} padding={{ bottom: 'n' }}>
49-
{i18nStrings.tutorialListTitle}
50-
</InternalBox>
50+
<div ref={headerRef} tabIndex={-1}>
51+
<InternalBox variant="h2" fontSize={isRefresh ? 'heading-m' : 'heading-l'} padding={{ bottom: 'n' }}>
52+
{i18nStrings.tutorialListTitle}
53+
</InternalBox>
54+
</div>
5155
<InternalBox variant="p" color="text-body-secondary" padding="n">
5256
{i18nStrings.tutorialListDescription}
5357
</InternalBox>

src/tutorial-panel/index.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33
'use client';
4-
import React, { useContext } from 'react';
4+
import React, { useContext, useEffect, useRef } from 'react';
55
import clsx from 'clsx';
66

77
import { hotspotContext } from '../annotation-context/context';
@@ -28,6 +28,16 @@ export default function TutorialPanel({
2828

2929
const baseProps = getBaseProps(restProps);
3030
const context = useContext(hotspotContext);
31+
const headerRef = useRef<HTMLHeadingElement>(null);
32+
const previousTutorialRef = useRef(context.currentTutorial);
33+
34+
// Restore focus to the tutorial panel header when exiting a tutorial
35+
useEffect(() => {
36+
if (!context.currentTutorial && previousTutorialRef.current) {
37+
headerRef.current?.focus({ preventScroll: true });
38+
}
39+
previousTutorialRef.current = context.currentTutorial;
40+
}, [context.currentTutorial]);
3141

3242
return (
3343
<>
@@ -47,6 +57,7 @@ export default function TutorialPanel({
4757
loading={loading}
4858
onStartTutorial={context.onStartTutorial}
4959
downloadUrl={downloadUrl}
60+
headerRef={headerRef}
5061
/>
5162
)}
5263
</div>

0 commit comments

Comments
 (0)