Skip to content

Commit 80edae3

Browse files
authored
Fix issues with SkipLink and align to NHSUK frontend (#248)
1 parent 16d417f commit 80edae3

File tree

3 files changed

+60
-18
lines changed

3 files changed

+60
-18
lines changed

src/components/navigation/skip-link/SkipLink.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@ import React, { HTMLProps, MouseEvent, useEffect } from 'react';
22
import classNames from 'classnames';
33

44
interface SkipLinkProps extends HTMLProps<HTMLAnchorElement> {
5+
/** The reference to the element to set focus to when the link is clicked */
56
focusTargetRef?: React.RefObject<HTMLElement>;
7+
/** Disables the default anchor click behaviour, avoiding navigation entries being added to the browser history */
68
disableDefaultBehaviour?: boolean;
9+
/** Disables focusing the first h1 level heading when {@link focusTargetRef} is not set */
10+
disableHeadingFocus?: boolean;
711
}
812

913
const SkipLink = ({
1014
children = 'Skip to main content',
1115
className,
1216
disableDefaultBehaviour,
17+
disableHeadingFocus,
1318
focusTargetRef,
1419
href = '#maincontent',
1520
tabIndex = 0,
@@ -65,7 +70,7 @@ const SkipLink = ({
6570
if (disableDefaultBehaviour) event.preventDefault();
6671
if (focusTargetRef && focusTargetRef.current) {
6772
focusElement(focusTargetRef.current);
68-
} else if (!disableDefaultBehaviour) {
73+
} else if (!disableHeadingFocus) {
6974
// Follow the default NHSUK Frontend behaviour, but go about it in a safer way.
7075
// https://github.com/nhsuk/nhsuk-frontend/blob/master/packages/components/skip-link/skip-link.js
7176
if (firstHeadingElement) focusElement(firstHeadingElement);
@@ -80,7 +85,7 @@ const SkipLink = ({
8085
<a
8186
className={classNames('nhsuk-skip-link', className)}
8287
onClick={focusTarget}
83-
href={disableDefaultBehaviour ? undefined : href}
88+
href={href}
8489
tabIndex={tabIndex}
8590
{...rest}
8691
>

src/components/navigation/skip-link/__tests__/SkipLink.test.tsx

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,44 @@ describe('SkipLink', () => {
2222
expect(container).toMatchSnapshot('SkipLink');
2323
});
2424

25-
it('sets the href to #maincontent by default', () => {
26-
const { container } = render(<SkipLink />);
25+
it('sets the href to #maincontent by default and focuses the first heading', () => {
26+
const { container } = render(
27+
<>
28+
<SkipLink />
29+
<h1 id="heading">Heading</h1>
30+
</>,
31+
);
32+
33+
const headingEl = container.querySelector('#heading') as HTMLElement;
34+
const focusSpy = jest.spyOn(headingEl, 'focus');
35+
36+
const skipLinkEl = container.querySelector('.nhsuk-skip-link')!;
37+
38+
expect(skipLinkEl.getAttribute('href')).toBe('#maincontent');
2739

28-
expect(container.querySelector('.nhsuk-skip-link')?.getAttribute('href')).toBe('#maincontent');
40+
fireEvent.click(skipLinkEl);
41+
42+
expect(focusSpy).toHaveBeenCalled();
43+
});
44+
45+
it('Does not focus the first heading if disableHeadingFocus is set', () => {
46+
const { container } = render(
47+
<>
48+
<SkipLink disableHeadingFocus />
49+
<h1 id="heading">Heading</h1>
50+
</>,
51+
);
52+
53+
const headingEl = container.querySelector('#heading') as HTMLElement;
54+
const focusSpy = jest.spyOn(headingEl, 'focus');
55+
56+
const skipLinkEl = container.querySelector('.nhsuk-skip-link')!;
57+
58+
expect(skipLinkEl.getAttribute('href')).toBe('#maincontent');
59+
60+
fireEvent.click(skipLinkEl);
61+
62+
expect(focusSpy).not.toHaveBeenCalled();
2963
});
3064

3165
it('calls onClick callback when clicked', () => {
@@ -38,12 +72,6 @@ describe('SkipLink', () => {
3872
expect(onClick).toHaveBeenCalled();
3973
});
4074

41-
it('does not set the href when disableDefaultBehaviour is set', () => {
42-
const { container } = render(<SkipLink disableDefaultBehaviour />);
43-
44-
expect(container.querySelector('.nhsuk-skip-link')?.getAttribute('href')).toBeFalsy();
45-
});
46-
4775
it('Focuses the main content when clicked', () => {
4876
const { container } = render(<SkipLinkTestApp />);
4977

stories/Navigation/SkipLink.stories.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,20 @@ const CodeText: React.FC<{ children: React.ReactNode }> = ({
2525
const meta: Meta<typeof SkipLink> = {
2626
title: 'Navigation/SkipLink',
2727
component: SkipLink,
28-
argTypes: {
29-
focusTargetRef: { table: { disable: true } },
30-
},
28+
3129
render: (args) => (
3230
<>
3331
<HintText>
3432
Press
3533
<CodeText>tab</CodeText>
3634
to show the SkipLink
3735
</HintText>
38-
<SkipLink disableDefaultBehaviour={args.disableDefaultBehaviour} />
36+
<SkipLink
37+
disableDefaultBehaviour={args.disableDefaultBehaviour}
38+
disableHeadingFocus={args.disableHeadingFocus}
39+
/>
40+
<h1>Page heading</h1>
41+
<div id="#maincontent">This is the main content</div>
3942
</>
4043
),
4144
};
@@ -46,14 +49,20 @@ type Story = StoryObj<typeof SkipLink>;
4649
export const Standard: Story = {
4750
args: {
4851
disableDefaultBehaviour: false,
49-
},
50-
argTypes: {
51-
disableDefaultBehaviour: { table: { disable: true } },
52+
disableHeadingFocus: false,
5253
},
5354
};
5455

5556
export const SkipLinkWithDefaultBehaviourDisabled: Story = {
5657
args: {
5758
disableDefaultBehaviour: true,
59+
disableHeadingFocus: false,
60+
},
61+
};
62+
63+
export const SkipLinkWithHeadingFocusDisabled: Story = {
64+
args: {
65+
disableDefaultBehaviour: false,
66+
disableHeadingFocus: true,
5867
},
5968
};

0 commit comments

Comments
 (0)