-
Notifications
You must be signed in to change notification settings - Fork 325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve public JavaScript API for code initialisation #5338
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
September 20, 2024 10:51
Inactive
📋 StatsFile sizes
Modules
View stats and visualisations on the review app Action run for 086734c |
JavaScript changes to npm packagediff --git a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
index 33b78bfdd..0416a210b 100644
--- a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
+++ b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
@@ -1,183 +1,214 @@
const version = "development";
-function normaliseString(e, t) {
- const n = e ? e.trim() : "";
- let i, s = null == t ? void 0 : t.type;
- switch (s || (["true", "false"].includes(n) && (s = "boolean"), n.length > 0 && isFinite(Number(n)) && (s = "number")), s) {
+function normaliseString(t, e) {
+ const s = t ? t.trim() : "";
+ let n, i = null == e ? void 0 : e.type;
+ switch (i || (["true", "false"].includes(s) && (i = "boolean"), s.length > 0 && isFinite(Number(s)) && (i = "number")), i) {
case "boolean":
- i = "true" === n;
+ n = "true" === s;
break;
case "number":
- i = Number(n);
+ n = Number(s);
break;
default:
- i = e
+ n = t
}
- return i
+ return n
}
-function mergeConfigs(...e) {
- const t = {};
- for (const n of e)
- for (const e of Object.keys(n)) {
- const i = t[e],
- s = n[e];
- isObject(i) && isObject(s) ? t[e] = mergeConfigs(i, s) : t[e] = s
+function mergeConfigs(...t) {
+ const e = {};
+ for (const s of t)
+ for (const t of Object.keys(s)) {
+ const n = e[t],
+ i = s[t];
+ isObject(n) && isObject(i) ? e[t] = mergeConfigs(n, i) : e[t] = i
}
- return t
+ return e
}
-function extractConfigByNamespace(e, t, n) {
- const i = e.schema.properties[n];
- if ("object" !== (null == i ? void 0 : i.type)) return;
- const s = {
- [n]: {}
+function extractConfigByNamespace(Component, t, e) {
+ const s = Component.schema.properties[e];
+ if ("object" !== (null == s ? void 0 : s.type)) return;
+ const n = {
+ [e]: {}
};
- for (const [o, r] of Object.entries(t)) {
- let e = s;
- const t = o.split(".");
- for (const [i, s] of t.entries()) "object" == typeof e && (i < t.length - 1 ? (isObject(e[s]) || (e[s] = {}), e = e[s]) : o !== n && (e[s] = normaliseString(r)))
+ for (const [i, o] of Object.entries(t)) {
+ let t = n;
+ const s = i.split(".");
+ for (const [n, r] of s.entries()) "object" == typeof t && (n < s.length - 1 ? (isObject(t[r]) || (t[r] = {}), t = t[r]) : i !== e && (t[r] = normaliseString(o)))
}
- return s[n]
+ return n[e]
}
-function getFragmentFromUrl(e) {
- if (e.includes("#")) return e.split("#").pop()
+function getFragmentFromUrl(t) {
+ if (t.includes("#")) return t.split("#").pop()
}
-function getBreakpoint(e) {
- const t = `--govuk-frontend-breakpoint-${e}`;
+function getBreakpoint(t) {
+ const e = `--govuk-frontend-breakpoint-${t}`;
return {
- property: t,
- value: window.getComputedStyle(document.documentElement).getPropertyValue(t) || void 0
+ property: e,
+ value: window.getComputedStyle(document.documentElement).getPropertyValue(e) || void 0
}
}
-function setFocus(e, t = {}) {
- var n;
- const i = e.getAttribute("tabindex");
+function setFocus(t, e = {}) {
+ var s;
+ const n = t.getAttribute("tabindex");
function onBlur() {
- var n;
- null == (n = t.onBlur) || n.call(e), i || e.removeAttribute("tabindex")
+ var s;
+ null == (s = e.onBlur) || s.call(t), n || t.removeAttribute("tabindex")
}
- i || e.setAttribute("tabindex", "-1"), e.addEventListener("focus", (function() {
- e.addEventListener("blur", onBlur, {
+ n || t.setAttribute("tabindex", "-1"), t.addEventListener("focus", (function() {
+ t.addEventListener("blur", onBlur, {
once: !0
})
}), {
once: !0
- }), null == (n = t.onBeforeFocus) || n.call(e), e.focus()
+ }), null == (s = e.onBeforeFocus) || s.call(t), t.focus()
}
-function isSupported(e = document.body) {
- return !!e && e.classList.contains("govuk-frontend-supported")
+function isSupported(t = document.body) {
+ return !!t && t.classList.contains("govuk-frontend-supported")
}
-function isObject(e) {
- return !!e && "object" == typeof e && ! function(e) {
- return Array.isArray(e)
- }(e)
+function isObject(t) {
+ return !!t && "object" == typeof t && ! function(t) {
+ return Array.isArray(t)
+ }(t)
}
-function normaliseDataset(e, t) {
- const n = {};
- for (const [i, s] of Object.entries(e.schema.properties)) i in t && (n[i] = normaliseString(t[i], s)), "object" === (null == s ? void 0 : s.type) && (n[i] = extractConfigByNamespace(e, t, i));
- return n
+function formatErrorMessage(Component, t) {
+ return `${Component.moduleName}: ${t}`
+}
+
+function normaliseDataset(Component, t) {
+ const e = {};
+ for (const [s, n] of Object.entries(Component.schema.properties)) s in t && (e[s] = normaliseString(t[s], n)), "object" === (null == n ? void 0 : n.type) && (e[s] = extractConfigByNamespace(Component, t, s));
+ return e
}
class GOVUKFrontendError extends Error {
- constructor(...e) {
- super(...e), this.name = "GOVUKFrontendError"
+ constructor(...t) {
+ super(...t), this.name = "GOVUKFrontendError"
}
}
class SupportError extends GOVUKFrontendError {
- constructor(e = document.body) {
- const t = "noModule" in HTMLScriptElement.prototype ? 'GOV.UK Frontend initialised without `<body class="govuk-frontend-supported">` from template `<script>` snippet' : "GOV.UK Frontend is not supported in this browser";
- super(e ? t : 'GOV.UK Frontend initialised without `<script type="module">`'), this.name = "SupportError"
+ constructor(t = document.body) {
+ const e = "noModule" in HTMLScriptElement.prototype ? 'GOV.UK Frontend initialised without `<body class="govuk-frontend-supported">` from template `<script>` snippet' : "GOV.UK Frontend is not supported in this browser";
+ super(t ? e : 'GOV.UK Frontend initialised without `<script type="module">`'), this.name = "SupportError"
}
}
class ConfigError extends GOVUKFrontendError {
- constructor(...e) {
- super(...e), this.name = "ConfigError"
+ constructor(...t) {
+ super(...t), this.name = "ConfigError"
}
}
class ElementError extends GOVUKFrontendError {
- constructor(e) {
- let t = "string" == typeof e ? e : "";
- if ("object" == typeof e) {
+ constructor(t) {
+ let e = "string" == typeof t ? t : "";
+ if ("object" == typeof t) {
const {
- componentName: n,
- identifier: i,
- element: s,
+ component: s,
+ identifier: n,
+ element: i,
expectedType: o
- } = e;
- t = `${n}: ${i}`, t += s ? ` is not of type ${null!=o?o:"HTMLElement"}` : " not found"
+ } = t;
+ e = n, e += i ? ` is not of type ${null!=o?o:"HTMLElement"}` : " not found", e = formatErrorMessage(s, e)
}
- super(t), this.name = "ElementError"
+ super(e), this.name = "ElementError"
}
}
-class GOVUKFrontendComponent {
- constructor() {
- this.checkSupport()
+class InitError extends GOVUKFrontendError {
+ constructor(t) {
+ super("string" == typeof t ? t : formatErrorMessage(t, "Root element (`$root`) already initialised")), this.name = "InitError"
}
- checkSupport() {
+}
+class GOVUKFrontendComponent {
+ get $root() {
+ return this._$root
+ }
+ constructor(t) {
+ this._$root = void 0;
+ const e = this.constructor;
+ if ("string" != typeof e.moduleName) throw new InitError("`moduleName` not defined in component");
+ if (!(t instanceof e.elementType)) throw new ElementError({
+ element: t,
+ component: e,
+ identifier: "Root element (`$root`)",
+ expectedType: e.elementType.name
+ });
+ this._$root = t, e.checkSupport(), this.checkInitialised();
+ const s = e.moduleName;
+ this.$root.setAttribute(`data-${s}-init`, "")
+ }
+ checkInitialised() {
+ const t = this.constructor,
+ e = t.moduleName;
+ if (e && function(t, e) {
+ return t instanceof HTMLElement && t.hasAttribute(`data-${e}-init`)
+ }(this.$root, e)) throw new InitError(t)
+ }
+ static checkSupport() {
if (!isSupported()) throw new SupportError
}
}
+GOVUKFrontendComponent.elementType = HTMLElement;
class I18n {
- constructor(e = {}, t = {}) {
- var n;
- this.translations = void 0, this.locale = void 0, this.translations = e, this.locale = null != (n = t.locale) ? n : document.documentElement.lang || "en"
- }
- t(e, t) {
- if (!e) throw new Error("i18n: lookup key missing");
- let n = this.translations[e];
- if ("number" == typeof(null == t ? void 0 : t.count) && "object" == typeof n) {
- const i = n[this.getPluralSuffix(e, t.count)];
- i && (n = i)
+ constructor(t = {}, e = {}) {
+ var s;
+ this.translations = void 0, this.locale = void 0, this.translations = t, this.locale = null != (s = e.locale) ? s : document.documentElement.lang || "en"
+ }
+ t(t, e) {
+ if (!t) throw new Error("i18n: lookup key missing");
+ let s = this.translations[t];
+ if ("number" == typeof(null == e ? void 0 : e.count) && "object" == typeof s) {
+ const n = s[this.getPluralSuffix(t, e.count)];
+ n && (s = n)
}
- if ("string" == typeof n) {
- if (n.match(/%{(.\S+)}/)) {
- if (!t) throw new Error("i18n: cannot replace placeholders in string if no option data provided");
- return this.replacePlaceholders(n, t)
+ if ("string" == typeof s) {
+ if (s.match(/%{(.\S+)}/)) {
+ if (!e) throw new Error("i18n: cannot replace placeholders in string if no option data provided");
+ return this.replacePlaceholders(s, e)
}
- return n
+ return s
}
- return e
- }
- replacePlaceholders(e, t) {
- const n = Intl.NumberFormat.supportedLocalesOf(this.locale).length ? new Intl.NumberFormat(this.locale) : void 0;
- return e.replace(/%{(.\S+)}/g, (function(e, i) {
- if (Object.prototype.hasOwnProperty.call(t, i)) {
- const e = t[i];
- return !1 === e || "number" != typeof e && "string" != typeof e ? "" : "number" == typeof e ? n ? n.format(e) : `${e}` : e
+ return t
+ }
+ replacePlaceholders(t, e) {
+ const s = Intl.NumberFormat.supportedLocalesOf(this.locale).length ? new Intl.NumberFormat(this.locale) : void 0;
+ return t.replace(/%{(.\S+)}/g, (function(t, n) {
+ if (Object.prototype.hasOwnProperty.call(e, n)) {
+ const t = e[n];
+ return !1 === t || "number" != typeof t && "string" != typeof t ? "" : "number" == typeof t ? s ? s.format(t) : `${t}` : t
}
- throw new Error(`i18n: no data found to replace ${e} placeholder in string`)
+ throw new Error(`i18n: no data found to replace ${t} placeholder in string`)
}))
}
hasIntlPluralRulesSupport() {
return Boolean("PluralRules" in window.Intl && Intl.PluralRules.supportedLocalesOf(this.locale).length)
}
- getPluralSuffix(e, t) {
- if (t = Number(t), !isFinite(t)) return "other";
- const n = this.translations[e],
- i = this.hasIntlPluralRulesSupport() ? new Intl.PluralRules(this.locale).select(t) : this.selectPluralFormUsingFallbackRules(t);
- if ("object" == typeof n) {
- if (i in n) return i;
- if ("other" in n) return console.warn(`i18n: Missing plural form ".${i}" for "${this.locale}" locale. Falling back to ".other".`), "other"
+ getPluralSuffix(t, e) {
+ if (e = Number(e), !isFinite(e)) return "other";
+ const s = this.translations[t],
+ n = this.hasIntlPluralRulesSupport() ? new Intl.PluralRules(this.locale).select(e) : this.selectPluralFormUsingFallbackRules(e);
+ if ("object" == typeof s) {
+ if (n in s) return n;
+ if ("other" in s) return console.warn(`i18n: Missing plural form ".${n}" for "${this.locale}" locale. Falling back to ".other".`), "other"
}
throw new Error(`i18n: Plural form ".other" is required for "${this.locale}" locale`)
}
- selectPluralFormUsingFallbackRules(e) {
- e = Math.abs(Math.floor(e));
- const t = this.getPluralRulesForLocale();
- return t ? I18n.pluralRules[t](e) : "other"
+ selectPluralFormUsingFallbackRules(t) {
+ t = Math.abs(Math.floor(t));
+ const e = this.getPluralRulesForLocale();
+ return e ? I18n.pluralRules[e](t) : "other"
}
getPluralRulesForLocale() {
- const e = this.locale.split("-")[0];
- for (const t in I18n.pluralRulesMap) {
- const n = I18n.pluralRulesMap[t];
- if (n.includes(this.locale) || n.includes(e)) return t
+ const t = this.locale.split("-")[0];
+ for (const e in I18n.pluralRulesMap) {
+ const s = I18n.pluralRulesMap[e];
+ if (s.includes(this.locale) || s.includes(t)) return e
}
}
}
@@ -192,151 +223,146 @@ I18n.pluralRulesMap = {
spanish: ["pt-PT", "it", "es"],
welsh: ["cy"]
}, I18n.pluralRules = {
- arabic: e => 0 === e ? "zero" : 1 === e ? "one" : 2 === e ? "two" : e % 100 >= 3 && e % 100 <= 10 ? "few" : e % 100 >= 11 && e % 100 <= 99 ? "many" : "other",
+ arabic: t => 0 === t ? "zero" : 1 === t ? "one" : 2 === t ? "two" : t % 100 >= 3 && t % 100 <= 10 ? "few" : t % 100 >= 11 && t % 100 <= 99 ? "many" : "other",
chinese: () => "other",
- french: e => 0 === e || 1 === e ? "one" : "other",
- german: e => 1 === e ? "one" : "other",
- irish: e => 1 === e ? "one" : 2 === e ? "two" : e >= 3 && e <= 6 ? "few" : e >= 7 && e <= 10 ? "many" : "other",
- russian(e) {
- const t = e % 100,
- n = t % 10;
- return 1 === n && 11 !== t ? "one" : n >= 2 && n <= 4 && !(t >= 12 && t <= 14) ? "few" : 0 === n || n >= 5 && n <= 9 || t >= 11 && t <= 14 ? "many" : "other"
+ french: t => 0 === t || 1 === t ? "one" : "other",
+ german: t => 1 === t ? "one" : "other",
+ irish: t => 1 === t ? "one" : 2 === t ? "two" : t >= 3 && t <= 6 ? "few" : t >= 7 && t <= 10 ? "many" : "other",
+ russian(t) {
+ const e = t % 100,
+ s = e % 10;
+ return 1 === s && 11 !== e ? "one" : s >= 2 && s <= 4 && !(e >= 12 && e <= 14) ? "few" : 0 === s || s >= 5 && s <= 9 || e >= 11 && e <= 14 ? "many" : "other"
},
- scottish: e => 1 === e || 11 === e ? "one" : 2 === e || 12 === e ? "two" : e >= 3 && e <= 10 || e >= 13 && e <= 19 ? "few" : "other",
- spanish: e => 1 === e ? "one" : e % 1e6 == 0 && 0 !== e ? "many" : "other",
- welsh: e => 0 === e ? "zero" : 1 === e ? "one" : 2 === e ? "two" : 3 === e ? "few" : 6 === e ? "many" : "other"
+ scottish: t => 1 === t || 11 === t ? "one" : 2 === t || 12 === t ? "two" : t >= 3 && t <= 10 || t >= 13 && t <= 19 ? "few" : "other",
+ spanish: t => 1 === t ? "one" : t % 1e6 == 0 && 0 !== t ? "many" : "other",
+ welsh: t => 0 === t ? "zero" : 1 === t ? "one" : 2 === t ? "two" : 3 === t ? "few" : 6 === t ? "many" : "other"
};
class Accordion extends GOVUKFrontendComponent {
- constructor(e, t = {}) {
- if (super(), this.$module = void 0, this.config = void 0, this.i18n = void 0, this.controlsClass = "govuk-accordion__controls", this.showAllClass = "govuk-accordion__show-all", this.showAllTextClass = "govuk-accordion__show-all-text", this.sectionClass = "govuk-accordion__section", this.sectionExpandedClass = "govuk-accordion__section--expanded", this.sectionButtonClass = "govuk-accordion__section-button", this.sectionHeaderClass = "govuk-accordion__section-header", this.sectionHeadingClass = "govuk-accordion__section-heading", this.sectionHeadingDividerClass = "govuk-accordion__section-heading-divider", this.sectionHeadingTextClass = "govuk-accordion__section-heading-text", this.sectionHeadingTextFocusClass = "govuk-accordion__section-heading-text-focus", this.sectionShowHideToggleClass = "govuk-accordion__section-toggle", this.sectionShowHideToggleFocusClass = "govuk-accordion__section-toggle-focus", this.sectionShowHideTextClass = "govuk-accordion__section-toggle-text", this.upChevronIconClass = "govuk-accordion-nav__chevron", this.downChevronIconClass = "govuk-accordion-nav__chevron--down", this.sectionSummaryClass = "govuk-accordion__section-summary", this.sectionSummaryFocusClass = "govuk-accordion__section-summary-focus", this.sectionContentClass = "govuk-accordion__section-content", this.$sections = void 0, this.$showAllButton = null, this.$showAllIcon = null, this.$showAllText = null, !(e instanceof HTMLElement)) throw new ElementError({
- componentName: "Accordion",
- element: e,
- identifier: "Root element (`$module`)"
- });
- this.$module = e, this.config = mergeConfigs(Accordion.defaults, t, normaliseDataset(Accordion, e.dataset)), this.i18n = new I18n(this.config.i18n);
- const n = this.$module.querySelectorAll(`.${this.sectionClass}`);
- if (!n.length) throw new ElementError({
- componentName: "Accordion",
+ constructor(t, e = {}) {
+ super(t), this.config = void 0, this.i18n = void 0, this.controlsClass = "govuk-accordion__controls", this.showAllClass = "govuk-accordion__show-all", this.showAllTextClass = "govuk-accordion__show-all-text", this.sectionClass = "govuk-accordion__section", this.sectionExpandedClass = "govuk-accordion__section--expanded", this.sectionButtonClass = "govuk-accordion__section-button", this.sectionHeaderClass = "govuk-accordion__section-header", this.sectionHeadingClass = "govuk-accordion__section-heading", this.sectionHeadingDividerClass = "govuk-accordion__section-heading-divider", this.sectionHeadingTextClass = "govuk-accordion__section-heading-text", this.sectionHeadingTextFocusClass = "govuk-accordion__section-heading-text-focus", this.sectionShowHideToggleClass = "govuk-accordion__section-toggle", this.sectionShowHideToggleFocusClass = "govuk-accordion__section-toggle-focus", this.sectionShowHideTextClass = "govuk-accordion__section-toggle-text", this.upChevronIconClass = "govuk-accordion-nav__chevron", this.downChevronIconClass = "govuk-accordion-nav__chevron--down", this.sectionSummaryClass = "govuk-accordion__section-summary", this.sectionSummaryFocusClass = "govuk-accordion__section-summary-focus", this.sectionContentClass = "govuk-accordion__section-content", this.$sections = void 0, this.$showAllButton = null, this.$showAllIcon = null, this.$showAllText = null, this.config = mergeConfigs(Accordion.defaults, e, normaliseDataset(Accordion, this.$root.dataset)), this.i18n = new I18n(this.config.i18n);
+ const s = this.$root.querySelectorAll(`.${this.sectionClass}`);
+ if (!s.length) throw new ElementError({
+ component: Accordion,
identifier: `Sections (\`<div class="${this.sectionClass}">\`)`
});
- this.$sections = n, this.initControls(), this.initSectionHeaders(), this.updateShowAllButton(this.areAllSectionsOpen())
+ this.$sections = s, this.initControls(), this.initSectionHeaders(), this.updateShowAllButton(this.areAllSectionsOpen())
}
initControls() {
this.$showAllButton = document.createElement("button"), this.$showAllButton.setAttribute("type", "button"), this.$showAllButton.setAttribute("class", this.showAllClass), this.$showAllButton.setAttribute("aria-expanded", "false"), this.$showAllIcon = document.createElement("span"), this.$showAllIcon.classList.add(this.upChevronIconClass), this.$showAllButton.appendChild(this.$showAllIcon);
- const e = document.createElement("div");
- e.setAttribute("class", this.controlsClass), e.appendChild(this.$showAllButton), this.$module.insertBefore(e, this.$module.firstChild), this.$showAllText = document.createElement("span"), this.$showAllText.classList.add(this.showAllTextClass), this.$showAllButton.appendChild(this.$showAllText), this.$showAllButton.addEventListener("click", (() => this.onShowOrHideAllToggle())), "onbeforematch" in document && document.addEventListener("beforematch", (e => this.onBeforeMatch(e)))
+ const t = document.createElement("div");
+ t.setAttribute("class", this.controlsClass), t.appendChild(this.$showAllButton), this.$root.insertBefore(t, this.$root.firstChild), this.$showAllText = document.createElement("span"), this.$showAllText.classList.add(this.showAllTextClass), this.$showAllButton.appendChild(this.$showAllText), this.$showAllButton.addEventListener("click", (() => this.onShowOrHideAllToggle())), "onbeforematch" in document && document.addEventListener("beforematch", (t => this.onBeforeMatch(t)))
}
initSectionHeaders() {
- this.$sections.forEach(((e, t) => {
- const n = e.querySelector(`.${this.sectionHeaderClass}`);
- if (!n) throw new ElementError({
- componentName: "Accordion",
+ this.$sections.forEach(((t, e) => {
+ const s = t.querySelector(`.${this.sectionHeaderClass}`);
+ if (!s) throw new ElementError({
+ component: Accordion,
identifier: `Section headers (\`<div class="${this.sectionHeaderClass}">\`)`
});
- this.constructHeaderMarkup(n, t), this.setExpanded(this.isExpanded(e), e), n.addEventListener("click", (() => this.onSectionToggle(e))), this.setInitialState(e)
+ this.constructHeaderMarkup(s, e), this.setExpanded(this.isExpanded(t), t), s.addEventListener("click", (() => this.onSectionToggle(t))), this.setInitialState(t)
}))
}
- constructHeaderMarkup(e, t) {
- const n = e.querySelector(`.${this.sectionButtonClass}`),
- i = e.querySelector(`.${this.sectionHeadingClass}`),
- s = e.querySelector(`.${this.sectionSummaryClass}`);
- if (!i) throw new ElementError({
- componentName: "Accordion",
+ constructHeaderMarkup(t, e) {
+ const s = t.querySelector(`.${this.sectionButtonClass}`),
+ n = t.querySelector(`.${this.sectionHeadingClass}`),
+ i = t.querySelector(`.${this.sectionSummaryClass}`);
+ if (!n) throw new ElementError({
+ component: Accordion,
identifier: `Section heading (\`.${this.sectionHeadingClass}\`)`
});
- if (!n) throw new ElementError({
- componentName: "Accordion",
+ if (!s) throw new ElementError({
+ component: Accordion,
identifier: `Section button placeholder (\`<span class="${this.sectionButtonClass}">\`)`
});
const o = document.createElement("button");
- o.setAttribute("type", "button"), o.setAttribute("aria-controls", `${this.$module.id}-content-${t+1}`);
- for (const d of Array.from(n.attributes)) "id" !== d.name && o.setAttribute(d.name, d.value);
+ o.setAttribute("type", "button"), o.setAttribute("aria-controls", `${this.$root.id}-content-${e+1}`);
+ for (const d of Array.from(s.attributes)) "id" !== d.name && o.setAttribute(d.name, d.value);
const r = document.createElement("span");
- r.classList.add(this.sectionHeadingTextClass), r.id = n.id;
+ r.classList.add(this.sectionHeadingTextClass), r.id = s.id;
const a = document.createElement("span");
- a.classList.add(this.sectionHeadingTextFocusClass), r.appendChild(a), Array.from(n.childNodes).forEach((e => a.appendChild(e)));
- const l = document.createElement("span");
- l.classList.add(this.sectionShowHideToggleClass), l.setAttribute("data-nosnippet", "");
+ a.classList.add(this.sectionHeadingTextFocusClass), r.appendChild(a), Array.from(s.childNodes).forEach((t => a.appendChild(t)));
const c = document.createElement("span");
- c.classList.add(this.sectionShowHideToggleFocusClass), l.appendChild(c);
+ c.classList.add(this.sectionShowHideToggleClass), c.setAttribute("data-nosnippet", "");
+ const l = document.createElement("span");
+ l.classList.add(this.sectionShowHideToggleFocusClass), c.appendChild(l);
const h = document.createElement("span"),
u = document.createElement("span");
- if (u.classList.add(this.upChevronIconClass), c.appendChild(u), h.classList.add(this.sectionShowHideTextClass), c.appendChild(h), o.appendChild(r), o.appendChild(this.getButtonPunctuationEl()), s) {
- const e = document.createElement("span"),
- t = document.createElement("span");
- t.classList.add(this.sectionSummaryFocusClass), e.appendChild(t);
- for (const n of Array.from(s.attributes)) e.setAttribute(n.name, n.value);
- Array.from(s.childNodes).forEach((e => t.appendChild(e))), s.remove(), o.appendChild(e), o.appendChild(this.getButtonPunctuationEl())
+ if (u.classList.add(this.upChevronIconClass), l.appendChild(u), h.classList.add(this.sectionShowHideTextClass), l.appendChild(h), o.appendChild(r), o.appendChild(this.getButtonPunctuationEl()), i) {
+ const t = document.createElement("span"),
+ e = document.createElement("span");
+ e.classList.add(this.sectionSummaryFocusClass), t.appendChild(e);
+ for (const s of Array.from(i.attributes)) t.setAttribute(s.name, s.value);
+ Array.from(i.childNodes).forEach((t => e.appendChild(t))), i.remove(), o.appendChild(t), o.appendChild(this.getButtonPunctuationEl())
}
- o.appendChild(l), i.removeChild(n), i.appendChild(o)
+ o.appendChild(c), n.removeChild(s), n.appendChild(o)
}
- onBeforeMatch(e) {
- const t = e.target;
- if (!(t instanceof Element)) return;
- const n = t.closest(`.${this.sectionClass}`);
- n && this.setExpanded(!0, n)
+ onBeforeMatch(t) {
+ const e = t.target;
+ if (!(e instanceof Element)) return;
+ const s = e.closest(`.${this.sectionClass}`);
+ s && this.setExpanded(!0, s)
}
- onSectionToggle(e) {
- const t = !this.isExpanded(e);
- this.setExpanded(t, e), this.storeState(e, t)
+ onSectionToggle(t) {
+ const e = !this.isExpanded(t);
+ this.setExpanded(e, t), this.storeState(t, e)
}
onShowOrHideAllToggle() {
- const e = !this.areAllSectionsOpen();
- this.$sections.forEach((t => {
- this.setExpanded(e, t), this.storeState(t, e)
- })), this.updateShowAllButton(e)
- }
- setExpanded(e, t) {
- const n = t.querySelector(`.${this.upChevronIconClass}`),
- i = t.querySelector(`.${this.sectionShowHideTextClass}`),
- s = t.querySelector(`.${this.sectionButtonClass}`),
- o = t.querySelector(`.${this.sectionContentClass}`);
+ const t = !this.areAllSectionsOpen();
+ this.$sections.forEach((e => {
+ this.setExpanded(t, e), this.storeState(e, t)
+ })), this.updateShowAllButton(t)
+ }
+ setExpanded(t, e) {
+ const s = e.querySelector(`.${this.upChevronIconClass}`),
+ n = e.querySelector(`.${this.sectionShowHideTextClass}`),
+ i = e.querySelector(`.${this.sectionButtonClass}`),
+ o = e.querySelector(`.${this.sectionContentClass}`);
if (!o) throw new ElementError({
- componentName: "Accordion",
+ component: Accordion,
identifier: `Section content (\`<div class="${this.sectionContentClass}">\`)`
});
- if (!n || !i || !s) return;
- const r = e ? this.i18n.t("hideSection") : this.i18n.t("showSection");
- i.textContent = r, s.setAttribute("aria-expanded", `${e}`);
+ if (!s || !n || !i) return;
+ const r = t ? this.i18n.t("hideSection") : this.i18n.t("showSection");
+ n.textContent = r, i.setAttribute("aria-expanded", `${t}`);
const a = [],
- l = t.querySelector(`.${this.sectionHeadingTextClass}`);
- l && a.push(`${l.textContent}`.trim());
- const c = t.querySelector(`.${this.sectionSummaryClass}`);
+ c = e.querySelector(`.${this.sectionHeadingTextClass}`);
c && a.push(`${c.textContent}`.trim());
- const h = e ? this.i18n.t("hideSectionAriaLabel") : this.i18n.t("showSectionAriaLabel");
- a.push(h), s.setAttribute("aria-label", a.join(" , ")), e ? (o.removeAttribute("hidden"), t.classList.add(this.sectionExpandedClass), n.classList.remove(this.downChevronIconClass)) : (o.setAttribute("hidden", "until-found"), t.classList.remove(this.sectionExpandedClass), n.classList.add(this.downChevronIconClass)), this.updateShowAllButton(this.areAllSectionsOpen())
+ const l = e.querySelector(`.${this.sectionSummaryClass}`);
+ l && a.push(`${l.textContent}`.trim());
+ const h = t ? this.i18n.t("hideSectionAriaLabel") : this.i18n.t("showSectionAriaLabel");
+ a.push(h), i.setAttribute("aria-label", a.join(" , ")), t ? (o.removeAttribute("hidden"), e.classList.add(this.sectionExpandedClass), s.classList.remove(this.downChevronIconClass)) : (o.setAttribute("hidden", "until-found"), e.classList.remove(this.sectionExpandedClass), s.classList.add(this.downChevronIconClass)), this.updateShowAllButton(this.areAllSectionsOpen())
}
- isExpanded(e) {
- return e.classList.contains(this.sectionExpandedClass)
+ isExpanded(t) {
+ return t.classList.contains(this.sectionExpandedClass)
}
areAllSectionsOpen() {
- return Array.from(this.$sections).every((e => this.isExpanded(e)))
+ return Array.from(this.$sections).every((t => this.isExpanded(t)))
}
- updateShowAllButton(e) {
- this.$showAllButton && this.$showAllText && this.$showAllIcon && (this.$showAllButton.setAttribute("aria-expanded", e.toString()), this.$showAllText.textContent = e ? this.i18n.t("hideAllSections") : this.i18n.t("showAllSections"), this.$showAllIcon.classList.toggle(this.downChevronIconClass, !e))
+ updateShowAllButton(t) {
+ this.$showAllButton && this.$showAllText && this.$showAllIcon && (this.$showAllButton.setAttribute("aria-expanded", t.toString()), this.$showAllText.textContent = t ? this.i18n.t("hideAllSections") : this.i18n.t("showAllSections"), this.$showAllIcon.classList.toggle(this.downChevronIconClass, !t))
}
- getIdentifier(e) {
- const t = e.querySelector(`.${this.sectionButtonClass}`);
- return null == t ? void 0 : t.getAttribute("aria-controls")
+ getIdentifier(t) {
+ const e = t.querySelector(`.${this.sectionButtonClass}`);
+ return null == e ? void 0 : e.getAttribute("aria-controls")
}
- storeState(e, t) {
+ storeState(t, e) {
if (!this.config.rememberExpanded) return;
- const n = this.getIdentifier(e);
- if (n) try {
- window.sessionStorage.setItem(n, t.toString())
- } catch (i) {}
+ const s = this.getIdentifier(t);
+ if (s) try {
+ window.sessionStorage.setItem(s, e.toString())
+ } catch (n) {}
}
- setInitialState(e) {
+ setInitialState(t) {
if (!this.config.rememberExpanded) return;
- const t = this.getIdentifier(e);
- if (t) try {
- const n = window.sessionStorage.getItem(t);
- null !== n && this.setExpanded("true" === n, e)
- } catch (n) {}
+ const e = this.getIdentifier(t);
+ if (e) try {
+ const s = window.sessionStorage.getItem(e);
+ null !== s && this.setExpanded("true" === s, t)
+ } catch (s) {}
}
getButtonPunctuationEl() {
- const e = document.createElement("span");
- return e.classList.add("govuk-visually-hidden", this.sectionHeadingDividerClass), e.textContent = ", ", e
+ const t = document.createElement("span");
+ return t.classList.add("govuk-visually-hidden", this.sectionHeadingDividerClass), t.textContent = ", ", t
}
}
Accordion.moduleName = "govuk-accordion", Accordion.defaults = Object.freeze({
@@ -360,28 +386,23 @@ Accordion.moduleName = "govuk-accordion", Accordion.defaults = Object.freeze({
}
});
class Button extends GOVUKFrontendComponent {
- constructor(e, t = {}) {
- if (super(), this.$module = void 0, this.config = void 0, this.debounceFormSubmitTimer = null, !(e instanceof HTMLElement)) throw new ElementError({
- componentName: "Button",
- element: e,
- identifier: "Root element (`$module`)"
- });
- this.$module = e, this.config = mergeConfigs(Button.defaults, t, normaliseDataset(Button, e.dataset)), this.$module.addEventListener("keydown", (e => this.handleKeyDown(e))), this.$module.addEventListener("click", (e => this.debounce(e)))
+ constructor(t, e = {}) {
+ super(t), this.config = void 0, this.debounceFormSubmitTimer = null, this.config = mergeConfigs(Button.defaults, e, normaliseDataset(Button, this.$root.dataset)), this.$root.addEventListener("keydown", (t => this.handleKeyDown(t))), this.$root.addEventListener("click", (t => this.debounce(t)))
}
- handleKeyDown(e) {
- const t = e.target;
- " " === e.key && t instanceof HTMLElement && "button" === t.getAttribute("role") && (e.preventDefault(), t.click())
+ handleKeyDown(t) {
+ const e = t.target;
+ " " === t.key && e instanceof HTMLElement && "button" === e.getAttribute("role") && (t.preventDefault(), e.click())
}
- debounce(e) {
- if (this.config.preventDoubleClick) return this.debounceFormSubmitTimer ? (e.preventDefault(), !1) : void(this.debounceFormSubmitTimer = window.setTimeout((() => {
+ debounce(t) {
+ if (this.config.preventDoubleClick) return this.debounceFormSubmitTimer ? (t.preventDefault(), !1) : void(this.debounceFormSubmitTimer = window.setTimeout((() => {
this.debounceFormSubmitTimer = null
}), 1e3))
}
}
-function closestAttributeValue(e, t) {
- const n = e.closest(`[${t}]`);
- return n ? n.getAttribute(t) : null
+function closestAttributeValue(t, e) {
+ const s = t.closest(`[${e}]`);
+ return s ? s.getAttribute(e) : null
}
Button.moduleName = "govuk-button", Button.defaults = Object.freeze({
preventDoubleClick: !1
@@ -393,59 +414,55 @@ Button.moduleName = "govuk-button", Button.defaults = Object.freeze({
}
});
class CharacterCount extends GOVUKFrontendComponent {
- constructor(e, t = {}) {
- var n, i;
- if (super(), this.$module = void 0, this.$textarea = void 0, this.$visibleCountMessage = void 0, this.$screenReaderCountMessage = void 0, this.lastInputTimestamp = null, this.lastInputValue = "", this.valueChecker = null, this.config = void 0, this.i18n = void 0, this.maxLength = void 0, !(e instanceof HTMLElement)) throw new ElementError({
- componentName: "Character count",
- element: e,
- identifier: "Root element (`$module`)"
- });
- const s = e.querySelector(".govuk-js-character-count");
- if (!(s instanceof HTMLTextAreaElement || s instanceof HTMLInputElement)) throw new ElementError({
- componentName: "Character count",
- element: s,
+ constructor(t, e = {}) {
+ var s, n;
+ super(t), this.$textarea = void 0, this.$visibleCountMessage = void 0, this.$screenReaderCountMessage = void 0, this.lastInputTimestamp = null, this.lastInputValue = "", this.valueChecker = null, this.config = void 0, this.i18n = void 0, this.maxLength = void 0;
+ const i = this.$root.querySelector(".govuk-js-character-count");
+ if (!(i instanceof HTMLTextAreaElement || i instanceof HTMLInputElement)) throw new ElementError({
+ component: CharacterCount,
+ element: i,
expectedType: "HTMLTextareaElement or HTMLInputElement",
identifier: "Form field (`.govuk-js-character-count`)"
});
- const o = normaliseDataset(CharacterCount, e.dataset);
+ const o = normaliseDataset(CharacterCount, this.$root.dataset);
let r = {};
("maxwords" in o || "maxlength" in o) && (r = {
maxlength: void 0,
maxwords: void 0
- }), this.config = mergeConfigs(CharacterCount.defaults, t, r, o);
- const a = function(e, t) {
- const n = [];
- for (const [i, s] of Object.entries(e)) {
- const e = [];
- if (Array.isArray(s)) {
+ }), this.config = mergeConfigs(CharacterCount.defaults, e, r, o);
+ const a = function(t, e) {
+ const s = [];
+ for (const [n, i] of Object.entries(t)) {
+ const t = [];
+ if (Array.isArray(i)) {
for (const {
- required: n,
- errorMessage: i
+ required: s,
+ errorMessage: n
}
- of s) n.every((e => !!t[e])) || e.push(i);
- "anyOf" !== i || s.length - e.length >= 1 || n.push(...e)
+ of i) s.every((t => !!e[t])) || t.push(n);
+ "anyOf" !== n || i.length - t.length >= 1 || s.push(...t)
}
}
- return n
+ return s
}(CharacterCount.schema, this.config);
- if (a[0]) throw new ConfigError(`Character count: ${a[0]}`);
+ if (a[0]) throw new ConfigError(formatErrorMessage(CharacterCount, a[0]));
this.i18n = new I18n(this.config.i18n, {
- locale: closestAttributeValue(e, "lang")
- }), this.maxLength = null != (n = null != (i = this.config.maxwords) ? i : this.config.maxlength) ? n : 1 / 0, this.$module = e, this.$textarea = s;
- const l = `${this.$textarea.id}-info`,
- c = document.getElementById(l);
- if (!c) throw new ElementError({
- componentName: "Character count",
- element: c,
- identifier: `Count message (\`id="${l}"\`)`
+ locale: closestAttributeValue(this.$root, "lang")
+ }), this.maxLength = null != (s = null != (n = this.config.maxwords) ? n : this.config.maxlength) ? s : 1 / 0, this.$textarea = i;
+ const c = `${this.$textarea.id}-info`,
+ l = document.getElementById(c);
+ if (!l) throw new ElementError({
+ component: CharacterCount,
+ element: l,
+ identifier: `Count message (\`id="${c}"\`)`
});
- `${c.textContent}`.match(/^\s*$/) && (c.textContent = this.i18n.t("textareaDescription", {
+ `${l.textContent}`.match(/^\s*$/) && (l.textContent = this.i18n.t("textareaDescription", {
count: this.maxLength
- })), this.$textarea.insertAdjacentElement("afterend", c);
+ })), this.$textarea.insertAdjacentElement("afterend", l);
const h = document.createElement("div");
- h.className = "govuk-character-count__sr-status govuk-visually-hidden", h.setAttribute("aria-live", "polite"), this.$screenReaderCountMessage = h, c.insertAdjacentElement("afterend", h);
+ h.className = "govuk-character-count__sr-status govuk-visually-hidden", h.setAttribute("aria-live", "polite"), this.$screenReaderCountMessage = h, l.insertAdjacentElement("afterend", h);
const u = document.createElement("div");
- u.className = c.className, u.classList.add("govuk-character-count__status"), u.setAttribute("aria-hidden", "true"), this.$visibleCountMessage = u, c.insertAdjacentElement("afterend", u), c.classList.add("govuk-visually-hidden"), this.$textarea.removeAttribute("maxlength"), this.bindChangeEvents(), window.addEventListener("pageshow", (() => this.updateCountMessage())), this.updateCountMessage()
+ u.className = l.className, u.classList.add("govuk-character-count__status"), u.setAttribute("aria-hidden", "true"), this.$visibleCountMessage = u, l.insertAdjacentElement("afterend", u), l.classList.add("govuk-visually-hidden"), this.$textarea.removeAttribute("maxlength"), this.bindChangeEvents(), window.addEventListener("pageshow", (() => this.updateCountMessage())), this.updateCountMessage()
}
bindChangeEvents() {
this.$textarea.addEventListener("keyup", (() => this.handleKeyUp())), this.$textarea.addEventListener("focus", (() => this.handleFocus())), this.$textarea.addEventListener("blur", (() => this.handleBlur()))
@@ -468,35 +485,35 @@ class CharacterCount extends GOVUKFrontendComponent {
this.updateVisibleCountMessage(), this.updateScreenReaderCountMessage()
}
updateVisibleCountMessage() {
- const e = this.maxLength - this.count(this.$textarea.value) < 0;
- this.$visibleCountMessage.classList.toggle("govuk-character-count__message--disabled", !this.isOverThreshold()), this.$textarea.classList.toggle("govuk-textarea--error", e), this.$visibleCountMessage.classList.toggle("govuk-error-message", e), this.$visibleCountMessage.classList.toggle("govuk-hint", !e), this.$visibleCountMessage.textContent = this.getCountMessage()
+ const t = this.maxLength - this.count(this.$textarea.value) < 0;
+ this.$visibleCountMessage.classList.toggle("govuk-character-count__message--disabled", !this.isOverThreshold()), this.$textarea.classList.toggle("govuk-textarea--error", t), this.$visibleCountMessage.classList.toggle("govuk-error-message", t), this.$visibleCountMessage.classList.toggle("govuk-hint", !t), this.$visibleCountMessage.textContent = this.getCountMessage()
}
updateScreenReaderCountMessage() {
this.isOverThreshold() ? this.$screenReaderCountMessage.removeAttribute("aria-hidden") : this.$screenReaderCountMessage.setAttribute("aria-hidden", "true"), this.$screenReaderCountMessage.textContent = this.getCountMessage()
}
- count(e) {
+ count(t) {
if (this.config.maxwords) {
- var t;
- return (null != (t = e.match(/\S+/g)) ? t : []).length
+ var e;
+ return (null != (e = t.match(/\S+/g)) ? e : []).length
}
- return e.length
+ return t.length
}
getCountMessage() {
- const e = this.maxLength - this.count(this.$textarea.value),
- t = this.config.maxwords ? "words" : "characters";
- return this.formatCountMessage(e, t)
- }
- formatCountMessage(e, t) {
- if (0 === e) return this.i18n.t(`${t}AtLimit`);
- const n = e < 0 ? "OverLimit" : "UnderLimit";
- return this.i18n.t(`${t}${n}`, {
- count: Math.abs(e)
+ const t = this.maxLength - this.count(this.$textarea.value),
+ e = this.config.maxwords ? "words" : "characters";
+ return this.formatCountMessage(t, e)
+ }
+ formatCountMessage(t, e) {
+ if (0 === t) return this.i18n.t(`${e}AtLimit`);
+ const s = t < 0 ? "OverLimit" : "UnderLimit";
+ return this.i18n.t(`${e}${s}`, {
+ count: Math.abs(t)
})
}
isOverThreshold() {
if (!this.config.threshold) return !0;
- const e = this.count(this.$textarea.value);
- return this.maxLength * this.config.threshold / 100 <= e
+ const t = this.count(this.$textarea.value);
+ return this.maxLength * this.config.threshold / 100 <= t
}
}
CharacterCount.moduleName = "govuk-character-count", CharacterCount.defaults = Object.freeze({
@@ -548,98 +565,89 @@ CharacterCount.moduleName = "govuk-character-count", CharacterCount.defaults = O
}]
});
class Checkboxes extends GOVUKFrontendComponent {
- constructor(e) {
- if (super(), this.$module = void 0, this.$inputs = void 0, !(e instanceof HTMLElement)) throw new ElementError({
- componentName: "Checkboxes",
- element: e,
- identifier: "Root element (`$module`)"
- });
- const t = e.querySelectorAll('input[type="checkbox"]');
- if (!t.length) throw new ElementError({
- componentName: "Checkboxes",
+ constructor(t) {
+ super(t), this.$inputs = void 0;
+ const e = this.$root.querySelectorAll('input[type="checkbox"]');
+ if (!e.length) throw new ElementError({
+ component: Checkboxes,
identifier: 'Form inputs (`<input type="checkbox">`)'
});
- this.$module = e, this.$inputs = t, this.$inputs.forEach((e => {
- const t = e.getAttribute("data-aria-controls");
- if (t) {
- if (!document.getElementById(t)) throw new ElementError({
- componentName: "Checkboxes",
- identifier: `Conditional reveal (\`id="${t}"\`)`
+ this.$inputs = e, this.$inputs.forEach((t => {
+ const e = t.getAttribute("data-aria-controls");
+ if (e) {
+ if (!document.getElementById(e)) throw new ElementError({
+ component: Checkboxes,
+ identifier: `Conditional reveal (\`id="${e}"\`)`
});
- e.setAttribute("aria-controls", t), e.removeAttribute("data-aria-controls")
+ t.setAttribute("aria-controls", e), t.removeAttribute("data-aria-controls")
}
- })), window.addEventListener("pageshow", (() => this.syncAllConditionalReveals())), this.syncAllConditionalReveals(), this.$module.addEventListener("click", (e => this.handleClick(e)))
+ })), window.addEventListener("pageshow", (() => this.syncAllConditionalReveals())), this.syncAllConditionalReveals(), this.$root.addEventListener("click", (t => this.handleClick(t)))
}
syncAllConditionalReveals() {
- this.$inputs.forEach((e => this.syncConditionalRevealWithInputState(e)))
- }
- syncConditionalRevealWithInputState(e) {
- const t = e.getAttribute("aria-controls");
- if (!t) return;
- const n = document.getElementById(t);
- if (null != n && n.classList.contains("govuk-checkboxes__conditional")) {
- const t = e.checked;
- e.setAttribute("aria-expanded", t.toString()), n.classList.toggle("govuk-checkboxes__conditional--hidden", !t)
+ this.$inputs.forEach((t => this.syncConditionalRevealWithInputState(t)))
+ }
+ syncConditionalRevealWithInputState(t) {
+ const e = t.getAttribute("aria-controls");
+ if (!e) return;
+ const s = document.getElementById(e);
+ if (null != s && s.classList.contains("govuk-checkboxes__conditional")) {
+ const e = t.checked;
+ t.setAttribute("aria-expanded", e.toString()), s.classList.toggle("govuk-checkboxes__conditional--hidden", !e)
}
}
- unCheckAllInputsExcept(e) {
- document.querySelectorAll(`input[type="checkbox"][name="${e.name}"]`).forEach((t => {
- e.form === t.form && t !== e && (t.checked = !1, this.syncConditionalRevealWithInputState(t))
+ unCheckAllInputsExcept(t) {
+ document.querySelectorAll(`input[type="checkbox"][name="${t.name}"]`).forEach((e => {
+ t.form === e.form && e !== t && (e.checked = !1, this.syncConditionalRevealWithInputState(e))
}))
}
- unCheckExclusiveInputs(e) {
- document.querySelectorAll(`input[data-behaviour="exclusive"][type="checkbox"][name="${e.name}"]`).forEach((t => {
- e.form === t.form && (t.checked = !1, this.syncConditionalRevealWithInputState(t))
+ unCheckExclusiveInputs(t) {
+ document.querySelectorAll(`input[data-behaviour="exclusive"][type="checkbox"][name="${t.name}"]`).forEach((e => {
+ t.form === e.form && (e.checked = !1, this.syncConditionalRevealWithInputState(e))
}))
}
- handleClick(e) {
- const t = e.target;
- if (!(t instanceof HTMLInputElement) || "checkbox" !== t.type) return;
- if (t.getAttribute("aria-controls") && this.syncConditionalRevealWithInputState(t), !t.checked) return;
- "exclusive" === t.getAttribute("data-behaviour") ? this.unCheckAllInputsExcept(t) : this.unCheckExclusiveInputs(t)
+ handleClick(t) {
+ const e = t.target;
+ if (!(e instanceof HTMLInputElement) || "checkbox" !== e.type) return;
+ if (e.getAttribute("aria-controls") && this.syncConditionalRevealWithInputState(e), !e.checked) return;
+ "exclusive" === e.getAttribute("data-behaviour") ? this.unCheckAllInputsExcept(e) : this.unCheckExclusiveInputs(e)
}
}
Checkboxes.moduleName = "govuk-checkboxes";
class ErrorSummary extends GOVUKFrontendComponent {
- constructor(e, t = {}) {
- if (super(), this.$module = void 0, this.config = void 0, !(e instanceof HTMLElement)) throw new ElementError({
- componentName: "Error summary",
- element: e,
- identifier: "Root element (`$module`)"
- });
- this.$module = e, this.config = mergeConfigs(ErrorSummary.defaults, t, normaliseDataset(ErrorSummary, e.dataset)), this.config.disableAutoFocus || setFocus(this.$module), this.$module.addEventListener("click", (e => this.handleClick(e)))
- }
- handleClick(e) {
- const t = e.target;
- t && this.focusTarget(t) && e.preventDefault()
- }
- focusTarget(e) {
- if (!(e instanceof HTMLAnchorElement)) return !1;
- const t = getFragmentFromUrl(e.href);
- if (!t) return !1;
- const n = document.getElementById(t);
- if (!n) return !1;
- const i = this.getAssociatedLegendOrLabel(n);
- return !!i && (i.scrollIntoView(), n.focus({
+ constructor(t, e = {}) {
+ super(t), this.config = void 0, this.config = mergeConfigs(ErrorSummary.defaults, e, normaliseDataset(ErrorSummary, this.$root.dataset)), this.config.disableAutoFocus || setFocus(this.$root), this.$root.addEventListener("click", (t => this.handleClick(t)))
+ }
+ handleClick(t) {
+ const e = t.target;
+ e && this.focusTarget(e) && t.preventDefault()
+ }
+ focusTarget(t) {
+ if (!(t instanceof HTMLAnchorElement)) return !1;
+ const e = getFragmentFromUrl(t.href);
+ if (!e) return !1;
+ const s = document.getElementById(e);
+ if (!s) return !1;
+ const n = this.getAssociatedLegendOrLabel(s);
+ return !!n && (n.scrollIntoView(), s.focus({
preventScroll: !0
}), !0)
}
- getAssociatedLegendOrLabel(e) {
- var t;
- const n = e.closest("fieldset");
- if (n) {
- const t = n.getElementsByTagName("legend");
- if (t.length) {
- const n = t[0];
- if (e instanceof HTMLInputElement && ("checkbox" === e.type || "radio" === e.type)) return n;
- const i = n.getBoundingClientRect().top,
- s = e.getBoundingClientRect();
- if (s.height && window.innerHeight) {
- if (s.top + s.height - i < window.innerHeight / 2) return n
+ getAssociatedLegendOrLabel(t) {
+ var e;
+ const s = t.closest("fieldset");
+ if (s) {
+ const e = s.getElementsByTagName("legend");
+ if (e.length) {
+ const s = e[0];
+ if (t instanceof HTMLInputElement && ("checkbox" === t.type || "radio" === t.type)) return s;
+ const n = s.getBoundingClientRect().top,
+ i = t.getBoundingClientRect();
+ if (i.height && window.innerHeight) {
+ if (i.top + i.height - n < window.innerHeight / 2) return s
}
}
}
- return null != (t = document.querySelector(`label[for='${e.getAttribute("id")}']`)) ? t : e.closest("label")
+ return null != (e = document.querySelector(`label[for='${t.getAttribute("id")}']`)) ? e : t.closest("label")
}
}
ErrorSummary.moduleName = "govuk-error-summary", ErrorSummary.defaults = Object.freeze({
@@ -652,52 +660,48 @@ ErrorSummary.moduleName = "govuk-error-summary", ErrorSummary.defaults = Object.
}
});
class ExitThisPage extends GOVUKFrontendComponent {
- constructor(e, t = {}) {
- if (super(), this.$module = void 0, this.config = void 0, this.i18n = void 0, this.$button = void 0, this.$skiplinkButton = null, this.$updateSpan = null, this.$indicatorContainer = null, this.$overlay = null, this.keypressCounter = 0, this.lastKeyWasModified = !1, this.timeoutTime = 5e3, this.keypressTimeoutId = null, this.timeoutMessageId = null, !(e instanceof HTMLElement)) throw new ElementError({
- componentName: "Exit this page",
- element: e,
- identifier: "Root element (`$module`)"
- });
- const n = e.querySelector(".govuk-exit-this-page__button");
- if (!(n instanceof HTMLAnchorElement)) throw new ElementError({
- componentName: "Exit this page",
- element: n,
+ constructor(t, e = {}) {
+ super(t), this.config = void 0, this.i18n = void 0, this.$button = void 0, this.$skiplinkButton = null, this.$updateSpan = null, this.$indicatorContainer = null, this.$overlay = null, this.keypressCounter = 0, this.lastKeyWasModified = !1, this.timeoutTime = 5e3, this.keypressTimeoutId = null, this.timeoutMessageId = null;
+ const s = this.$root.querySelector(".govuk-exit-this-page__button");
+ if (!(s instanceof HTMLAnchorElement)) throw new ElementError({
+ component: ExitThisPage,
+ element: s,
expectedType: "HTMLAnchorElement",
identifier: "Button (`.govuk-exit-this-page__button`)"
});
- this.config = mergeConfigs(ExitThisPage.defaults, t, normaliseDataset(ExitThisPage, e.dataset)), this.i18n = new I18n(this.config.i18n), this.$module = e, this.$button = n;
- const i = document.querySelector(".govuk-js-exit-this-page-skiplink");
- i instanceof HTMLAnchorElement && (this.$skiplinkButton = i), this.buildIndicator(), this.initUpdateSpan(), this.initButtonClickHandler(), "govukFrontendExitThisPageKeypress" in document.body.dataset || (document.addEventListener("keyup", this.handleKeypress.bind(this), !0), document.body.dataset.govukFrontendExitThisPageKeypress = "true"), window.addEventListener("pageshow", this.resetPage.bind(this))
+ this.config = mergeConfigs(ExitThisPage.defaults, e, normaliseDataset(ExitThisPage, this.$root.dataset)), this.i18n = new I18n(this.config.i18n), this.$button = s;
+ const n = document.querySelector(".govuk-js-exit-this-page-skiplink");
+ n instanceof HTMLAnchorElement && (this.$skiplinkButton = n), this.buildIndicator(), this.initUpdateSpan(), this.initButtonClickHandler(), "govukFrontendExitThisPageKeypress" in document.body.dataset || (document.addEventListener("keyup", this.handleKeypress.bind(this), !0), document.body.dataset.govukFrontendExitThisPageKeypress = "true"), window.addEventListener("pageshow", this.resetPage.bind(this))
}
initUpdateSpan() {
- this.$updateSpan = document.createElement("span"), this.$updateSpan.setAttribute("role", "status"), this.$updateSpan.className = "govuk-visually-hidden", this.$module.appendChild(this.$updateSpan)
+ this.$updateSpan = document.createElement("span"), this.$updateSpan.setAttribute("role", "status"), this.$updateSpan.className = "govuk-visually-hidden", this.$root.appendChild(this.$updateSpan)
}
initButtonClickHandler() {
this.$button.addEventListener("click", this.handleClick.bind(this)), this.$skiplinkButton && this.$skiplinkButton.addEventListener("click", this.handleClick.bind(this))
}
buildIndicator() {
this.$indicatorContainer = document.createElement("div"), this.$indicatorContainer.className = "govuk-exit-this-page__indicator", this.$indicatorContainer.setAttribute("aria-hidden", "true");
- for (let e = 0; e < 3; e++) {
- const e = document.createElement("div");
- e.className = "govuk-exit-this-page__indicator-light", this.$indicatorContainer.appendChild(e)
+ for (let t = 0; t < 3; t++) {
+ const t = document.createElement("div");
+ t.className = "govuk-exit-this-page__indicator-light", this.$indicatorContainer.appendChild(t)
}
this.$button.appendChild(this.$indicatorContainer)
}
updateIndicator() {
if (!this.$indicatorContainer) return;
this.$indicatorContainer.classList.toggle("govuk-exit-this-page__indicator--visible", this.keypressCounter > 0);
- this.$indicatorContainer.querySelectorAll(".govuk-exit-this-page__indicator-light").forEach(((e, t) => {
- e.classList.toggle("govuk-exit-this-page__indicator-light--on", t < this.keypressCounter)
+ this.$indicatorContainer.querySelectorAll(".govuk-exit-this-page__indicator-light").forEach(((t, e) => {
+ t.classList.toggle("govuk-exit-this-page__indicator-light--on", e < this.keypressCounter)
}))
}
exitPage() {
this.$updateSpan && (this.$updateSpan.textContent = "", document.body.classList.add("govuk-exit-this-page-hide-content"), this.$overlay = document.createElement("div"), this.$overlay.className = "govuk-exit-this-page-overlay", this.$overlay.setAttribute("role", "alert"), document.body.appendChild(this.$overlay), this.$overlay.textContent = this.i18n.t("activated"), window.location.href = this.$button.href)
}
- handleClick(e) {
- e.preventDefault(), this.exitPage()
+ handleClick(t) {
+ t.preventDefault(), this.exitPage()
}
- handleKeypress(e) {
- this.$updateSpan && ("Shift" !== e.key || this.lastKeyWasModified ? this.keypressTimeoutId && this.resetKeypressTimer() : (this.keypressCounter += 1, this.updateIndicator(), this.timeoutMessageId && (window.clearTimeout(this.timeoutMessageId), this.timeoutMessageId = null), this.keypressCounter >= 3 ? (this.keypressCounter = 0, this.keypressTimeoutId && (window.clearTimeout(this.keypressTimeoutId), this.keypressTimeoutId = null), this.exitPage()) : 1 === this.keypressCounter ? this.$updateSpan.textContent = this.i18n.t("pressTwoMoreTimes") : this.$updateSpan.textContent = this.i18n.t("pressOneMoreTime"), this.setKeypressTimer()), this.lastKeyWasModified = e.shiftKey)
+ handleKeypress(t) {
+ this.$updateSpan && ("Shift" !== t.key || this.lastKeyWasModified ? this.keypressTimeoutId && this.resetKeypressTimer() : (this.keypressCounter += 1, this.updateIndicator(), this.timeoutMessageId && (window.clearTimeout(this.timeoutMessageId), this.timeoutMessageId = null), this.keypressCounter >= 3 ? (this.keypressCounter = 0, this.keypressTimeoutId && (window.clearTimeout(this.keypressTimeoutId), this.keypressTimeoutId = null), this.exitPage()) : 1 === this.keypressCounter ? this.$updateSpan.textContent = this.i18n.t("pressTwoMoreTimes") : this.$updateSpan.textContent = this.i18n.t("pressOneMoreTime"), this.setKeypressTimer()), this.lastKeyWasModified = t.shiftKey)
}
setKeypressTimer() {
this.keypressTimeoutId && window.clearTimeout(this.keypressTimeoutId), this.keypressTimeoutId = window.setTimeout(this.resetKeypressTimer.bind(this), this.timeoutTime)
@@ -705,9 +709,9 @@ class ExitThisPage extends GOVUKFrontendComponent {
resetKeypressTimer() {
if (!this.$updateSpan) return;
this.keypressTimeoutId && (window.clearTimeout(this.keypressTimeoutId), this.keypressTimeoutId = null);
- const e = this.$updateSpan;
- this.keypressCounter = 0, e.textContent = this.i18n.t("timedOut"), this.timeoutMessageId = window.setTimeout((() => {
- e.textContent = ""
+ const t = this.$updateSpan;
+ this.keypressCounter = 0, t.textContent = this.i18n.t("timedOut"), this.timeoutMessageId = window.setTimeout((() => {
+ t.textContent = ""
}), this.timeoutTime), this.updateIndicator()
}
resetPage() {
@@ -729,35 +733,30 @@ ExitThisPage.moduleName = "govuk-exit-this-page", ExitThisPage.defaults = Object
}
});
class Header extends GOVUKFrontendComponent {
- constructor(e) {
- if (super(), this.$module = void 0, this.$menuButton = void 0, this.$menu = void 0, this.menuIsOpen = !1, this.mql = null, !e) throw new ElementError({
- componentName: "Header",
- element: e,
- identifier: "Root element (`$module`)"
- });
- this.$module = e;
- const t = e.querySelector(".govuk-js-header-toggle");
- if (!t) return this;
- const n = t.getAttribute("aria-controls");
- if (!n) throw new ElementError({
- componentName: "Header",
+ constructor(t) {
+ super(t), this.$menuButton = void 0, this.$menu = void 0, this.menuIsOpen = !1, this.mql = null;
+ const e = this.$root.querySelector(".govuk-js-header-toggle");
+ if (!e) return this;
+ const s = e.getAttribute("aria-controls");
+ if (!s) throw new ElementError({
+ component: Header,
identifier: 'Navigation button (`<button class="govuk-js-header-toggle">`) attribute (`aria-controls`)'
});
- const i = document.getElementById(n);
- if (!i) throw new ElementError({
- componentName: "Header",
- element: i,
- identifier: `Navigation (\`<ul id="${n}">\`)`
+ const n = document.getElementById(s);
+ if (!n) throw new ElementError({
+ component: Header,
+ element: n,
+ identifier: `Navigation (\`<ul id="${s}">\`)`
});
- this.$menu = i, this.$menuButton = t, this.setupResponsiveChecks(), this.$menuButton.addEventListener("click", (() => this.handleMenuButtonClick()))
+ this.$menu = n, this.$menuButton = e, this.setupResponsiveChecks(), this.$menuButton.addEventListener("click", (() => this.handleMenuButtonClick()))
}
setupResponsiveChecks() {
- const e = getBreakpoint("desktop");
- if (!e.value) throw new ElementError({
- componentName: "Header",
- identifier: `CSS custom property (\`${e.property}\`) on pseudo-class \`:root\``
+ const t = getBreakpoint("desktop");
+ if (!t.value) throw new ElementError({
+ component: Header,
+ identifier: `CSS custom property (\`${t.property}\`) on pseudo-class \`:root\``
});
- this.mql = window.matchMedia(`(min-width: ${e.value})`), "addEventListener" in this.mql ? this.mql.addEventListener("change", (() => this.checkMode())) : this.mql.addListener((() => this.checkMode())), this.checkMode()
+ this.mql = window.matchMedia(`(min-width: ${t.value})`), "addEventListener" in this.mql ? this.mql.addEventListener("change", (() => this.checkMode())) : this.mql.addListener((() => this.checkMode())), this.checkMode()
}
checkMode() {
this.mql && this.$menu && this.$menuButton && (this.mql.matches ? (this.$menu.removeAttribute("hidden"), this.$menuButton.setAttribute("hidden", "")) : (this.$menuButton.removeAttribute("hidden"), this.$menuButton.setAttribute("aria-expanded", this.menuIsOpen.toString()), this.menuIsOpen ? this.$menu.removeAttribute("hidden") : this.$menu.setAttribute("hidden", "")))
@@ -768,13 +767,8 @@ class Header extends GOVUKFrontendComponent {
}
Header.moduleName = "govuk-header";
class NotificationBanner extends GOVUKFrontendComponent {
- constructor(e, t = {}) {
- if (super(), this.$module = void 0, this.config = void 0, !(e instanceof HTMLElement)) throw new ElementError({
- componentName: "Notification banner",
- element: e,
- identifier: "Root element (`$module`)"
- });
- this.$module = e, this.config = mergeConfigs(NotificationBanner.defaults, t, normaliseDataset(NotificationBanner, e.dataset)), "alert" !== this.$module.getAttribute("role") || this.config.disableAutoFocus || setFocus(this.$module)
+ constructor(t, e = {}) {
+ super(t), this.config = void 0, this.config = mergeConfigs(NotificationBanner.defaults, e, normaliseDataset(NotificationBanner, this.$root.dataset)), "alert" !== this.$root.getAttribute("role") || this.config.disableAutoFocus || setFocus(this.$root)
}
}
NotificationBanner.moduleName = "govuk-notification-banner", NotificationBanner.defaults = Object.freeze({
@@ -787,38 +781,34 @@ NotificationBanner.moduleName = "govuk-notification-banner", NotificationBanner.
}
});
class PasswordInput extends GOVUKFrontendComponent {
- constructor(e, t = {}) {
- if (super(), this.$module = void 0, this.config = void 0, this.i18n = void 0, this.$input = void 0, this.$showHideButton = void 0, this.$screenReaderStatusMessage = void 0, !(e instanceof HTMLElement)) throw new ElementError({
- componentName: "Password input",
- element: e,
- identifier: "Root element (`$module`)"
- });
- const n = e.querySelector(".govuk-js-password-input-input");
- if (!(n instanceof HTMLInputElement)) throw new ElementError({
- componentName: "Password input",
- element: n,
+ constructor(t, e = {}) {
+ super(t), this.config = void 0, this.i18n = void 0, this.$input = void 0, this.$showHideButton = void 0, this.$screenReaderStatusMessage = void 0;
+ const s = this.$root.querySelector(".govuk-js-password-input-input");
+ if (!(s instanceof HTMLInputElement)) throw new ElementError({
+ component: PasswordInput,
+ element: s,
expectedType: "HTMLInputElement",
identifier: "Form field (`.govuk-js-password-input-input`)"
});
- if ("password" !== n.type) throw new ElementError("Password input: Form field (`.govuk-js-password-input-input`) must be of type `password`.");
- const i = e.querySelector(".govuk-js-password-input-toggle");
- if (!(i instanceof HTMLButtonElement)) throw new ElementError({
- componentName: "Password input",
- element: i,
+ if ("password" !== s.type) throw new ElementError("Password input: Form field (`.govuk-js-password-input-input`) must be of type `password`.");
+ const n = this.$root.querySelector(".govuk-js-password-input-toggle");
+ if (!(n instanceof HTMLButtonElement)) throw new ElementError({
+ component: PasswordInput,
+ element: n,
expectedType: "HTMLButtonElement",
identifier: "Button (`.govuk-js-password-input-toggle`)"
});
- if ("button" !== i.type) throw new ElementError("Password input: Button (`.govuk-js-password-input-toggle`) must be of type `button`.");
- this.$module = e, this.$input = n, this.$showHideButton = i, this.config = mergeConfigs(PasswordInput.defaults, t, normaliseDataset(PasswordInput, e.dataset)), this.i18n = new I18n(this.config.i18n, {
- locale: closestAttributeValue(e, "lang")
+ if ("button" !== n.type) throw new ElementError("Password input: Button (`.govuk-js-password-input-toggle`) must be of type `button`.");
+ this.$input = s, this.$showHideButton = n, this.config = mergeConfigs(PasswordInput.defaults, e, normaliseDataset(PasswordInput, this.$root.dataset)), this.i18n = new I18n(this.config.i18n, {
+ locale: closestAttributeValue(this.$root, "lang")
}), this.$showHideButton.removeAttribute("hidden");
- const s = document.createElement("div");
- s.className = "govuk-password-input__sr-status govuk-visually-hidden", s.setAttribute("aria-live", "polite"), this.$screenReaderStatusMessage = s, this.$input.insertAdjacentElement("afterend", s), this.$showHideButton.addEventListener("click", this.toggle.bind(this)), this.$input.form && this.$input.form.addEventListener("submit", (() => this.hide())), window.addEventListener("pageshow", (e => {
- e.persisted && "password" !== this.$input.type && this.hide()
+ const i = document.createElement("div");
+ i.className = "govuk-password-input__sr-status govuk-visually-hidden", i.setAttribute("aria-live", "polite"), this.$screenReaderStatusMessage = i, this.$input.insertAdjacentElement("afterend", i), this.$showHideButton.addEventListener("click", this.toggle.bind(this)), this.$input.form && this.$input.form.addEventListener("submit", (() => this.hide())), window.addEventListener("pageshow", (t => {
+ t.persisted && "password" !== this.$input.type && this.hide()
})), this.hide()
}
- toggle(e) {
- e.preventDefault(), "password" !== this.$input.type ? this.hide() : this.show()
+ toggle(t) {
+ t.preventDefault(), "password" !== this.$input.type ? this.hide() : this.show()
}
show() {
this.setType("text")
@@ -826,13 +816,13 @@ class PasswordInput extends GOVUKFrontendComponent {
hide() {
this.setType("password")
}
- setType(e) {
- if (e === this.$input.type) return;
- this.$input.setAttribute("type", e);
- const t = "password" === e,
- n = t ? "show" : "hide",
- i = t ? "passwordHidden" : "passwordShown";
- this.$showHideButton.innerText = this.i18n.t(`${n}Password`), this.$showHideButton.setAttribute("aria-label", this.i18n.t(`${n}PasswordAriaLabel`)), this.$screenReaderStatusMessage.innerText = this.i18n.t(`${i}Announcement`)
+ setType(t) {
+ if (t === this.$input.type) return;
+ this.$input.setAttribute("type", t);
+ const e = "password" === t,
+ s = e ? "show" : "hide",
+ n = e ? "passwordHidden" : "passwordShown";
+ this.$showHideButton.innerText = this.i18n.t(`${s}Password`), this.$showHideButton.setAttribute("aria-label", this.i18n.t(`${s}PasswordAriaLabel`)), this.$screenReaderStatusMessage.innerText = this.i18n.t(`${n}Announcement`)
}
}
PasswordInput.moduleName = "govuk-password-input", PasswordInput.defaults = Object.freeze({
@@ -852,83 +842,74 @@ PasswordInput.moduleName = "govuk-password-input", PasswordInput.defaults = Obje
}
});
class Radios extends GOVUKFrontendComponent {
- constructor(e) {
- if (super(), this.$module = void 0, this.$inputs = void 0, !(e instanceof HTMLElement)) throw new ElementError({
- componentName: "Radios",
- element: e,
- identifier: "Root element (`$module`)"
- });
- const t = e.querySelectorAll('input[type="radio"]');
- if (!t.length) throw new ElementError({
- componentName: "Radios",
+ constructor(t) {
+ super(t), this.$inputs = void 0;
+ const e = this.$root.querySelectorAll('input[type="radio"]');
+ if (!e.length) throw new ElementError({
+ component: Radios,
identifier: 'Form inputs (`<input type="radio">`)'
});
- this.$module = e, this.$inputs = t, this.$inputs.forEach((e => {
- const t = e.getAttribute("data-aria-controls");
- if (t) {
- if (!document.getElementById(t)) throw new ElementError({
- componentName: "Radios",
- identifier: `Conditional reveal (\`id="${t}"\`)`
+ this.$inputs = e, this.$inputs.forEach((t => {
+ const e = t.getAttribute("data-aria-controls");
+ if (e) {
+ if (!document.getElementById(e)) throw new ElementError({
+ component: Radios,
+ identifier: `Conditional reveal (\`id="${e}"\`)`
});
- e.setAttribute("aria-controls", t), e.removeAttribute("data-aria-controls")
+ t.setAttribute("aria-controls", e), t.removeAttribute("data-aria-controls")
}
- })), window.addEventListener("pageshow", (() => this.syncAllConditionalReveals())), this.syncAllConditionalReveals(), this.$module.addEventListener("click", (e => this.handleClick(e)))
+ })), window.addEventListener("pageshow", (() => this.syncAllConditionalReveals())), this.syncAllConditionalReveals(), this.$root.addEventListener("click", (t => this.handleClick(t)))
}
syncAllConditionalReveals() {
- this.$inputs.forEach((e => this.syncConditionalRevealWithInputState(e)))
- }
- syncConditionalRevealWithInputState(e) {
- const t = e.getAttribute("aria-controls");
- if (!t) return;
- const n = document.getElementById(t);
- if (null != n && n.classList.contains("govuk-radios__conditional")) {
- const t = e.checked;
- e.setAttribute("aria-expanded", t.toString()), n.classList.toggle("govuk-radios__conditional--hidden", !t)
+ this.$inputs.forEach((t => this.syncConditionalRevealWithInputState(t)))
+ }
+ syncConditionalRevealWithInputState(t) {
+ const e = t.getAttribute("aria-controls");
+ if (!e) return;
+ const s = document.getElementById(e);
+ if (null != s && s.classList.contains("govuk-radios__conditional")) {
+ const e = t.checked;
+ t.setAttribute("aria-expanded", e.toString()), s.classList.toggle("govuk-radios__conditional--hidden", !e)
}
}
- handleClick(e) {
- const t = e.target;
- if (!(t instanceof HTMLInputElement) || "radio" !== t.type) return;
- const n = document.querySelectorAll('input[type="radio"][aria-controls]'),
- i = t.form,
- s = t.name;
- n.forEach((e => {
- const t = e.form === i;
- e.name === s && t && this.syncConditionalRevealWithInputState(e)
+ handleClick(t) {
+ const e = t.target;
+ if (!(e instanceof HTMLInputElement) || "radio" !== e.type) return;
+ const s = document.querySelectorAll('input[type="radio"][aria-controls]'),
+ n = e.form,
+ i = e.name;
+ s.forEach((t => {
+ const e = t.form === n;
+ t.name === i && e && this.syncConditionalRevealWithInputState(t)
}))
}
}
Radios.moduleName = "govuk-radios";
class ServiceNavigation extends GOVUKFrontendComponent {
- constructor(e) {
- if (super(), this.$module = void 0, this.$menuButton = void 0, this.$menu = void 0, this.menuIsOpen = !1, this.mql = null, !e) throw new ElementError({
- componentName: "Service Navigation",
- element: e,
- identifier: "Root element (`$module`)"
- });
- this.$module = e;
- const t = e.querySelector(".govuk-js-service-navigation-toggle");
- if (!t) return this;
- const n = t.getAttribute("aria-controls");
- if (!n) throw new ElementError({
- componentName: "Service Navigation",
+ constructor(t) {
+ super(t), this.$menuButton = void 0, this.$menu = void 0, this.menuIsOpen = !1, this.mql = null;
+ const e = this.$root.querySelector(".govuk-js-service-navigation-toggle");
+ if (!e) return this;
+ const s = e.getAttribute("aria-controls");
+ if (!s) throw new ElementError({
+ component: ServiceNavigation,
identifier: 'Navigation button (`<button class="govuk-js-service-navigation-toggle">`) attribute (`aria-controls`)'
});
- const i = document.getElementById(n);
- if (!i) throw new ElementError({
- componentName: "Service Navigation",
- element: i,
- identifier: `Navigation (\`<ul id="${n}">\`)`
+ const n = document.getElementById(s);
+ if (!n) throw new ElementError({
+ component: ServiceNavigation,
+ element: n,
+ identifier: `Navigation (\`<ul id="${s}">\`)`
});
- this.$menu = i, this.$menuButton = t, this.setupResponsiveChecks(), this.$menuButton.addEventListener("click", (() => this.handleMenuButtonClick()))
+ this.$menu = n, this.$menuButton = e, this.setupResponsiveChecks(), this.$menuButton.addEventListener("click", (() => this.handleMenuButtonClick()))
}
setupResponsiveChecks() {
- const e = getBreakpoint("tablet");
- if (!e.value) throw new ElementError({
- componentName: "Service Navigation",
- identifier: `CSS custom property (\`${e.property}\`) on pseudo-class \`:root\``
+ const t = getBreakpoint("tablet");
+ if (!t.value) throw new ElementError({
+ component: ServiceNavigation,
+ identifier: `CSS custom property (\`${t.property}\`) on pseudo-class \`:root\``
});
- this.mql = window.matchMedia(`(min-width: ${e.value})`), "addEventListener" in this.mql ? this.mql.addEventListener("change", (() => this.checkMode())) : this.mql.addListener((() => this.checkMode())), this.checkMode()
+ this.mql = window.matchMedia(`(min-width: ${t.value})`), "addEventListener" in this.mql ? this.mql.addEventListener("change", (() => this.checkMode())) : this.mql.addListener((() => this.checkMode())), this.checkMode()
}
checkMode() {
this.mql && this.$menu && this.$menuButton && (this.mql.matches ? (this.$menu.removeAttribute("hidden"), this.$menuButton.setAttribute("hidden", "")) : (this.$menuButton.removeAttribute("hidden"), this.$menuButton.setAttribute("aria-expanded", this.menuIsOpen.toString()), this.menuIsOpen ? this.$menu.removeAttribute("hidden") : this.$menu.setAttribute("hidden", "")))
@@ -939,33 +920,27 @@ class ServiceNavigation extends GOVUKFrontendComponent {
}
ServiceNavigation.moduleName = "govuk-service-navigation";
class SkipLink extends GOVUKFrontendComponent {
- constructor(e) {
- var t;
- if (super(), this.$module = void 0, !(e instanceof HTMLAnchorElement)) throw new ElementError({
- componentName: "Skip link",
- element: e,
- expectedType: "HTMLAnchorElement",
- identifier: "Root element (`$module`)"
- });
- this.$module = e;
- const n = this.$module.hash,
- i = null != (t = this.$module.getAttribute("href")) ? t : "";
- let s;
+ constructor(t) {
+ var e;
+ super(t);
+ const s = this.$root.hash,
+ n = null != (e = this.$root.getAttribute("href")) ? e : "";
+ let i;
try {
- s = new window.URL(this.$module.href)
+ i = new window.URL(this.$root.href)
} catch (a) {
- throw new ElementError(`Skip link: Target link (\`href="${i}"\`) is invalid`)
+ throw new ElementError(`Skip link: Target link (\`href="${n}"\`) is invalid`)
}
- if (s.origin !== window.location.origin || s.pathname !== window.location.pathname) return;
- const o = getFragmentFromUrl(n);
- if (!o) throw new ElementError(`Skip link: Target link (\`href="${i}"\`) has no hash fragment`);
+ if (i.origin !== window.location.origin || i.pathname !== window.location.pathname) return;
+ const o = getFragmentFromUrl(s);
+ if (!o) throw new ElementError(`Skip link: Target link (\`href="${n}"\`) has no hash fragment`);
const r = document.getElementById(o);
if (!r) throw new ElementError({
- componentName: "Skip link",
+ component: SkipLink,
element: r,
identifier: `Target content (\`id="${o}"\`)`
});
- this.$module.addEventListener("click", (() => setFocus(r, {
+ this.$root.addEventListener("click", (() => setFocus(r, {
onBeforeFocus() {
r.classList.add("govuk-skip-link-focused-element")
},
@@ -975,184 +950,196 @@ class SkipLink extends GOVUKFrontendComponent {
})))
}
}
-SkipLink.moduleName = "govuk-skip-link";
+SkipLink.elementType = HTMLAnchorElement, SkipLink.moduleName = "govuk-skip-link";
class Tabs extends GOVUKFrontendComponent {
- constructor(e) {
- if (super(), this.$module = void 0, this.$tabs = void 0, this.$tabList = void 0, this.$tabListItems = void 0, this.jsHiddenClass = "govuk-tabs__panel--hidden", this.changingHash = !1, this.boundTabClick = void 0, this.boundTabKeydown = void 0, this.boundOnHashChange = void 0, this.mql = null, !e) throw new ElementError({
- componentName: "Tabs",
- element: e,
- identifier: "Root element (`$module`)"
- });
- const t = e.querySelectorAll("a.govuk-tabs__tab");
- if (!t.length) throw new ElementError({
- componentName: "Tabs",
+ constructor(t) {
+ super(t), this.$tabs = void 0, this.$tabList = void 0, this.$tabListItems = void 0, this.jsHiddenClass = "govuk-tabs__panel--hidden", this.changingHash = !1, this.boundTabClick = void 0, this.boundTabKeydown = void 0, this.boundOnHashChange = void 0, this.mql = null;
+ const e = this.$root.querySelectorAll("a.govuk-tabs__tab");
+ if (!e.length) throw new ElementError({
+ component: Tabs,
identifier: 'Links (`<a class="govuk-tabs__tab">`)'
});
- this.$module = e, this.$tabs = t, this.boundTabClick = this.onTabClick.bind(this), this.boundTabKeydown = this.onTabKeydown.bind(this), this.boundOnHashChange = this.onHashChange.bind(this);
- const n = this.$module.querySelector(".govuk-tabs__list"),
- i = this.$module.querySelectorAll("li.govuk-tabs__list-item");
- if (!n) throw new ElementError({
- componentName: "Tabs",
+ this.$tabs = e, this.boundTabClick = this.onTabClick.bind(this), this.boundTabKeydown = this.onTabKeydown.bind(this), this.boundOnHashChange = this.onHashChange.bind(this);
+ const s = this.$root.querySelector(".govuk-tabs__list"),
+ n = this.$root.querySelectorAll("li.govuk-tabs__list-item");
+ if (!s) throw new ElementError({
+ component: Tabs,
identifier: 'List (`<ul class="govuk-tabs__list">`)'
});
- if (!i.length) throw new ElementError({
- componentName: "Tabs",
+ if (!n.length) throw new ElementError({
+ component: Tabs,
identifier: 'List items (`<li class="govuk-tabs__list-item">`)'
});
- this.$tabList = n, this.$tabListItems = i, this.setupResponsiveChecks()
+ this.$tabList = s, this.$tabListItems = n, this.setupResponsiveChecks()
}
setupResponsiveChecks() {
- const e = getBreakpoint("tablet");
- if (!e.value) throw new ElementError({
- componentName: "Tabs",
- identifier: `CSS custom property (\`${e.property}\`) on pseudo-class \`:root\``
+ const t = getBreakpoint("tablet");
+ if (!t.value) throw new ElementError({
+ component: Tabs,
+ identifier: `CSS custom property (\`${t.property}\`) on pseudo-class \`:root\``
});
- this.mql = window.matchMedia(`(min-width: ${e.value})`), "addEventListener" in this.mql ? this.mql.addEventListener("change", (() => this.checkMode())) : this.mql.addListener((() => this.checkMode())), this.checkMode()
+ this.mql = window.matchMedia(`(min-width: ${t.value})`), "addEventListener" in this.mql ? this.mql.addEventListener("change", (() => this.checkMode())) : this.mql.addListener((() => this.checkMode())), this.checkMode()
}
checkMode() {
- var e;
- null != (e = this.mql) && e.matches ? this.setup() : this.teardown()
+ var t;
+ null != (t = this.mql) && t.matches ? this.setup() : this.teardown()
}
setup() {
- var e;
- this.$tabList.setAttribute("role", "tablist"), this.$tabListItems.forEach((e => {
- e.setAttribute("role", "presentation")
- })), this.$tabs.forEach((e => {
- this.setAttributes(e), e.addEventListener("click", this.boundTabClick, !0), e.addEventListener("keydown", this.boundTabKeydown, !0), this.hideTab(e)
+ var t;
+ this.$tabList.setAttribute("role", "tablist"), this.$tabListItems.forEach((t => {
+ t.setAttribute("role", "presentation")
+ })), this.$tabs.forEach((t => {
+ this.setAttributes(t), t.addEventListener("click", this.boundTabClick, !0), t.addEventListener("keydown", this.boundTabKeydown, !0), this.hideTab(t)
}));
- const t = null != (e = this.getTab(window.location.hash)) ? e : this.$tabs[0];
- this.showTab(t), window.addEventListener("hashchange", this.boundOnHashChange, !0)
+ const e = null != (t = this.getTab(window.location.hash)) ? t : this.$tabs[0];
+ this.showTab(e), window.addEventListener("hashchange", this.boundOnHashChange, !0)
}
teardown() {
- this.$tabList.removeAttribute("role"), this.$tabListItems.forEach((e => {
- e.removeAttribute("role")
- })), this.$tabs.forEach((e => {
- e.removeEventListener("click", this.boundTabClick, !0), e.removeEventListener("keydown", this.boundTabKeydown, !0), this.unsetAttributes(e)
+ this.$tabList.removeAttribute("role"), this.$tabListItems.forEach((t => {
+ t.removeAttribute("role")
+ })), this.$tabs.forEach((t => {
+ t.removeEventListener("click", this.boundTabClick, !0), t.removeEventListener("keydown", this.boundTabKeydown, !0), this.unsetAttributes(t)
})), window.removeEventListener("hashchange", this.boundOnHashChange, !0)
}
onHashChange() {
- const e = window.location.hash,
- t = this.getTab(e);
- if (!t) return;
+ const t = window.location.hash,
+ e = this.getTab(t);
+ if (!e) return;
if (this.changingHash) return void(this.changingHash = !1);
- const n = this.getCurrentTab();
- n && (this.hideTab(n), this.showTab(t), t.focus())
- }
- hideTab(e) {
- this.unhighlightTab(e), this.hidePanel(e)
- }
- showTab(e) {
- this.highlightTab(e), this.showPanel(e)
- }
- getTab(e) {
- return this.$module.querySelector(`a.govuk-tabs__tab[href="${e}"]`)
- }
- setAttributes(e) {
- const t = getFragmentFromUrl(e.href);
- if (!t) return;
- e.setAttribute("id", `tab_${t}`), e.setAttribute("role", "tab"), e.setAttribute("aria-controls", t), e.setAttribute("aria-selected", "false"), e.setAttribute("tabindex", "-1");
- const n = this.getPanel(e);
- n && (n.setAttribute("role", "tabpanel"), n.setAttribute("aria-labelledby", e.id), n.classList.add(this.jsHiddenClass))
- }
- unsetAttributes(e) {
- e.removeAttribute("id"), e.removeAttribute("role"), e.removeAttribute("aria-controls"), e.removeAttribute("aria-selected"), e.removeAttribute("tabindex");
- const t = this.getPanel(e);
- t && (t.removeAttribute("role"), t.removeAttribute("aria-labelledby"), t.classList.remove(this.jsHiddenClass))
- }
- onTabClick(e) {
- const t = this.getCurrentTab(),
- n = e.currentTarget;
- t && n instanceof HTMLAnchorElement && (e.preventDefault(), this.hideTab(t), this.showTab(n), this.createHistoryEntry(n))
- }
- createHistoryEntry(e) {
- const t = this.getPanel(e);
- if (!t) return;
- const n = t.id;
- t.id = "", this.changingHash = !0, window.location.hash = n, t.id = n
- }
- onTabKeydown(e) {
- switch (e.key) {
+ const s = this.getCurrentTab();
+ s && (this.hideTab(s), this.showTab(e), e.focus())
+ }
+ hideTab(t) {
+ this.unhighlightTab(t), this.hidePanel(t)
+ }
+ showTab(t) {
+ this.highlightTab(t), this.showPanel(t)
+ }
+ getTab(t) {
+ return this.$root.querySelector(`a.govuk-tabs__tab[href="${t}"]`)
+ }
+ setAttributes(t) {
+ const e = getFragmentFromUrl(t.href);
+ if (!e) return;
+ t.setAttribute("id", `tab_${e}`), t.setAttribute("role", "tab"), t.setAttribute("aria-controls", e), t.setAttribute("aria-selected", "false"), t.setAttribute("tabindex", "-1");
+ const s = this.getPanel(t);
+ s && (s.setAttribute("role", "tabpanel"), s.setAttribute("aria-labelledby", t.id), s.classList.add(this.jsHiddenClass))
+ }
+ unsetAttributes(t) {
+ t.removeAttribute("id"), t.removeAttribute("role"), t.removeAttribute("aria-controls"), t.removeAttribute("aria-selected"), t.removeAttribute("tabindex");
+ const e = this.getPanel(t);
+ e && (e.removeAttribute("role"), e.removeAttribute("aria-labelledby"), e.classList.remove(this.jsHiddenClass))
+ }
+ onTabClick(t) {
+ const e = this.getCurrentTab(),
+ s = t.currentTarget;
+ e && s instanceof HTMLAnchorElement && (t.preventDefault(), this.hideTab(e), this.showTab(s), this.createHistoryEntry(s))
+ }
+ createHistoryEntry(t) {
+ const e = this.getPanel(t);
+ if (!e) return;
+ const s = e.id;
+ e.id = "", this.changingHash = !0, window.location.hash = s, e.id = s
+ }
+ onTabKeydown(t) {
+ switch (t.key) {
case "ArrowLeft":
case "Left":
- this.activatePreviousTab(), e.preventDefault();
+ this.activatePreviousTab(), t.preventDefault();
break;
case "ArrowRight":
case "Right":
- this.activateNextTab(), e.preventDefault()
+ this.activateNextTab(), t.preventDefault()
}
}
activateNextTab() {
- const e = this.getCurrentTab();
- if (null == e || !e.parentElement) return;
- const t = e.parentElement.nextElementSibling;
- if (!t) return;
- const n = t.querySelector("a.govuk-tabs__tab");
- n && (this.hideTab(e), this.showTab(n), n.focus(), this.createHistoryEntry(n))
+ const t = this.getCurrentTab();
+ if (null == t || !t.parentElement) return;
+ const e = t.parentElement.nextElementSibling;
+ if (!e) return;
+ const s = e.querySelector("a.govuk-tabs__tab");
+ s && (this.hideTab(t), this.showTab(s), s.focus(), this.createHistoryEntry(s))
}
activatePreviousTab() {
- const e = this.getCurrentTab();
- if (null == e || !e.parentElement) return;
- const t = e.parentElement.previousElementSibling;
- if (!t) return;
- const n = t.querySelector("a.govuk-tabs__tab");
- n && (this.hideTab(e), this.showTab(n), n.focus(), this.createHistoryEntry(n))
+ const t = this.getCurrentTab();
+ if (null == t || !t.parentElement) return;
+ const e = t.parentElement.previousElementSibling;
+ if (!e) return;
+ const s = e.querySelector("a.govuk-tabs__tab");
+ s && (this.hideTab(t), this.showTab(s), s.focus(), this.createHistoryEntry(s))
}
- getPanel(e) {
- const t = getFragmentFromUrl(e.href);
- return t ? this.$module.querySelector(`#${t}`) : null
+ getPanel(t) {
+ const e = getFragmentFromUrl(t.href);
+ return e ? this.$root.querySelector(`#${e}`) : null
}
- showPanel(e) {
- const t = this.getPanel(e);
- t && t.classList.remove(this.jsHiddenClass)
+ showPanel(t) {
+ const e = this.getPanel(t);
+ e && e.classList.remove(this.jsHiddenClass)
}
- hidePanel(e) {
- const t = this.getPanel(e);
- t && t.classList.add(this.jsHiddenClass)
+ hidePanel(t) {
+ const e = this.getPanel(t);
+ e && e.classList.add(this.jsHiddenClass)
}
- unhighlightTab(e) {
- e.parentElement && (e.setAttribute("aria-selected", "false"), e.parentElement.classList.remove("govuk-tabs__list-item--selected"), e.setAttribute("tabindex", "-1"))
+ unhighlightTab(t) {
+ t.parentElement && (t.setAttribute("aria-selected", "false"), t.parentElement.classList.remove("govuk-tabs__list-item--selected"), t.setAttribute("tabindex", "-1"))
}
- highlightTab(e) {
- e.parentElement && (e.setAttribute("aria-selected", "true"), e.parentElement.classList.add("govuk-tabs__list-item--selected"), e.setAttribute("tabindex", "0"))
+ highlightTab(t) {
+ t.parentElement && (t.setAttribute("aria-selected", "true"), t.parentElement.classList.add("govuk-tabs__list-item--selected"), t.setAttribute("tabindex", "0"))
}
getCurrentTab() {
- return this.$module.querySelector(".govuk-tabs__list-item--selected a.govuk-tabs__tab")
+ return this.$root.querySelector(".govuk-tabs__list-item--selected a.govuk-tabs__tab")
}
}
-function initAll(e) {
- var t;
- if (e = void 0 !== e ? e : {}, !isSupported()) return void console.log(new SupportError);
- const n = [
- [Accordion, e.accordion],
- [Button, e.button],
- [CharacterCount, e.characterCount],
+function initAll(t) {
+ var e;
+ if (t = void 0 !== t ? t : {}, !isSupported()) return void(t.onError ? t.onError(new SupportError, {
+ config: t
+ }) : console.log(new SupportError));
+ const s = [
+ [Accordion, t.accordion],
+ [Button, t.button],
+ [CharacterCount, t.characterCount],
[Checkboxes],
- [ErrorSummary, e.errorSummary],
- [ExitThisPage, e.exitThisPage],
+ [ErrorSummary, t.errorSummary],
+ [ExitThisPage, t.exitThisPage],
[Header],
- [NotificationBanner, e.notificationBanner],
- [PasswordInput, e.passwordInput],
+ [NotificationBanner, t.notificationBanner],
+ [PasswordInput, t.passwordInput],
[Radios],
[ServiceNavigation],
[SkipLink],
[Tabs]
],
- i = null != (t = e.scope) ? t : document;
- n.forEach((([e, t]) => {
- createAll(e, t, i)
+ n = {
+ scope: null != (e = t.scope) ? e : document,
+ onError: t.onError
+ };
+ s.forEach((([Component, t]) => {
+ createAll(Component, t, n)
}))
}
-function createAll(e, t, n = document) {
- const i = n.querySelectorAll(`[data-module="${e.moduleName}"]`);
- return Array.from(i).map((n => {
+function createAll(Component, t, e) {
+ let s, n = document;
+ var i;
+ "object" == typeof e && (n = null != (i = e.scope) ? i : n, s = e.onError);
+ "function" == typeof e && (s = e), e instanceof HTMLElement && (n = e);
+ const o = n.querySelectorAll(`[data-module="${Component.moduleName}"]`);
+ return isSupported() ? Array.from(o).map((e => {
try {
- return "defaults" in e && void 0 !== t ? new e(n, t) : new e(n)
- } catch (i) {
- return console.log(i), null
+ return void 0 !== t ? new Component(e, t) : new Component(e)
+ } catch (n) {
+ return s ? s(n, {
+ element: e,
+ component: Component,
+ config: t
+ }) : console.log(n), null
}
- })).filter(Boolean)
+ })).filter(Boolean) : (s ? s(new SupportError, {
+ component: Component,
+ config: t
+ }) : console.log(new SupportError), [])
}
Tabs.moduleName = "govuk-tabs";
export {
@@ -1160,6 +1147,7 @@ export {
Button,
CharacterCount,
Checkboxes,
+ GOVUKFrontendComponent as Component,
ErrorSummary,
ExitThisPage,
Header,
@@ -1171,5 +1159,6 @@ export {
Tabs,
createAll,
initAll,
+ isSupported,
version
}; //# sourceMappingURL=govuk-frontend.min.js.map
\ No newline at end of file
Action run for 086734c |
Other changes to npm packageThe diff could not be posted as a comment. You can download it from the workflow artifacts. Action run for 086734c |
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
September 20, 2024 14:19
Inactive
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
September 24, 2024 10:13
Inactive
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
September 25, 2024 14:39
Inactive
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
September 26, 2024 15:01
Inactive
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
September 26, 2024 16:51
Inactive
Closed
4 tasks
patrickpatrickpatrick
force-pushed
the
public-js-api
branch
from
September 30, 2024 10:20
2d93945
to
bd306f7
Compare
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
September 30, 2024 10:20
Inactive
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
October 4, 2024 09:43
Inactive
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
October 4, 2024 09:43
Inactive
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
October 4, 2024 09:44
Inactive
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
October 7, 2024 09:25
Inactive
romaricpascal
changed the title
[WIP] Improve public JavaScript API for initialising components
Improve public JavaScript API for initialising components
Oct 8, 2024
romaricpascal
changed the title
Improve public JavaScript API for initialising components
Improve public JavaScript API for code initialisation
Oct 8, 2024
romaricpascal
force-pushed
the
public-js-api
branch
from
October 8, 2024 14:48
74c8102
to
295996f
Compare
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
October 8, 2024 14:48
Inactive
romaricpascal
commented
Oct 8, 2024
romaricpascal
commented
Oct 8, 2024
romaricpascal
force-pushed
the
public-js-api
branch
from
October 8, 2024 16:13
295996f
to
068eb0b
Compare
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
October 8, 2024 16:14
Inactive
- adds `isSupported` as an export for import from `govuk-frontend` - removes `internal` from JSDoc to mark function as public - adds `exports isSupported function` test - updates `exports Components` test
- createAll returns empty array and throws error is `isSupported` false - update existing createAll tests and add test for `isSupported` false
- Add support for `onError` callback in `createAll` which is called if error occurs on component initialisation. - New parameter for `createAll`, `createAllOptions` which allows user to specify a `scope`, `onError` or an object that contains both. - `isSupported` will execute `onError` callback if specified - New tests added for `onError` callback and `createAllOptions`.
- `config` parameter of `initAll` now accepts `onError` callback - `initAll` tests updated for `onError` callback
`checkSupport` of `GOVUKFrontend` can be overloaded by classes that extend it. Added tests for `createAll` when `checkSupport` has been overloaded.
It was introduced in #5328, but we'll be encouraging people to override `checkSupport` and throw their own errors. This means they don't need to override `isSupported` specifically, especially as this didn't let them customise the error message when the component was not supported
`ElementError` now accepts a `component` in config. This means the component name can be accessed from the supplied `component`. `formatErrorMessage` helper function added as it is a common string format for error messages that can be used by other errors going forward.
Components now use new `ElementError` format, they provide the class which called the error rather than the string of the component name. The tests have been updated with this change.
Co-authored-by: seaemsi <[email protected]>
Co-authored-by: seaemsi <[email protected]>
Co-authored-by: seaemsi <[email protected]>
Co-authored-by: seaemsi <[email protected]>
Remove the need for components to have a `defaults` static property, as it's really an internal convention which is a lot to ask from outside components.
This ensures the `onError` callback doesn't miss errors which wouldn't be `Error`. It's best practice to throw an instance of `Error` (or a subclass of `Error`), but no guarantees on that, so we can't anticipate the type here.
It aligns the `ConfigError` it throws with the rest of the errors thrown by components
…c property This allows to make the `moduleName` in `ChildClass` non-optional, which will be required for using `formatErrorMessage`, which requires a `ComponentWithModuleName`
…rbitrary message This ensures errors thrown when the component is already initialised have the same format as other errors related to components. Accepting an arbitrary message was the simplest way I figured to type the arguments of `InitError` (and I think should be a default for all errors) Tests for each component need updating as the messages have changed
- Moves the type check of `root` into `GOVUKFrontendComponent` base class which components in the Design System extend - Moves assignment of `root` into the `GOVUKFrontendComponent` base class which components in the Design System extend - Adds `elementType` which specifies `type` of `root` to be checked and can be overloaded by child classes - Add `getGOVUKFrontendExportsNames` to `rollup.release.config.mjs` which ensures that we don't run into an error in which `window.*` is undefined during build - Update `Skeleton` section of js documentation to reflect new changes
- Remove `root` from each component (now defined and assigned in GOVUKFrontendComponent) - Change references to `root` to be `this.root` - Add `augments` when component requires typing of the `root` to be set
romaricpascal
force-pushed
the
public-js-api
branch
from
October 8, 2024 17:44
068eb0b
to
42c5276
Compare
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
October 8, 2024 17:45
Inactive
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
October 8, 2024 17:47
Inactive
patrickpatrickpatrick
force-pushed
the
public-js-api
branch
from
October 10, 2024 09:27
1633ac7
to
4dc6be9
Compare
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
October 10, 2024 09:28
Inactive
romaricpascal
force-pushed
the
public-js-api
branch
from
October 10, 2024 09:47
4dc6be9
to
42c5276
Compare
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
October 10, 2024 09:47
Inactive
Allows Typescript user to work around the lack of types in GOVUKFrontend package, Typescript is not aware of $root in components that extend GOVUKFrontendComponent Co-authored-by: Romaric Pascal <[email protected]>
govuk-design-system-ci
temporarily deployed
to
govuk-frontend-pr-5338
October 10, 2024 09:52
Inactive
We've reviewed and approved the individual PRs that make this branch. |
patrickpatrickpatrick
approved these changes
Oct 10, 2024
patrickpatrickpatrick
pushed a commit
that referenced
this pull request
Nov 14, 2024
Improve public JavaScript API for code initialisation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR aggregates the work to offer new features in our JavaScript public API around code initialisation:
isSupported
function to check if GOV.UK Frontend is supported outside of componentscreateAll
to initialise external componentsComponent
class to extend when building your own componentsSee the list of PRs merged on its branch for more details.