From b1c1618c9ba8418626e4d1c06bbbe4ed5f719d9d Mon Sep 17 00:00:00 2001 From: Lovelyfin00 Date: Wed, 2 Nov 2022 13:26:33 +0100 Subject: [PATCH] Migrated initButtonSelects from core.js to own includes file - removed initButtonSelects from core.js , migrate to Typescript & and wrote test for it - Fixes #9494 --- CHANGELOG.txt | 1 + client/src/entrypoints/admin/core.js | 29 +-------- client/src/includes/initButtonSelects.ts | 35 +++++++++++ client/src/includes/messages.test.js | 77 ++++++++++++++++++++++++ docs/releases/4.2.md | 1 + 5 files changed, 116 insertions(+), 27 deletions(-) create mode 100644 client/src/includes/initButtonSelects.ts create mode 100644 client/src/includes/messages.test.js diff --git a/CHANGELOG.txt b/CHANGELOG.txt index c20dfb1d1d..6e91cc3855 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -17,6 +17,7 @@ Changelog * Add documentation for `register_user_listing_buttons` hook (LB (Ben Johnston)) * 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) * Fix: Make sure workflow timeline icons are visible in high-contrast mode (Loveth Omokaro) * Fix: Ensure authentication forms (login, password reset) have a visible border in Windows high-contrast mode (Loveth Omokaro) * Fix: Ensure visual consistency between buttons and links as buttons in Windows high-contrast mode (Albina Starykova) diff --git a/client/src/entrypoints/admin/core.js b/client/src/entrypoints/admin/core.js index ec9b7694f9..a39e504a3d 100644 --- a/client/src/entrypoints/admin/core.js +++ b/client/src/entrypoints/admin/core.js @@ -1,6 +1,7 @@ import $ from 'jquery'; -import { initTooltips } from '../../includes/initTooltips'; import { escapeHtml } from '../../utils/text'; +import { initButtonSelects } from '../../includes/initButtonSelects'; +import { initTooltips } from '../../includes/initTooltips'; /* generic function for adding a message to message area through JS alone */ function addMessage(status, text) { @@ -593,32 +594,6 @@ $(document).ready(initDropDowns); wagtail.ui.initDropDowns = initDropDowns; wagtail.ui.DropDownController = DropDownController; -// Initialise button selectors -function initButtonSelects() { - document.querySelectorAll('.button-select').forEach((element) => { - const inputElement = element.querySelector('input[type="hidden"]'); - - element - .querySelectorAll('.button-select__option') - .forEach((buttonElement) => { - buttonElement.addEventListener('click', (e) => { - e.preventDefault(); - inputElement.value = buttonElement.value; - - element - .querySelectorAll('.button-select__option--selected') - .forEach((selectedButtonElement) => { - selectedButtonElement.classList.remove( - 'button-select__option--selected', - ); - }); - - buttonElement.classList.add('button-select__option--selected'); - }); - }); - }); -} - $(document).ready(initButtonSelects); window.wagtail = wagtail; diff --git a/client/src/includes/initButtonSelects.ts b/client/src/includes/initButtonSelects.ts new file mode 100644 index 0000000000..daedebcc27 --- /dev/null +++ b/client/src/includes/initButtonSelects.ts @@ -0,0 +1,35 @@ +/** + * Initialise button selectors + */ +const initButtonSelects = () => { + document.querySelectorAll('.button-select').forEach((element) => { + const inputElement = element.querySelector( + 'input[type="hidden"]', + ) as HTMLInputElement; + + if (!inputElement) { + return; + } + + element + .querySelectorAll('.button-select__option') + .forEach((buttonElement) => { + buttonElement.addEventListener('click', (event) => { + event.preventDefault(); + inputElement.value = (buttonElement as HTMLButtonElement).value; + + element + .querySelectorAll('.button-select__option--selected') + .forEach((selectedButtonElement) => { + selectedButtonElement.classList.remove( + 'button-select__option--selected', + ); + }); + + buttonElement.classList.add('button-select__option--selected'); + }); + }); + }); +}; + +export { initButtonSelects }; diff --git a/client/src/includes/messages.test.js b/client/src/includes/messages.test.js new file mode 100644 index 0000000000..1fff2be6d8 --- /dev/null +++ b/client/src/includes/messages.test.js @@ -0,0 +1,77 @@ +import { initButtonSelects } from './initButtonSelects'; + +// save our DOM elements to a variable +const testElements = ` +
+ + + +
+`; + +describe('initButtonSelects', () => { + const spy = jest.spyOn(document, 'addEventListener'); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should do nothing if there is no button-select container', () => { + // Set up our document body + document.body.innerHTML = ` +
+ +
`; + initButtonSelects(); + // no event listeners registered + expect(spy).not.toHaveBeenCalled(); + }); + + describe('there is a button-select container present', () => { + it('should add class of button-select__option--selected to button-select__option when clicked', () => { + document.body.innerHTML = testElements; + initButtonSelects(); + document.querySelectorAll('.button-select__option').forEach((button) => { + button.click(); + expect(button.classList.value).toContain( + 'button-select__option--selected', + ); + }); + }); + + it('should remove the class button-select__option--selected when button is not clicked', () => { + document.body.innerHTML = testElements; + initButtonSelects(); + document.querySelectorAll('.button-select__option').forEach((button) => { + button.click(); + document + .querySelector('.button-select') + .querySelectorAll('.button-select__option--selected') + .forEach((selectedButtonElement) => { + selectedButtonElement.classList.remove( + 'button-select__option--selected', + ); + }); + expect(button.classList.value).not.toContain( + 'button-select__option--selected', + ); + }); + }); + it('add the value of the button clicked to the input value', () => { + document.body.innerHTML = testElements; + initButtonSelects(); + const inputElement = document.querySelector('input[type="hidden"]'); + // Checking that the input ellement has no value + expect(inputElement.value).toBeFalsy(); + document.querySelectorAll('.button-select__option').forEach((button) => { + button.click(); + expect(inputElement.value).toEqual(button.value); + }); + }); + }); +}); diff --git a/docs/releases/4.2.md b/docs/releases/4.2.md index 7a8e218337..b0ba4a6215 100644 --- a/docs/releases/4.2.md +++ b/docs/releases/4.2.md @@ -26,6 +26,7 @@ depth: 1 * Clean up duplicate JavaScript for the `escapeHtml` function (Jordan Rob) * 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) ### Bug fixes