UNPKG

govuk-frontend

Version:

GOV.UK Frontend contains the code you need to start building a user interface for government platforms and services.

1 lines 10.9 kB
{"version":3,"file":"checkboxes.mjs","sources":["../../../../src/govuk/components/checkboxes/checkboxes.mjs"],"sourcesContent":["import { Component } from '../../component.mjs'\nimport { ElementError } from '../../errors/index.mjs'\n\n/**\n * Checkboxes component\n *\n * @preserve\n */\nexport class Checkboxes extends Component {\n /** @private */\n $inputs\n\n /**\n * Checkboxes can be associated with a 'conditionally revealed' content block\n * – for example, a checkbox for 'Phone' could reveal an additional form field\n * for the user to enter their phone number.\n *\n * These associations are made using a `data-aria-controls` attribute, which\n * is promoted to an aria-controls attribute during initialisation.\n *\n * We also need to restore the state of any conditional reveals on the page\n * (for example if the user has navigated back), and set up event handlers to\n * keep the reveal in sync with the checkbox state.\n *\n * @param {Element | null} $root - HTML element to use for checkboxes\n */\n constructor($root) {\n super($root)\n\n const $inputs = this.$root.querySelectorAll('input[type=\"checkbox\"]')\n if (!$inputs.length) {\n throw new ElementError({\n component: Checkboxes,\n identifier: 'Form inputs (`<input type=\"checkbox\">`)'\n })\n }\n\n this.$inputs = $inputs\n\n this.$inputs.forEach(($input) => {\n const targetId = $input.getAttribute('data-aria-controls')\n\n // Skip radios without data-aria-controls attributes\n if (!targetId) {\n return\n }\n\n // Throw if target conditional element does not exist.\n if (!document.getElementById(targetId)) {\n throw new ElementError({\n component: Checkboxes,\n identifier: `Conditional reveal (\\`id=\"${targetId}\"\\`)`\n })\n }\n\n // Promote the data-aria-controls attribute to a aria-controls attribute\n // so that the relationship is exposed in the AOM\n $input.setAttribute('aria-controls', targetId)\n $input.removeAttribute('data-aria-controls')\n })\n\n // When the page is restored after navigating 'back' in some browsers the\n // state of form controls is not restored until *after* the DOMContentLoaded\n // event is fired, so we need to sync after the pageshow event.\n window.addEventListener('pageshow', () => this.syncAllConditionalReveals())\n\n // Although we've set up handlers to sync state on the pageshow event, init\n // could be called after those events have fired, for example if they are\n // added to the page dynamically, so sync now too.\n this.syncAllConditionalReveals()\n\n // Handle events\n this.$root.addEventListener('click', (event) => this.handleClick(event))\n }\n\n /**\n * Sync the conditional reveal states for all checkboxes in this component.\n *\n * @private\n */\n syncAllConditionalReveals() {\n this.$inputs.forEach(($input) =>\n this.syncConditionalRevealWithInputState($input)\n )\n }\n\n /**\n * Sync conditional reveal with the input state\n *\n * Synchronise the visibility of the conditional reveal, and its accessible\n * state, with the input's checked state.\n *\n * @private\n * @param {HTMLInputElement} $input - Checkbox input\n */\n syncConditionalRevealWithInputState($input) {\n const targetId = $input.getAttribute('aria-controls')\n if (!targetId) {\n return\n }\n\n const $target = document.getElementById(targetId)\n if ($target?.classList.contains('govuk-checkboxes__conditional')) {\n const inputIsChecked = $input.checked\n\n $input.setAttribute('aria-expanded', inputIsChecked.toString())\n $target.classList.toggle(\n 'govuk-checkboxes__conditional--hidden',\n !inputIsChecked\n )\n }\n }\n\n /**\n * Uncheck other checkboxes\n *\n * Find any other checkbox inputs with the same name value, and uncheck them.\n * This is useful for when a “None of these\" checkbox is checked.\n *\n * @private\n * @param {HTMLInputElement} $input - Checkbox input\n */\n unCheckAllInputsExcept($input) {\n const allInputsWithSameName = document.querySelectorAll(\n `input[type=\"checkbox\"][name=\"${$input.name}\"]`\n )\n\n allInputsWithSameName.forEach(($inputWithSameName) => {\n const hasSameFormOwner = $input.form === $inputWithSameName.form\n if (hasSameFormOwner && $inputWithSameName !== $input) {\n $inputWithSameName.checked = false\n this.syncConditionalRevealWithInputState($inputWithSameName)\n }\n })\n }\n\n /**\n * Uncheck exclusive checkboxes\n *\n * Find any checkbox inputs with the same name value and the 'exclusive'\n * behaviour, and uncheck them. This helps prevent someone checking both a\n * regular checkbox and a \"None of these\" checkbox in the same fieldset.\n *\n * @private\n * @param {HTMLInputElement} $input - Checkbox input\n */\n unCheckExclusiveInputs($input) {\n const allInputsWithSameNameAndExclusiveBehaviour =\n document.querySelectorAll(\n `input[data-behaviour=\"exclusive\"][type=\"checkbox\"][name=\"${$input.name}\"]`\n )\n\n allInputsWithSameNameAndExclusiveBehaviour.forEach(($exclusiveInput) => {\n const hasSameFormOwner = $input.form === $exclusiveInput.form\n if (hasSameFormOwner) {\n $exclusiveInput.checked = false\n this.syncConditionalRevealWithInputState($exclusiveInput)\n }\n })\n }\n\n /**\n * Click event handler\n *\n * Handle a click within the component root – if the click occurred on a checkbox,\n * sync the state of any associated conditional reveal with the checkbox\n * state.\n *\n * @private\n * @param {MouseEvent} event - Click event\n */\n handleClick(event) {\n const $clickedInput = event.target\n\n // Ignore clicks on things that aren't checkbox inputs\n if (\n !($clickedInput instanceof HTMLInputElement) ||\n $clickedInput.type !== 'checkbox'\n ) {\n return\n }\n\n // If the checkbox conditionally-reveals some content, sync the state\n const hasAriaControls = $clickedInput.getAttribute('aria-controls')\n if (hasAriaControls) {\n this.syncConditionalRevealWithInputState($clickedInput)\n }\n\n // No further behaviour needed for unchecking\n if (!$clickedInput.checked) {\n return\n }\n\n // Handle 'exclusive' checkbox behaviour (ie \"None of these\")\n const hasBehaviourExclusive =\n $clickedInput.getAttribute('data-behaviour') === 'exclusive'\n if (hasBehaviourExclusive) {\n this.unCheckAllInputsExcept($clickedInput)\n } else {\n this.unCheckExclusiveInputs($clickedInput)\n }\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-checkboxes'\n}\n"],"names":["Checkboxes","Component","constructor","$root","$inputs","querySelectorAll","length","ElementError","component","identifier","forEach","$input","targetId","getAttribute","document","getElementById","setAttribute","removeAttribute","window","addEventListener","syncAllConditionalReveals","event","handleClick","syncConditionalRevealWithInputState","$target","classList","contains","inputIsChecked","checked","toString","toggle","unCheckAllInputsExcept","allInputsWithSameName","name","$inputWithSameName","hasSameFormOwner","form","unCheckExclusiveInputs","allInputsWithSameNameAndExclusiveBehaviour","$exclusiveInput","$clickedInput","target","HTMLInputElement","type","hasAriaControls","hasBehaviourExclusive","moduleName"],"mappings":"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"}