diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 67f93990a1..571e077d24 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -18,6 +18,7 @@ Changelog * Clean up Prettier & Eslint usage for search promotions formset JS file (LB (Ben Johnston)) * Ensure that translation file generation ignores JavaScript unit tests and clean up unit tests for Django gettext utils (LB (Ben Johnston)) * Migrated `initButtonSelects` from core.js to own TypesScript file and add unit tests (Loveth Omokaro) + * Migrated `initSkipLink` util to TypeScript and add JSDoc & unit tests (Juliet Adeboye) * Clean up some unused utility classes and migrate `unlist` to Tailwind utility class `w-list-none` (Loveth Omokaro) * Ensure that the `rebuild_references_index` command can run without console output if called with `--verbosity 0` (Omerzahid Ali, Aman Pandey) * Fix: Make sure workflow timeline icons are visible in high-contrast mode (Loveth Omokaro) diff --git a/client/src/entrypoints/admin/wagtailadmin.js b/client/src/entrypoints/admin/wagtailadmin.js index 11e3f800d0..a32b033105 100644 --- a/client/src/entrypoints/admin/wagtailadmin.js +++ b/client/src/entrypoints/admin/wagtailadmin.js @@ -4,8 +4,8 @@ import { Icon, Portal, initDismissibles, - initUpgradeNotification, initSkipLink, + initUpgradeNotification, } from '../..'; import { initModernDropdown, initTooltips } from '../../includes/initTooltips'; import { initTabs } from '../../includes/tabs'; diff --git a/client/src/includes/initSkipLink.test.js b/client/src/includes/initSkipLink.test.js new file mode 100644 index 0000000000..cea4ccd8b9 --- /dev/null +++ b/client/src/includes/initSkipLink.test.js @@ -0,0 +1,22 @@ +import { initSkipLink } from './initSkipLink'; + +describe('initSkipLink', () => { + document.body.innerHTML = ` +
+
Main content
+ `; + + it('should add tabindex to make focusable and remove again', () => { + const mainElement = document.getElementById('main'); + + expect(document.activeElement).toBe(document.body); + expect(mainElement.getAttribute('tabindex')).toEqual(null); + + initSkipLink(); + + document.getElementById('test').click(); + + expect(mainElement.getAttribute('tabindex')).toEqual('-1'); + expect(document.activeElement).toEqual(mainElement); + }); +}); diff --git a/client/src/includes/initSkipLink.js b/client/src/includes/initSkipLink.ts similarity index 52% rename from client/src/includes/initSkipLink.js rename to client/src/includes/initSkipLink.ts index 59d41f7133..4a817b6f05 100644 --- a/client/src/includes/initSkipLink.js +++ b/client/src/includes/initSkipLink.ts @@ -1,17 +1,25 @@ +/** + * Appears at the top left corner of the admin page with the tab button is clicked. + * Used to provide an accessible skip button for keyboard control so that users can + * easily navigate to the main content without having to navigate a long list of navigation links. + * + * Inspired by https://github.com/selfthinker/dokuwiki_template_writr/blob/master/js/skip-link-focus-fix.js + * + */ const initSkipLink = () => { - // Inspired by https://github.com/selfthinker/dokuwiki_template_writr/blob/master/js/skip-link-focus-fix.js - const skiplink = document.querySelector('[data-skiplink]'); const main = document.querySelector('main'); const handleBlur = () => { + if (!main) return; main.removeAttribute('tabindex'); main.removeEventListener('blur', handleBlur); main.removeEventListener('focusout', handleBlur); }; const handleClick = () => { - main.setAttribute('tabindex', -1); + if (!main) return; + main.setAttribute('tabindex', '-1'); main.addEventListener('blur', handleBlur); main.addEventListener('focusout', handleBlur); main.focus(); @@ -22,4 +30,4 @@ const initSkipLink = () => { } }; -export default initSkipLink; +export { initSkipLink }; diff --git a/client/src/index.ts b/client/src/index.ts index a7d95daf96..a785e07f1a 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -9,6 +9,6 @@ export { default as LoadingSpinner } from './components/LoadingSpinner/LoadingSp export { default as Portal } from './components/Portal/Portal'; export { default as PublicationStatus } from './components/PublicationStatus/PublicationStatus'; export { default as Transition } from './components/Transition/Transition'; -export { default as initSkipLink } from './includes/initSkipLink'; export { initDismissibles } from './includes/initDismissibles'; +export { initSkipLink } from './includes/initSkipLink'; export { initUpgradeNotification } from './includes/initUpgradeNotification'; diff --git a/docs/releases/4.2.md b/docs/releases/4.2.md index 5de999dd70..6270afd5f2 100644 --- a/docs/releases/4.2.md +++ b/docs/releases/4.2.md @@ -27,6 +27,7 @@ depth: 1 * Add documentation for [`register_user_listing_buttons`](register_user_listing_buttons) hook (LB (Ben Johnston)) * Ensure that translation file generation ignores JavaScript unit tests and clean up unit tests for Django gettext utils (LB (Ben Johnston)) * Migrated `initButtonSelects` from core.js to own TypesScript file and add unit tests (Loveth Omokaro) + * Migrated `initSkipLink` util to TypeScript and add JSDoc & unit tests (Juliet Adeboye) * Clean up some unused utility classes and migrate `unlist` to Tailwind utility class `w-list-none` (Loveth Omokaro) * Ensure that the `rebuild_references_index` command can run without console output if called with `--verbosity 0` (Omerzahid Ali, Aman Pandey)