diff --git a/src/components/navigation/skip-link/SkipLink.tsx b/src/components/navigation/skip-link/SkipLink.tsx index 23b0b86a..4d63b589 100644 --- a/src/components/navigation/skip-link/SkipLink.tsx +++ b/src/components/navigation/skip-link/SkipLink.tsx @@ -2,14 +2,19 @@ import React, { HTMLProps, MouseEvent, useEffect } from 'react'; import classNames from 'classnames'; interface SkipLinkProps extends HTMLProps { + /** The reference to the element to set focus to when the link is clicked */ focusTargetRef?: React.RefObject; + /** Disables the default anchor click behaviour, avoiding navigation entries being added to the browser history */ disableDefaultBehaviour?: boolean; + /** Disables focusing the first h1 level heading when {@link focusTargetRef} is not set */ + disableHeadingFocus?: boolean; } const SkipLink = ({ children = 'Skip to main content', className, disableDefaultBehaviour, + disableHeadingFocus, focusTargetRef, href = '#maincontent', tabIndex = 0, @@ -65,7 +70,7 @@ const SkipLink = ({ if (disableDefaultBehaviour) event.preventDefault(); if (focusTargetRef && focusTargetRef.current) { focusElement(focusTargetRef.current); - } else if (!disableDefaultBehaviour) { + } else if (!disableHeadingFocus) { // Follow the default NHSUK Frontend behaviour, but go about it in a safer way. // https://github.com/nhsuk/nhsuk-frontend/blob/master/packages/components/skip-link/skip-link.js if (firstHeadingElement) focusElement(firstHeadingElement); @@ -80,7 +85,7 @@ const SkipLink = ({ diff --git a/src/components/navigation/skip-link/__tests__/SkipLink.test.tsx b/src/components/navigation/skip-link/__tests__/SkipLink.test.tsx index b2d781de..6a4cba47 100644 --- a/src/components/navigation/skip-link/__tests__/SkipLink.test.tsx +++ b/src/components/navigation/skip-link/__tests__/SkipLink.test.tsx @@ -22,10 +22,44 @@ describe('SkipLink', () => { expect(container).toMatchSnapshot('SkipLink'); }); - it('sets the href to #maincontent by default', () => { - const { container } = render(); + it('sets the href to #maincontent by default and focuses the first heading', () => { + const { container } = render( + <> + +

Heading

+ , + ); + + const headingEl = container.querySelector('#heading') as HTMLElement; + const focusSpy = jest.spyOn(headingEl, 'focus'); + + const skipLinkEl = container.querySelector('.nhsuk-skip-link')!; + + expect(skipLinkEl.getAttribute('href')).toBe('#maincontent'); - expect(container.querySelector('.nhsuk-skip-link')?.getAttribute('href')).toBe('#maincontent'); + fireEvent.click(skipLinkEl); + + expect(focusSpy).toHaveBeenCalled(); + }); + + it('Does not focus the first heading if disableHeadingFocus is set', () => { + const { container } = render( + <> + +

Heading

+ , + ); + + const headingEl = container.querySelector('#heading') as HTMLElement; + const focusSpy = jest.spyOn(headingEl, 'focus'); + + const skipLinkEl = container.querySelector('.nhsuk-skip-link')!; + + expect(skipLinkEl.getAttribute('href')).toBe('#maincontent'); + + fireEvent.click(skipLinkEl); + + expect(focusSpy).not.toHaveBeenCalled(); }); it('calls onClick callback when clicked', () => { @@ -38,12 +72,6 @@ describe('SkipLink', () => { expect(onClick).toHaveBeenCalled(); }); - it('does not set the href when disableDefaultBehaviour is set', () => { - const { container } = render(); - - expect(container.querySelector('.nhsuk-skip-link')?.getAttribute('href')).toBeFalsy(); - }); - it('Focuses the main content when clicked', () => { const { container } = render(); diff --git a/stories/Navigation/SkipLink.stories.tsx b/stories/Navigation/SkipLink.stories.tsx index e801c761..3573d6a5 100644 --- a/stories/Navigation/SkipLink.stories.tsx +++ b/stories/Navigation/SkipLink.stories.tsx @@ -25,9 +25,7 @@ const CodeText: React.FC<{ children: React.ReactNode }> = ({ const meta: Meta = { title: 'Navigation/SkipLink', component: SkipLink, - argTypes: { - focusTargetRef: { table: { disable: true } }, - }, + render: (args) => ( <> @@ -35,7 +33,12 @@ const meta: Meta = { tab to show the SkipLink - + +

Page heading

+
This is the main content
), }; @@ -46,14 +49,20 @@ type Story = StoryObj; export const Standard: Story = { args: { disableDefaultBehaviour: false, - }, - argTypes: { - disableDefaultBehaviour: { table: { disable: true } }, + disableHeadingFocus: false, }, }; export const SkipLinkWithDefaultBehaviourDisabled: Story = { args: { disableDefaultBehaviour: true, + disableHeadingFocus: false, + }, +}; + +export const SkipLinkWithHeadingFocusDisabled: Story = { + args: { + disableDefaultBehaviour: false, + disableHeadingFocus: true, }, };