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 9.2 kB
{"version":3,"file":"error-summary.mjs","sources":["../../../../src/govuk/components/error-summary/error-summary.mjs"],"sourcesContent":["import { ConfigurableComponent } from '../../common/configuration.mjs'\nimport { setFocus } from '../../common/index.mjs'\n\n/**\n * Error summary component\n *\n * Takes focus on initialisation for accessible announcement, unless disabled in\n * configuration.\n *\n * @preserve\n * @augments ConfigurableComponent<ErrorSummaryConfig>\n */\nexport class ErrorSummary extends ConfigurableComponent {\n /**\n * @param {Element | null} $root - HTML element to use for error summary\n * @param {ErrorSummaryConfig} [config] - Error summary config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n /**\n * Focus the error summary\n */\n if (!this.config.disableAutoFocus) {\n setFocus(this.$root)\n }\n\n this.$root.addEventListener('click', (event) => this.handleClick(event))\n }\n\n /**\n * Click event handler\n *\n * @private\n * @param {MouseEvent} event - Click event\n */\n handleClick(event) {\n const $target = event.target\n if ($target && this.focusTarget($target)) {\n event.preventDefault()\n }\n }\n\n /**\n * Focus the target element\n *\n * By default, the browser will scroll the target into view. Because our\n * labels or legends appear above the input, this means the user will be\n * presented with an input without any context, as the label or legend will be\n * off the top of the screen.\n *\n * Manually handling the click event, scrolling the question into view and\n * then focussing the element solves this.\n *\n * This also results in the label and/or legend being announced correctly in\n * NVDA (as tested in 2018.3.2) - without this only the field type is\n * announced (e.g. \"Edit, has autocomplete\").\n *\n * @private\n * @param {EventTarget} $target - Event target\n * @returns {boolean} True if the target was able to be focussed\n */\n focusTarget($target) {\n // If the element that was clicked was not a link, return early\n if (!($target instanceof HTMLAnchorElement)) {\n return false\n }\n\n const inputId = $target.hash.replace('#', '')\n if (!inputId) {\n return false\n }\n\n const $input = document.getElementById(inputId)\n if (!$input) {\n return false\n }\n\n const $legendOrLabel = this.getAssociatedLegendOrLabel($input)\n if (!$legendOrLabel) {\n return false\n }\n\n // Scroll the legend or label into view *before* calling focus on the input\n // to avoid extra scrolling in browsers that don't support `preventScroll`\n // (which at time of writing is most of them...)\n $legendOrLabel.scrollIntoView()\n $input.focus({ preventScroll: true })\n\n return true\n }\n\n /**\n * Get associated legend or label\n *\n * Returns the first element that exists from this list:\n *\n * - The `<legend>` associated with the closest `<fieldset>` ancestor, as long\n * as the top of it is no more than half a viewport height away from the\n * bottom of the input\n * - The first `<label>` that is associated with the input using for=\"inputId\"\n * - The closest parent `<label>`\n *\n * @private\n * @param {Element} $input - The input\n * @returns {Element | null} Associated legend or label, or null if no\n * associated legend or label can be found\n */\n getAssociatedLegendOrLabel($input) {\n const $fieldset = $input.closest('fieldset')\n\n if ($fieldset) {\n const $legends = $fieldset.getElementsByTagName('legend')\n\n if ($legends.length) {\n const $candidateLegend = $legends[0]\n\n // If the input type is radio or checkbox, always use the legend if\n // there is one.\n if (\n $input instanceof HTMLInputElement &&\n ($input.type === 'checkbox' || $input.type === 'radio')\n ) {\n return $candidateLegend\n }\n\n // For other input types, only scroll to the fieldset’s legend (instead\n // of the label associated with the input) if the input would end up in\n // the top half of the screen.\n //\n // This should avoid situations where the input either ends up off the\n // screen, or obscured by a software keyboard.\n const legendTop = $candidateLegend.getBoundingClientRect().top\n const inputRect = $input.getBoundingClientRect()\n\n // If the browser doesn't support Element.getBoundingClientRect().height\n // or window.innerHeight (like IE8), bail and just link to the label.\n if (inputRect.height && window.innerHeight) {\n const inputBottom = inputRect.top + inputRect.height\n\n if (inputBottom - legendTop < window.innerHeight / 2) {\n return $candidateLegend\n }\n }\n }\n }\n\n return (\n document.querySelector(`label[for='${$input.getAttribute('id')}']`) ??\n $input.closest('label')\n )\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-error-summary'\n\n /**\n * Error summary default config\n *\n * @see {@link ErrorSummaryConfig}\n * @constant\n * @type {ErrorSummaryConfig}\n */\n static defaults = Object.freeze({\n disableAutoFocus: false\n })\n\n /**\n * Error summary config schema\n *\n * @constant\n * @satisfies {Schema<ErrorSummaryConfig>}\n */\n static schema = Object.freeze({\n properties: {\n disableAutoFocus: { type: 'boolean' }\n }\n })\n}\n\n/**\n * Error summary config\n *\n * @typedef {object} ErrorSummaryConfig\n * @property {boolean} [disableAutoFocus=false] - If set to `true` the error\n * summary will not be focussed when the page loads.\n */\n\n/**\n * @import { Schema } from '../../common/configuration.mjs'\n */\n"],"names":["ErrorSummary","ConfigurableComponent","constructor","$root","config","disableAutoFocus","setFocus","addEventListener","event","handleClick","$target","target","focusTarget","preventDefault","HTMLAnchorElement","inputId","hash","replace","$input","document","getElementById","$legendOrLabel","getAssociatedLegendOrLabel","scrollIntoView","focus","preventScroll","_document$querySelect","$fieldset","closest","$legends","getElementsByTagName","length","$candidateLegend","HTMLInputElement","type","legendTop","getBoundingClientRect","top","inputRect","height","window","innerHeight","inputBottom","querySelector","getAttribute","moduleName","defaults","Object","freeze","schema","properties"],"mappings":"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"}