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)