-
Notifications
You must be signed in to change notification settings - Fork 324
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
Add createAll
function to initialise individual components
#4975
Conversation
📋 StatsFile sizes
Modules
View stats and visualisations on the review app Action run for f4667fd |
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 db99e502b..9192c3082 100644
--- a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
+++ b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
@@ -1,44 +1,44 @@
const version = "development";
function normaliseString(e, t) {
- const s = e ? e.trim() : "";
- let n, i = null == t ? void 0 : t.type;
- switch (i || (["true", "false"].includes(s) && (i = "boolean"), s.length > 0 && isFinite(Number(s)) && (i = "number")), i) {
+ const n = e ? e.trim() : "";
+ let s, i = null == t ? void 0 : t.type;
+ switch (i || (["true", "false"].includes(n) && (i = "boolean"), n.length > 0 && isFinite(Number(n)) && (i = "number")), i) {
case "boolean":
- n = "true" === s;
+ s = "true" === n;
break;
case "number":
- n = Number(s);
+ s = Number(n);
break;
default:
- n = e
+ s = e
}
- return n
+ return s
}
function mergeConfigs(...e) {
const t = {};
- for (const s of e)
- for (const e of Object.keys(s)) {
- const n = t[e],
- i = s[e];
- isObject(n) && isObject(i) ? t[e] = mergeConfigs(n, i) : t[e] = i
+ for (const n of e)
+ for (const e of Object.keys(n)) {
+ const s = t[e],
+ i = n[e];
+ isObject(s) && isObject(i) ? t[e] = mergeConfigs(s, i) : t[e] = i
}
return t
}
-function extractConfigByNamespace(e, t, s) {
- const n = e.schema.properties[s];
- if ("object" !== (null == n ? void 0 : n.type)) return;
+function extractConfigByNamespace(e, t, n) {
+ const s = e.schema.properties[n];
+ if ("object" !== (null == s ? void 0 : s.type)) return;
const i = {
- [s]: {}
+ [n]: {}
};
for (const [o, r] of Object.entries(t)) {
let e = i;
const t = o.split(".");
- for (const [n, i] of t.entries()) "object" == typeof e && (n < t.length - 1 ? (isObject(e[i]) || (e[i] = {}), e = e[i]) : o !== s && (e[i] = normaliseString(r)))
+ for (const [s, i] of t.entries()) "object" == typeof e && (s < t.length - 1 ? (isObject(e[i]) || (e[i] = {}), e = e[i]) : o !== n && (e[i] = normaliseString(r)))
}
- return i[s]
+ return i[n]
}
function getFragmentFromUrl(e) {
@@ -54,20 +54,20 @@ function getBreakpoint(e) {
}
function setFocus(e, t = {}) {
- var s;
- const n = e.getAttribute("tabindex");
+ var n;
+ const s = e.getAttribute("tabindex");
function onBlur() {
- var s;
- null == (s = t.onBlur) || s.call(e), n || e.removeAttribute("tabindex")
+ var n;
+ null == (n = t.onBlur) || n.call(e), s || e.removeAttribute("tabindex")
}
- n || e.setAttribute("tabindex", "-1"), e.addEventListener("focus", (function() {
+ s || e.setAttribute("tabindex", "-1"), e.addEventListener("focus", (function() {
e.addEventListener("blur", onBlur, {
once: !0
})
}), {
once: !0
- }), null == (s = t.onBeforeFocus) || s.call(e), e.focus()
+ }), null == (n = t.onBeforeFocus) || n.call(e), e.focus()
}
function isSupported(e = document.body) {
@@ -81,9 +81,9 @@ function isObject(e) {
}
function normaliseDataset(e, t) {
- const s = {};
- for (const [n, i] of Object.entries(e.schema.properties)) n in t && (s[n] = normaliseString(t[n], i)), "object" === (null == i ? void 0 : i.type) && (s[n] = extractConfigByNamespace(e, t, n));
- return s
+ const n = {};
+ for (const [s, i] of Object.entries(e.schema.properties)) s in t && (n[s] = normaliseString(t[s], i)), "object" === (null == i ? void 0 : i.type) && (n[s] = extractConfigByNamespace(e, t, s));
+ return n
}
class GOVUKFrontendError extends Error {
constructor(...e) {
@@ -106,12 +106,12 @@ class ElementError extends GOVUKFrontendError {
let t = "string" == typeof e ? e : "";
if ("object" == typeof e) {
const {
- componentName: s,
- identifier: n,
+ componentName: n,
+ identifier: s,
element: i,
expectedType: o
} = e;
- t = `${s}: ${n}`, t += i ? ` is not of type ${null!=o?o:"HTMLElement"}` : " not found"
+ t = `${n}: ${s}`, t += i ? ` is not of type ${null!=o?o:"HTMLElement"}` : " not found"
}
super(t), this.name = "ElementError"
}
@@ -126,31 +126,31 @@ class GOVUKFrontendComponent {
}
class I18n {
constructor(e = {}, t = {}) {
- var s;
- this.translations = void 0, this.locale = void 0, this.translations = e, this.locale = null != (s = t.locale) ? s : document.documentElement.lang || "en"
+ 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 s = this.translations[e];
- if ("number" == typeof(null == t ? void 0 : t.count) && "object" == typeof s) {
- const n = s[this.getPluralSuffix(e, t.count)];
- n && (s = n)
+ let n = this.translations[e];
+ if ("number" == typeof(null == t ? void 0 : t.count) && "object" == typeof n) {
+ const s = n[this.getPluralSuffix(e, t.count)];
+ s && (n = s)
}
- if ("string" == typeof s) {
- if (s.match(/%{(.\S+)}/)) {
+ 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(s, t)
+ return this.replacePlaceholders(n, t)
}
- return s
+ return n
}
return e
}
replacePlaceholders(e, t) {
- const s = Intl.NumberFormat.supportedLocalesOf(this.locale).length ? new Intl.NumberFormat(this.locale) : void 0;
- return e.replace(/%{(.\S+)}/g, (function(e, n) {
- if (Object.prototype.hasOwnProperty.call(t, n)) {
- const e = t[n];
- return !1 === e || "number" != typeof e && "string" != typeof e ? "" : "number" == typeof e ? s ? s.format(e) : `${e}` : e
+ const n = Intl.NumberFormat.supportedLocalesOf(this.locale).length ? new Intl.NumberFormat(this.locale) : void 0;
+ return e.replace(/%{(.\S+)}/g, (function(e, s) {
+ if (Object.prototype.hasOwnProperty.call(t, s)) {
+ const e = t[s];
+ return !1 === e || "number" != typeof e && "string" != typeof e ? "" : "number" == typeof e ? n ? n.format(e) : `${e}` : e
}
throw new Error(`i18n: no data found to replace ${e} placeholder in string`)
}))
@@ -160,11 +160,11 @@ class I18n {
}
getPluralSuffix(e, t) {
if (t = Number(t), !isFinite(t)) return "other";
- const s = this.translations[e],
- n = this.hasIntlPluralRulesSupport() ? new Intl.PluralRules(this.locale).select(t) : this.selectPluralFormUsingFallbackRules(t);
- 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"
+ const n = this.translations[e],
+ s = this.hasIntlPluralRulesSupport() ? new Intl.PluralRules(this.locale).select(t) : this.selectPluralFormUsingFallbackRules(t);
+ if ("object" == typeof n) {
+ if (s in n) return s;
+ if ("other" in n) return console.warn(`i18n: Missing plural form ".${s}" for "${this.locale}" locale. Falling back to ".other".`), "other"
}
throw new Error(`i18n: Plural form ".other" is required for "${this.locale}" locale`)
}
@@ -176,8 +176,8 @@ class I18n {
getPluralRulesForLocale() {
const e = this.locale.split("-")[0];
for (const t in I18n.pluralRulesMap) {
- const s = I18n.pluralRulesMap[t];
- if (s.includes(this.locale) || s.includes(e)) return t
+ const n = I18n.pluralRulesMap[t];
+ if (n.includes(this.locale) || n.includes(e)) return t
}
}
}
@@ -199,27 +199,27 @@ I18n.pluralRulesMap = {
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,
- s = t % 10;
- return 1 === s && 11 !== t ? "one" : s >= 2 && s <= 4 && !(t >= 12 && t <= 14) ? "few" : 0 === s || s >= 5 && s <= 9 || t >= 11 && t <= 14 ? "many" : "other"
+ 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"
},
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"
};
class Accordion extends GOVUKFrontendComponent {
- constructor(t, s = {}) {
+ constructor(t, n = {}) {
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.browserSupportsSessionStorage = !1, this.$showAllButton = null, this.$showAllIcon = null, this.$showAllText = null, !(t instanceof HTMLElement)) throw new ElementError({
componentName: "Accordion",
element: t,
identifier: "Root element (`$module`)"
});
- this.$module = t, this.config = mergeConfigs(Accordion.defaults, s, normaliseDataset(Accordion, t.dataset)), this.i18n = new I18n(this.config.i18n);
- const n = this.$module.querySelectorAll(`.${this.sectionClass}`);
- if (!n.length) throw new ElementError({
+ this.$module = t, this.config = mergeConfigs(Accordion.defaults, n, normaliseDataset(Accordion, t.dataset)), this.i18n = new I18n(this.config.i18n);
+ const s = this.$module.querySelectorAll(`.${this.sectionClass}`);
+ if (!s.length) throw new ElementError({
componentName: "Accordion",
identifier: `Sections (\`<div class="${this.sectionClass}">\`)`
});
- this.$sections = n, this.browserSupportsSessionStorage = e.checkForSessionStorage(), this.initControls(), this.initSectionHeaders();
+ this.$sections = s, this.browserSupportsSessionStorage = e.checkForSessionStorage(), this.initControls(), this.initSectionHeaders();
const i = this.checkIfAllSectionsOpen();
this.updateShowAllButton(i)
}
@@ -230,33 +230,33 @@ class Accordion extends GOVUKFrontendComponent {
}
initSectionHeaders() {
this.$sections.forEach(((e, t) => {
- const s = e.querySelector(`.${this.sectionHeaderClass}`);
- if (!s) throw new ElementError({
+ const n = e.querySelector(`.${this.sectionHeaderClass}`);
+ if (!n) throw new ElementError({
componentName: "Accordion",
identifier: `Section headers (\`<div class="${this.sectionHeaderClass}">\`)`
});
- this.constructHeaderMarkup(s, t), this.setExpanded(this.isExpanded(e), e), s.addEventListener("click", (() => this.onSectionToggle(e))), this.setInitialState(e)
+ this.constructHeaderMarkup(n, t), this.setExpanded(this.isExpanded(e), e), n.addEventListener("click", (() => this.onSectionToggle(e))), this.setInitialState(e)
}))
}
constructHeaderMarkup(e, t) {
- const s = e.querySelector(`.${this.sectionButtonClass}`),
- n = e.querySelector(`.${this.sectionHeadingClass}`),
+ const n = e.querySelector(`.${this.sectionButtonClass}`),
+ s = e.querySelector(`.${this.sectionHeadingClass}`),
i = e.querySelector(`.${this.sectionSummaryClass}`);
- if (!n) throw new ElementError({
+ if (!s) throw new ElementError({
componentName: "Accordion",
identifier: `Section heading (\`.${this.sectionHeadingClass}\`)`
});
- if (!s) throw new ElementError({
+ if (!n) throw new ElementError({
componentName: "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(s.attributes)) "id" !== d.nodeName && o.setAttribute(d.nodeName, `${d.nodeValue}`);
+ for (const d of Array.from(n.attributes)) "id" !== d.nodeName && o.setAttribute(d.nodeName, `${d.nodeValue}`);
const r = document.createElement("span");
- r.classList.add(this.sectionHeadingTextClass), r.id = s.id;
+ r.classList.add(this.sectionHeadingTextClass), r.id = n.id;
const a = document.createElement("span");
- a.classList.add(this.sectionHeadingTextFocusClass), r.appendChild(a), a.innerHTML = s.innerHTML;
+ a.classList.add(this.sectionHeadingTextFocusClass), r.appendChild(a), a.innerHTML = n.innerHTML;
const l = document.createElement("span");
l.classList.add(this.sectionShowHideToggleClass), l.setAttribute("data-nosnippet", "");
const c = document.createElement("span");
@@ -267,16 +267,16 @@ class Accordion extends GOVUKFrontendComponent {
const e = document.createElement("span"),
t = document.createElement("span");
t.classList.add(this.sectionSummaryFocusClass), e.appendChild(t);
- for (const s of Array.from(i.attributes)) e.setAttribute(s.nodeName, `${s.nodeValue}`);
+ for (const n of Array.from(i.attributes)) e.setAttribute(n.nodeName, `${n.nodeValue}`);
t.innerHTML = i.innerHTML, i.parentNode.replaceChild(e, i), o.appendChild(e), o.appendChild(this.getButtonPunctuationEl())
}
- o.appendChild(l), n.removeChild(s), n.appendChild(o)
+ o.appendChild(l), s.removeChild(n), s.appendChild(o)
}
onBeforeMatch(e) {
const t = e.target;
if (!(t instanceof Element)) return;
- const s = t.closest(`.${this.sectionClass}`);
- s && this.setExpanded(!0, s)
+ const n = t.closest(`.${this.sectionClass}`);
+ n && this.setExpanded(!0, n)
}
onSectionToggle(e) {
const t = this.isExpanded(e);
@@ -289,24 +289,24 @@ class Accordion extends GOVUKFrontendComponent {
})), this.updateShowAllButton(e)
}
setExpanded(e, t) {
- const s = t.querySelector(`.${this.upChevronIconClass}`),
- n = t.querySelector(`.${this.sectionShowHideTextClass}`),
+ const n = t.querySelector(`.${this.upChevronIconClass}`),
+ s = t.querySelector(`.${this.sectionShowHideTextClass}`),
i = t.querySelector(`.${this.sectionButtonClass}`),
o = t.querySelector(`.${this.sectionContentClass}`);
if (!o) throw new ElementError({
componentName: "Accordion",
identifier: `Section content (\`<div class="${this.sectionContentClass}">\`)`
});
- if (!s || !n || !i) return;
+ if (!n || !s || !i) return;
const r = e ? this.i18n.t("hideSection") : this.i18n.t("showSection");
- n.textContent = r, i.setAttribute("aria-expanded", `${e}`);
+ s.textContent = r, i.setAttribute("aria-expanded", `${e}`);
const a = [],
l = t.querySelector(`.${this.sectionHeadingTextClass}`);
l && a.push(`${l.textContent}`.trim());
const c = t.querySelector(`.${this.sectionSummaryClass}`);
c && a.push(`${c.textContent}`.trim());
const h = e ? this.i18n.t("hideSectionAriaLabel") : this.i18n.t("showSectionAriaLabel");
- a.push(h), i.setAttribute("aria-label", a.join(" , ")), e ? (o.removeAttribute("hidden"), t.classList.add(this.sectionExpandedClass), s.classList.remove(this.downChevronIconClass)) : (o.setAttribute("hidden", "until-found"), t.classList.remove(this.sectionExpandedClass), s.classList.add(this.downChevronIconClass));
+ a.push(h), i.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));
const u = this.checkIfAllSectionsOpen();
this.updateShowAllButton(u)
}
@@ -324,8 +324,8 @@ class Accordion extends GOVUKFrontendComponent {
const t = e.querySelector(`.${this.sectionButtonClass}`);
if (t) {
const e = t.getAttribute("aria-controls"),
- s = t.getAttribute("aria-expanded");
- e && s && window.sessionStorage.setItem(e, s)
+ n = t.getAttribute("aria-expanded");
+ e && n && window.sessionStorage.setItem(e, n)
}
}
}
@@ -333,9 +333,9 @@ class Accordion extends GOVUKFrontendComponent {
if (this.browserSupportsSessionStorage && this.config.rememberExpanded) {
const t = e.querySelector(`.${this.sectionButtonClass}`);
if (t) {
- const s = t.getAttribute("aria-controls"),
- n = s ? window.sessionStorage.getItem(s) : null;
- null !== n && this.setExpanded("true" === n, e)
+ const n = t.getAttribute("aria-controls"),
+ s = n ? window.sessionStorage.getItem(n) : null;
+ null !== s && this.setExpanded("true" === s, e)
}
}
}
@@ -370,7 +370,7 @@ const e = {
let t;
try {
return window.sessionStorage.setItem(e, e), t = window.sessionStorage.getItem(e) === e.toString(), window.sessionStorage.removeItem(e), t
- } catch (s) {
+ } catch (n) {
return !1
}
}
@@ -396,8 +396,8 @@ class Button extends GOVUKFrontendComponent {
}
function closestAttributeValue(e, t) {
- const s = e.closest(`[${t}]`);
- return s ? s.getAttribute(t) : null
+ const n = e.closest(`[${t}]`);
+ return n ? n.getAttribute(t) : null
}
Button.moduleName = "govuk-button", Button.defaults = Object.freeze({
preventDoubleClick: !1
@@ -410,7 +410,7 @@ Button.moduleName = "govuk-button", Button.defaults = Object.freeze({
});
class CharacterCount extends GOVUKFrontendComponent {
constructor(e, t = {}) {
- var s, n;
+ var n, s;
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,
@@ -430,24 +430,24 @@ class CharacterCount extends GOVUKFrontendComponent {
maxwords: void 0
}), this.config = mergeConfigs(CharacterCount.defaults, t, r, o);
const a = function(e, t) {
- const s = [];
- for (const [n, i] of Object.entries(e)) {
+ const n = [];
+ for (const [s, i] of Object.entries(e)) {
const e = [];
if (Array.isArray(i)) {
for (const {
- required: s,
- errorMessage: n
+ required: n,
+ errorMessage: s
}
- of i) s.every((e => !!t[e])) || e.push(n);
- "anyOf" !== n || i.length - e.length >= 1 || s.push(...e)
+ of i) n.every((e => !!t[e])) || e.push(s);
+ "anyOf" !== s || i.length - e.length >= 1 || n.push(...e)
}
}
- return s
+ return n
}(CharacterCount.schema, this.config);
if (a[0]) throw new ConfigError(`Character count: ${a[0]}`);
this.i18n = new I18n(this.config.i18n, {
locale: closestAttributeValue(e, "lang")
- }), this.maxLength = null != (s = null != (n = this.config.maxwords) ? n : this.config.maxlength) ? s : 1 / 0, this.$module = e, this.$textarea = i;
+ }), this.maxLength = null != (n = null != (s = this.config.maxwords) ? s : this.config.maxlength) ? n : 1 / 0, this.$module = e, this.$textarea = i;
const l = `${this.$textarea.id}-info`,
c = document.getElementById(l);
if (!c) throw new ElementError({
@@ -504,8 +504,8 @@ class CharacterCount extends GOVUKFrontendComponent {
}
formatCountMessage(e, t) {
if (0 === e) return this.i18n.t(`${t}AtLimit`);
- const s = e < 0 ? "OverLimit" : "UnderLimit";
- return this.i18n.t(`${t}${s}`, {
+ const n = e < 0 ? "OverLimit" : "UnderLimit";
+ return this.i18n.t(`${t}${n}`, {
count: Math.abs(e)
})
}
@@ -592,10 +592,10 @@ class Checkboxes extends GOVUKFrontendComponent {
syncConditionalRevealWithInputState(e) {
const t = e.getAttribute("aria-controls");
if (!t) return;
- const s = document.getElementById(t);
- if (null != s && s.classList.contains("govuk-checkboxes__conditional")) {
+ const n = document.getElementById(t);
+ if (null != n && n.classList.contains("govuk-checkboxes__conditional")) {
const t = e.checked;
- e.setAttribute("aria-expanded", t.toString()), s.classList.toggle("govuk-checkboxes__conditional--hidden", !t)
+ e.setAttribute("aria-expanded", t.toString()), n.classList.toggle("govuk-checkboxes__conditional--hidden", !t)
}
}
unCheckAllInputsExcept(e) {
@@ -633,25 +633,25 @@ class ErrorSummary extends GOVUKFrontendComponent {
if (!(e instanceof HTMLAnchorElement)) return !1;
const t = getFragmentFromUrl(e.href);
if (!t) return !1;
- const s = document.getElementById(t);
- if (!s) return !1;
- const n = this.getAssociatedLegendOrLabel(s);
- return !!n && (n.scrollIntoView(), s.focus({
+ const n = document.getElementById(t);
+ if (!n) return !1;
+ const s = this.getAssociatedLegendOrLabel(n);
+ return !!s && (s.scrollIntoView(), n.focus({
preventScroll: !0
}), !0)
}
getAssociatedLegendOrLabel(e) {
var t;
- const s = e.closest("fieldset");
- if (s) {
- const t = s.getElementsByTagName("legend");
+ const n = e.closest("fieldset");
+ if (n) {
+ const t = n.getElementsByTagName("legend");
if (t.length) {
- const s = t[0];
- if (e instanceof HTMLInputElement && ("checkbox" === e.type || "radio" === e.type)) return s;
- const n = s.getBoundingClientRect().top,
+ const n = t[0];
+ if (e instanceof HTMLInputElement && ("checkbox" === e.type || "radio" === e.type)) return n;
+ const s = n.getBoundingClientRect().top,
i = e.getBoundingClientRect();
if (i.height && window.innerHeight) {
- if (i.top + i.height - n < window.innerHeight / 2) return s
+ if (i.top + i.height - s < window.innerHeight / 2) return n
}
}
}
@@ -674,16 +674,16 @@ class ExitThisPage extends GOVUKFrontendComponent {
element: e,
identifier: "Root element (`$module`)"
});
- const s = e.querySelector(".govuk-exit-this-page__button");
- if (!(s instanceof HTMLAnchorElement)) throw new ElementError({
+ const n = e.querySelector(".govuk-exit-this-page__button");
+ if (!(n instanceof HTMLAnchorElement)) throw new ElementError({
componentName: "Exit this page",
- element: s,
+ element: n,
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 = 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))
+ this.config = mergeConfigs(ExitThisPage.defaults, t, normaliseDataset(ExitThisPage, e.dataset)), this.i18n = new I18n(this.config.i18n), this.$module = e, this.$button = n;
+ const s = document.querySelector(".govuk-js-exit-this-page-skiplink");
+ s instanceof HTMLAnchorElement && (this.$skiplinkButton = s), 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)
@@ -754,18 +754,18 @@ class Header extends GOVUKFrontendComponent {
this.$module = e;
const t = e.querySelector(".govuk-js-header-toggle");
if (!t) return this;
- const s = t.getAttribute("aria-controls");
- if (!s) throw new ElementError({
+ const n = t.getAttribute("aria-controls");
+ if (!n) throw new ElementError({
componentName: "Header",
identifier: 'Navigation button (`<button class="govuk-js-header-toggle">`) attribute (`aria-controls`)'
});
- const n = document.getElementById(s);
- if (!n) throw new ElementError({
+ const s = document.getElementById(n);
+ if (!s) throw new ElementError({
componentName: "Header",
- element: n,
- identifier: `Navigation (\`<ul id="${s}">\`)`
+ element: s,
+ identifier: `Navigation (\`<ul id="${n}">\`)`
});
- this.$menu = n, this.$menuButton = t, this.setupResponsiveChecks(), this.$menuButton.addEventListener("click", (() => this.handleMenuButtonClick()))
+ this.$menu = s, this.$menuButton = t, this.setupResponsiveChecks(), this.$menuButton.addEventListener("click", (() => this.handleMenuButtonClick()))
}
setupResponsiveChecks() {
const e = getBreakpoint("desktop");
@@ -809,23 +809,23 @@ class PasswordInput extends GOVUKFrontendComponent {
element: e,
identifier: "Root element (`$module`)"
});
- const s = e.querySelector(".govuk-js-password-input-input");
- if (!(s instanceof HTMLInputElement)) throw new ElementError({
+ const n = e.querySelector(".govuk-js-password-input-input");
+ if (!(n instanceof HTMLInputElement)) throw new ElementError({
componentName: "Password input",
- element: s,
+ element: n,
expectedType: "HTMLInputElement",
identifier: "Form field (`.govuk-js-password-input-input`)"
});
- if ("password" !== s.type) throw new ElementError("Password input: Form field (`.govuk-js-password-input-input`) must be of type `password`.");
- const n = e.querySelector(".govuk-js-password-input-toggle");
- if (!(n instanceof HTMLButtonElement)) throw new ElementError({
+ if ("password" !== n.type) throw new ElementError("Password input: Form field (`.govuk-js-password-input-input`) must be of type `password`.");
+ const s = e.querySelector(".govuk-js-password-input-toggle");
+ if (!(s instanceof HTMLButtonElement)) throw new ElementError({
componentName: "Password input",
- element: n,
+ element: s,
expectedType: "HTMLButtonElement",
identifier: "Button (`.govuk-js-password-input-toggle`)"
});
- if ("button" !== n.type) throw new ElementError("Password input: Button (`.govuk-js-password-input-toggle`) must be of type `button`.");
- this.$module = e, this.$input = s, this.$showHideButton = n, this.config = mergeConfigs(PasswordInput.defaults, t, normaliseDataset(PasswordInput, e.dataset)), this.i18n = new I18n(this.config.i18n, {
+ if ("button" !== s.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 = s, this.config = mergeConfigs(PasswordInput.defaults, t, normaliseDataset(PasswordInput, e.dataset)), this.i18n = new I18n(this.config.i18n, {
locale: closestAttributeValue(e, "lang")
}), this.$showHideButton.removeAttribute("hidden");
const i = document.createElement("div");
@@ -846,9 +846,9 @@ class PasswordInput extends GOVUKFrontendComponent {
if (e === this.$input.type) return;
this.$input.setAttribute("type", e);
const t = "password" === e,
- s = t ? "show" : "hide",
- n = t ? "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`)
+ n = t ? "show" : "hide",
+ s = 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(`${s}Announcement`)
}
}
PasswordInput.moduleName = "govuk-password-input", PasswordInput.defaults = Object.freeze({
@@ -896,20 +896,20 @@ class Radios extends GOVUKFrontendComponent {
syncConditionalRevealWithInputState(e) {
const t = e.getAttribute("aria-controls");
if (!t) return;
- const s = document.getElementById(t);
- if (null != s && s.classList.contains("govuk-radios__conditional")) {
+ const n = document.getElementById(t);
+ if (null != n && n.classList.contains("govuk-radios__conditional")) {
const t = e.checked;
- e.setAttribute("aria-expanded", t.toString()), s.classList.toggle("govuk-radios__conditional--hidden", !t)
+ e.setAttribute("aria-expanded", t.toString()), n.classList.toggle("govuk-radios__conditional--hidden", !t)
}
}
handleClick(e) {
const t = e.target;
if (!(t instanceof HTMLInputElement) || "radio" !== t.type) return;
- const s = document.querySelectorAll('input[type="radio"][aria-controls]'),
- n = t.form,
+ const n = document.querySelectorAll('input[type="radio"][aria-controls]'),
+ s = t.form,
i = t.name;
- s.forEach((e => {
- const t = e.form === n;
+ n.forEach((e => {
+ const t = e.form === s;
e.name === i && t && this.syncConditionalRevealWithInputState(e)
}))
}
@@ -925,17 +925,17 @@ class SkipLink extends GOVUKFrontendComponent {
identifier: "Root element (`$module`)"
});
this.$module = e;
- const s = this.$module.hash,
- n = null != (t = this.$module.getAttribute("href")) ? t : "";
+ const n = this.$module.hash,
+ s = null != (t = this.$module.getAttribute("href")) ? t : "";
let i;
try {
i = new window.URL(this.$module.href)
} catch (a) {
- throw new ElementError(`Skip link: Target link (\`href="${n}"\`) is invalid`)
+ throw new ElementError(`Skip link: Target link (\`href="${s}"\`) is invalid`)
}
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 o = getFragmentFromUrl(n);
+ if (!o) throw new ElementError(`Skip link: Target link (\`href="${s}"\`) has no hash fragment`);
const r = document.getElementById(o);
if (!r) throw new ElementError({
componentName: "Skip link",
@@ -966,17 +966,17 @@ class Tabs extends GOVUKFrontendComponent {
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 s = this.$module.querySelector(".govuk-tabs__list"),
- n = this.$module.querySelectorAll("li.govuk-tabs__list-item");
- if (!s) throw new ElementError({
+ const n = this.$module.querySelector(".govuk-tabs__list"),
+ s = this.$module.querySelectorAll("li.govuk-tabs__list-item");
+ if (!n) throw new ElementError({
componentName: "Tabs",
identifier: 'List (`<ul class="govuk-tabs__list">`)'
});
- if (!n.length) throw new ElementError({
+ if (!s.length) throw new ElementError({
componentName: "Tabs",
identifier: 'List items (`<li class="govuk-tabs__list-item">`)'
});
- this.$tabList = s, this.$tabListItems = n, this.setupResponsiveChecks()
+ this.$tabList = n, this.$tabListItems = s, this.setupResponsiveChecks()
}
setupResponsiveChecks() {
const e = getBreakpoint("tablet");
@@ -1012,8 +1012,8 @@ class Tabs extends GOVUKFrontendComponent {
t = this.getTab(e);
if (!t) return;
if (this.changingHash) return void(this.changingHash = !1);
- const s = this.getCurrentTab();
- s && (this.hideTab(s), this.showTab(t), t.focus())
+ const n = this.getCurrentTab();
+ n && (this.hideTab(n), this.showTab(t), t.focus())
}
hideTab(e) {
this.unhighlightTab(e), this.hidePanel(e)
@@ -1028,8 +1028,8 @@ class Tabs extends GOVUKFrontendComponent {
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 s = this.getPanel(e);
- s && (s.setAttribute("role", "tabpanel"), s.setAttribute("aria-labelledby", e.id), s.classList.add(this.jsHiddenClass))
+ 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");
@@ -1038,14 +1038,14 @@ class Tabs extends GOVUKFrontendComponent {
}
onTabClick(e) {
const t = this.getCurrentTab(),
- s = e.currentTarget;
- t && s instanceof HTMLAnchorElement && (e.preventDefault(), this.hideTab(t), this.showTab(s), this.createHistoryEntry(s))
+ 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 s = t.id;
- t.id = "", this.changingHash = !0, window.location.hash = s, t.id = s
+ const n = t.id;
+ t.id = "", this.changingHash = !0, window.location.hash = n, t.id = n
}
onTabKeydown(e) {
switch (e.key) {
@@ -1067,16 +1067,16 @@ class Tabs extends GOVUKFrontendComponent {
if (null == e || !e.parentElement) return;
const t = e.parentElement.nextElementSibling;
if (!t) return;
- const s = t.querySelector("a.govuk-tabs__tab");
- s && (this.hideTab(e), this.showTab(s), s.focus(), this.createHistoryEntry(s))
+ const n = t.querySelector("a.govuk-tabs__tab");
+ n && (this.hideTab(e), this.showTab(n), n.focus(), this.createHistoryEntry(n))
}
activatePreviousTab() {
const e = this.getCurrentTab();
if (null == e || !e.parentElement) return;
const t = e.parentElement.previousElementSibling;
if (!t) return;
- const s = t.querySelector("a.govuk-tabs__tab");
- s && (this.hideTab(e), this.showTab(s), s.focus(), this.createHistoryEntry(s))
+ const n = t.querySelector("a.govuk-tabs__tab");
+ n && (this.hideTab(e), this.showTab(n), n.focus(), this.createHistoryEntry(n))
}
getPanel(e) {
const t = getFragmentFromUrl(e.href);
@@ -1104,7 +1104,7 @@ class Tabs extends GOVUKFrontendComponent {
function initAll(e) {
var t;
if (e = void 0 !== e ? e : {}, !isSupported()) return void console.log(new SupportError);
- const s = [
+ const n = [
[Accordion, e.accordion],
[Button, e.button],
[CharacterCount, e.characterCount],
@@ -1118,17 +1118,22 @@ function initAll(e) {
[SkipLink],
[Tabs]
],
- n = null != (t = e.scope) ? t : document;
- s.forEach((([e, t]) => {
- n.querySelectorAll(`[data-module="${e.moduleName}"]`).forEach((s => {
- try {
- "defaults" in e ? new e(s, t) : new e(s)
- } catch (n) {
- console.log(n)
- }
- }))
+ s = null != (t = e.scope) ? t : document;
+ n.forEach((([e, t]) => {
+ createAll(e, t, s)
}))
}
+
+function createAll(e, t, n = document) {
+ const s = n.querySelectorAll(`[data-module="${e.moduleName}"]`);
+ return Array.from(s).map((n => {
+ try {
+ return "defaults" in e && void 0 !== t ? new e(n, t) : new e(n)
+ } catch (s) {
+ return console.log(s), null
+ }
+ })).filter(Boolean)
+}
Tabs.moduleName = "govuk-tabs";
export {
Accordion,
@@ -1143,6 +1148,7 @@ export {
Radios,
SkipLink,
Tabs,
+ createAll,
initAll,
version
}; //# sourceMappingURL=govuk-frontend.min.js.map
\ No newline at end of file
Action run for f4667fd |
Other changes to npm packagediff --git a/packages/govuk-frontend/dist/govuk/all.bundle.js b/packages/govuk-frontend/dist/govuk/all.bundle.js
index 66ef07672..8a123ccac 100644
--- a/packages/govuk-frontend/dist/govuk/all.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/all.bundle.js
@@ -2372,17 +2372,39 @@
const components = [[Accordion, config.accordion], [Button, config.button], [CharacterCount, config.characterCount], [Checkboxes], [ErrorSummary, config.errorSummary], [ExitThisPage, config.exitThisPage], [Header], [NotificationBanner, config.notificationBanner], [PasswordInput, config.passwordInput], [Radios], [SkipLink], [Tabs]];
const $scope = (_config$scope = config.scope) != null ? _config$scope : document;
components.forEach(([Component, config]) => {
- const $elements = $scope.querySelectorAll(`[data-module="${Component.moduleName}"]`);
- $elements.forEach($element => {
- try {
- 'defaults' in Component ? new Component($element, config) : new Component($element);
- } catch (error) {
- console.log(error);
- }
- });
+ createAll(Component, config, $scope);
});
}
+ /**
+ * Create all instances of a specific component on the page
+ *
+ * Uses the `data-module` attribute to find all elements matching the specified
+ * component on the page, creating instances of the component object for each
+ * of them.
+ *
+ * Any component errors will be caught and logged to the console.
+ *
+ * @template {CompatibleClass} T
+ * @param {T} Component - class of the component to create
+ * @param {T["defaults"]} [config] - config for the component
+ * @param {Element|Document} [$scope] - scope of the document to search within
+ * @returns {Array<InstanceType<T>>} - array of instantiated components
+ */
+ function createAll(Component, config, $scope = document) {
+ const $elements = $scope.querySelectorAll(`[data-module="${Component.moduleName}"]`);
+ return Array.from($elements).map($element => {
+ try {
+ return 'defaults' in Component && typeof config !== 'undefined' ? new Component($element, config) : new Component($element);
+ } catch (error) {
+ console.log(error);
+ return null;
+ }
+ }).filter(Boolean);
+ }
+ /**
+ * @typedef {{new (...args: any[]): any, defaults?: object, moduleName: string}} CompatibleClass
+ */
/**
* Config for all components via `initAll()`
*
@@ -2395,7 +2417,6 @@
* @property {NotificationBannerConfig} [notificationBanner] - Notification Banner config
* @property {PasswordInputConfig} [passwordInput] - Password input config
*/
-
/**
* Config for individual components
*
@@ -2410,7 +2431,6 @@
* @typedef {import('./components/notification-banner/notification-banner.mjs').NotificationBannerConfig} NotificationBannerConfig
* @typedef {import('./components/password-input/password-input.mjs').PasswordInputConfig} PasswordInputConfig
*/
-
/**
* Component config keys, e.g. `accordion` and `characterCount`
*
@@ -2429,6 +2449,7 @@
exports.Radios = Radios;
exports.SkipLink = SkipLink;
exports.Tabs = Tabs;
+ exports.createAll = createAll;
exports.initAll = initAll;
exports.version = version;
diff --git a/packages/govuk-frontend/dist/govuk/all.bundle.mjs b/packages/govuk-frontend/dist/govuk/all.bundle.mjs
index 669d06852..79ce345b0 100644
--- a/packages/govuk-frontend/dist/govuk/all.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/all.bundle.mjs
@@ -2366,17 +2366,39 @@ function initAll(config) {
const components = [[Accordion, config.accordion], [Button, config.button], [CharacterCount, config.characterCount], [Checkboxes], [ErrorSummary, config.errorSummary], [ExitThisPage, config.exitThisPage], [Header], [NotificationBanner, config.notificationBanner], [PasswordInput, config.passwordInput], [Radios], [SkipLink], [Tabs]];
const $scope = (_config$scope = config.scope) != null ? _config$scope : document;
components.forEach(([Component, config]) => {
- const $elements = $scope.querySelectorAll(`[data-module="${Component.moduleName}"]`);
- $elements.forEach($element => {
- try {
- 'defaults' in Component ? new Component($element, config) : new Component($element);
- } catch (error) {
- console.log(error);
- }
- });
+ createAll(Component, config, $scope);
});
}
+/**
+ * Create all instances of a specific component on the page
+ *
+ * Uses the `data-module` attribute to find all elements matching the specified
+ * component on the page, creating instances of the component object for each
+ * of them.
+ *
+ * Any component errors will be caught and logged to the console.
+ *
+ * @template {CompatibleClass} T
+ * @param {T} Component - class of the component to create
+ * @param {T["defaults"]} [config] - config for the component
+ * @param {Element|Document} [$scope] - scope of the document to search within
+ * @returns {Array<InstanceType<T>>} - array of instantiated components
+ */
+function createAll(Component, config, $scope = document) {
+ const $elements = $scope.querySelectorAll(`[data-module="${Component.moduleName}"]`);
+ return Array.from($elements).map($element => {
+ try {
+ return 'defaults' in Component && typeof config !== 'undefined' ? new Component($element, config) : new Component($element);
+ } catch (error) {
+ console.log(error);
+ return null;
+ }
+ }).filter(Boolean);
+}
+/**
+ * @typedef {{new (...args: any[]): any, defaults?: object, moduleName: string}} CompatibleClass
+ */
/**
* Config for all components via `initAll()`
*
@@ -2389,7 +2411,6 @@ function initAll(config) {
* @property {NotificationBannerConfig} [notificationBanner] - Notification Banner config
* @property {PasswordInputConfig} [passwordInput] - Password input config
*/
-
/**
* Config for individual components
*
@@ -2404,12 +2425,11 @@ function initAll(config) {
* @typedef {import('./components/notification-banner/notification-banner.mjs').NotificationBannerConfig} NotificationBannerConfig
* @typedef {import('./components/password-input/password-input.mjs').PasswordInputConfig} PasswordInputConfig
*/
-
/**
* Component config keys, e.g. `accordion` and `characterCount`
*
* @typedef {keyof Config} ConfigKey
*/
-export { Accordion, Button, CharacterCount, Checkboxes, ErrorSummary, ExitThisPage, Header, NotificationBanner, PasswordInput, Radios, SkipLink, Tabs, initAll, version };
+export { Accordion, Button, CharacterCount, Checkboxes, ErrorSummary, ExitThisPage, Header, NotificationBanner, PasswordInput, Radios, SkipLink, Tabs, createAll, initAll, version };
//# sourceMappingURL=all.bundle.mjs.map
diff --git a/packages/govuk-frontend/dist/govuk/all.mjs b/packages/govuk-frontend/dist/govuk/all.mjs
index 02cb75e14..e395113b0 100644
--- a/packages/govuk-frontend/dist/govuk/all.mjs
+++ b/packages/govuk-frontend/dist/govuk/all.mjs
@@ -1,81 +1,15 @@
export { version } from './common/govuk-frontend-version.mjs';
-import { isSupported } from './common/index.mjs';
-import { Accordion } from './components/accordion/accordion.mjs';
-import { Button } from './components/button/button.mjs';
-import { CharacterCount } from './components/character-count/character-count.mjs';
-import { Checkboxes } from './components/checkboxes/checkboxes.mjs';
-import { ErrorSummary } from './components/error-summary/error-summary.mjs';
-import { ExitThisPage } from './components/exit-this-page/exit-this-page.mjs';
-import { Header } from './components/header/header.mjs';
-import { NotificationBanner } from './components/notification-banner/notification-banner.mjs';
-import { PasswordInput } from './components/password-input/password-input.mjs';
-import { Radios } from './components/radios/radios.mjs';
-import { SkipLink } from './components/skip-link/skip-link.mjs';
-import { Tabs } from './components/tabs/tabs.mjs';
-import { SupportError } from './errors/index.mjs';
-
-/**
- * Initialise all components
- *
- * Use the `data-module` attributes to find, instantiate and init all of the
- * components provided as part of GOV.UK Frontend.
- *
- * @param {Config & { scope?: Element }} [config] - Config for all components (with optional scope)
- */
-function initAll(config) {
- var _config$scope;
- config = typeof config !== 'undefined' ? config : {};
- if (!isSupported()) {
- console.log(new SupportError());
- return;
- }
- const components = [[Accordion, config.accordion], [Button, config.button], [CharacterCount, config.characterCount], [Checkboxes], [ErrorSummary, config.errorSummary], [ExitThisPage, config.exitThisPage], [Header], [NotificationBanner, config.notificationBanner], [PasswordInput, config.passwordInput], [Radios], [SkipLink], [Tabs]];
- const $scope = (_config$scope = config.scope) != null ? _config$scope : document;
- components.forEach(([Component, config]) => {
- const $elements = $scope.querySelectorAll(`[data-module="${Component.moduleName}"]`);
- $elements.forEach($element => {
- try {
- 'defaults' in Component ? new Component($element, config) : new Component($element);
- } catch (error) {
- console.log(error);
- }
- });
- });
-}
-
-/**
- * Config for all components via `initAll()`
- *
- * @typedef {object} Config
- * @property {AccordionConfig} [accordion] - Accordion config
- * @property {ButtonConfig} [button] - Button config
- * @property {CharacterCountConfig} [characterCount] - Character Count config
- * @property {ErrorSummaryConfig} [errorSummary] - Error Summary config
- * @property {ExitThisPageConfig} [exitThisPage] - Exit This Page config
- * @property {NotificationBannerConfig} [notificationBanner] - Notification Banner config
- * @property {PasswordInputConfig} [passwordInput] - Password input config
- */
-
-/**
- * Config for individual components
- *
- * @typedef {import('./components/accordion/accordion.mjs').AccordionConfig} AccordionConfig
- * @typedef {import('./components/accordion/accordion.mjs').AccordionTranslations} AccordionTranslations
- * @typedef {import('./components/button/button.mjs').ButtonConfig} ButtonConfig
- * @typedef {import('./components/character-count/character-count.mjs').CharacterCountConfig} CharacterCountConfig
- * @typedef {import('./components/character-count/character-count.mjs').CharacterCountTranslations} CharacterCountTranslations
- * @typedef {import('./components/error-summary/error-summary.mjs').ErrorSummaryConfig} ErrorSummaryConfig
- * @typedef {import('./components/exit-this-page/exit-this-page.mjs').ExitThisPageConfig} ExitThisPageConfig
- * @typedef {import('./components/exit-this-page/exit-this-page.mjs').ExitThisPageTranslations} ExitThisPageTranslations
- * @typedef {import('./components/notification-banner/notification-banner.mjs').NotificationBannerConfig} NotificationBannerConfig
- * @typedef {import('./components/password-input/password-input.mjs').PasswordInputConfig} PasswordInputConfig
- */
-
-/**
- * Component config keys, e.g. `accordion` and `characterCount`
- *
- * @typedef {keyof Config} ConfigKey
- */
-
-export { Accordion, Button, CharacterCount, Checkboxes, ErrorSummary, ExitThisPage, Header, NotificationBanner, PasswordInput, Radios, SkipLink, Tabs, initAll };
+export { Accordion } from './components/accordion/accordion.mjs';
+export { Button } from './components/button/button.mjs';
+export { CharacterCount } from './components/character-count/character-count.mjs';
+export { Checkboxes } from './components/checkboxes/checkboxes.mjs';
+export { ErrorSummary } from './components/error-summary/error-summary.mjs';
+export { ExitThisPage } from './components/exit-this-page/exit-this-page.mjs';
+export { Header } from './components/header/header.mjs';
+export { NotificationBanner } from './components/notification-banner/notification-banner.mjs';
+export { PasswordInput } from './components/password-input/password-input.mjs';
+export { Radios } from './components/radios/radios.mjs';
+export { SkipLink } from './components/skip-link/skip-link.mjs';
+export { Tabs } from './components/tabs/tabs.mjs';
+export { createAll, initAll } from './init.mjs';
//# sourceMappingURL=all.mjs.map
diff --git a/packages/govuk-frontend/dist/govuk/init.mjs b/packages/govuk-frontend/dist/govuk/init.mjs
new file mode 100644
index 000000000..f985eaffb
--- /dev/null
+++ b/packages/govuk-frontend/dist/govuk/init.mjs
@@ -0,0 +1,100 @@
+import { isSupported } from './common/index.mjs';
+import { Accordion } from './components/accordion/accordion.mjs';
+import { Button } from './components/button/button.mjs';
+import { CharacterCount } from './components/character-count/character-count.mjs';
+import { Checkboxes } from './components/checkboxes/checkboxes.mjs';
+import { ErrorSummary } from './components/error-summary/error-summary.mjs';
+import { ExitThisPage } from './components/exit-this-page/exit-this-page.mjs';
+import { Header } from './components/header/header.mjs';
+import { NotificationBanner } from './components/notification-banner/notification-banner.mjs';
+import { PasswordInput } from './components/password-input/password-input.mjs';
+import { Radios } from './components/radios/radios.mjs';
+import { SkipLink } from './components/skip-link/skip-link.mjs';
+import { Tabs } from './components/tabs/tabs.mjs';
+import { SupportError } from './errors/index.mjs';
+
+/**
+ * Initialise all components
+ *
+ * Use the `data-module` attributes to find, instantiate and init all of the
+ * components provided as part of GOV.UK Frontend.
+ *
+ * @param {Config & { scope?: Element }} [config] - Config for all components (with optional scope)
+ */
+function initAll(config) {
+ var _config$scope;
+ config = typeof config !== 'undefined' ? config : {};
+ if (!isSupported()) {
+ console.log(new SupportError());
+ return;
+ }
+ const components = [[Accordion, config.accordion], [Button, config.button], [CharacterCount, config.characterCount], [Checkboxes], [ErrorSummary, config.errorSummary], [ExitThisPage, config.exitThisPage], [Header], [NotificationBanner, config.notificationBanner], [PasswordInput, config.passwordInput], [Radios], [SkipLink], [Tabs]];
+ const $scope = (_config$scope = config.scope) != null ? _config$scope : document;
+ components.forEach(([Component, config]) => {
+ createAll(Component, config, $scope);
+ });
+}
+
+/**
+ * Create all instances of a specific component on the page
+ *
+ * Uses the `data-module` attribute to find all elements matching the specified
+ * component on the page, creating instances of the component object for each
+ * of them.
+ *
+ * Any component errors will be caught and logged to the console.
+ *
+ * @template {CompatibleClass} T
+ * @param {T} Component - class of the component to create
+ * @param {T["defaults"]} [config] - config for the component
+ * @param {Element|Document} [$scope] - scope of the document to search within
+ * @returns {Array<InstanceType<T>>} - array of instantiated components
+ */
+function createAll(Component, config, $scope = document) {
+ const $elements = $scope.querySelectorAll(`[data-module="${Component.moduleName}"]`);
+ return Array.from($elements).map($element => {
+ try {
+ return 'defaults' in Component && typeof config !== 'undefined' ? new Component($element, config) : new Component($element);
+ } catch (error) {
+ console.log(error);
+ return null;
+ }
+ }).filter(Boolean);
+}
+/**
+ * @typedef {{new (...args: any[]): any, defaults?: object, moduleName: string}} CompatibleClass
+ */
+/**
+ * Config for all components via `initAll()`
+ *
+ * @typedef {object} Config
+ * @property {AccordionConfig} [accordion] - Accordion config
+ * @property {ButtonConfig} [button] - Button config
+ * @property {CharacterCountConfig} [characterCount] - Character Count config
+ * @property {ErrorSummaryConfig} [errorSummary] - Error Summary config
+ * @property {ExitThisPageConfig} [exitThisPage] - Exit This Page config
+ * @property {NotificationBannerConfig} [notificationBanner] - Notification Banner config
+ * @property {PasswordInputConfig} [passwordInput] - Password input config
+ */
+/**
+ * Config for individual components
+ *
+ * @typedef {import('./components/accordion/accordion.mjs').AccordionConfig} AccordionConfig
+ * @typedef {import('./components/accordion/accordion.mjs').AccordionTranslations} AccordionTranslations
+ * @typedef {import('./components/button/button.mjs').ButtonConfig} ButtonConfig
+ * @typedef {import('./components/character-count/character-count.mjs').CharacterCountConfig} CharacterCountConfig
+ * @typedef {import('./components/character-count/character-count.mjs').CharacterCountTranslations} CharacterCountTranslations
+ * @typedef {import('./components/error-summary/error-summary.mjs').ErrorSummaryConfig} ErrorSummaryConfig
+ * @typedef {import('./components/exit-this-page/exit-this-page.mjs').ExitThisPageConfig} ExitThisPageConfig
+ * @typedef {import('./components/exit-this-page/exit-this-page.mjs').ExitThisPageTranslations} ExitThisPageTranslations
+ * @typedef {import('./components/notification-banner/notification-banner.mjs').NotificationBannerConfig} NotificationBannerConfig
+ * @typedef {import('./components/password-input/password-input.mjs').PasswordInputConfig} PasswordInputConfig
+ */
+/**
+ * Component config keys, e.g. `accordion` and `characterCount`
+ *
+ * @typedef {keyof Config} ConfigKey
+ */
+
+export { createAll, initAll };
+//# sourceMappingURL=init.mjs.map
Action run for f4667fd |
c5a3989
to
a374062
Compare
a374062
to
b598e14
Compare
b598e14
to
d377b18
Compare
Co-authored-by: Romaric Pascal <romaric.pascal@digital.cabinet-office.gov.uk> Co-authored-by: Owen Jones <owen.jones@digital.cabinet-office.gov.uk>
TypeScript doesn’t really have the concept of passing a class to a function, so we have to fake it by defining a type that is constructable and has the properties we need to be able to instantiate it (a `moduleName` and optionally a set of `defaults` from which we can infer the config type). This is based on approaches from: - https://stackoverflow.com/questions/71086547/build-a-function-that-accepts-a-class-in-typescript - microsoft/TypeScript#17572 Co-authored-by: Owen Jones <owen.jones@digital.cabinet-office.gov.uk>
Co-authored-by: Owen Jones <owen.jones@digital.cabinet-office.gov.uk>
Co-authored-by: Oliver Byford <oliver.byford@digital.cabinet-office.gov.uk> Co-authored-by: Owen Jones <owen.jones@digital.cabinet-office.gov.uk>
Now that `createAll` returns the component objects, we can `expect` on their properties rather than use spies on the constructor. Also added a couple of missing test cases and grouped tests in `describe`. Co-authored-by: Oliver Byford <oliver.byford@digital.cabinet-office.gov.uk>
Move the `components/global.puppeteer.test.js` file up to `all.puppeteer.test.js` as it's more related to the `all.mjs` file than the components themselves. Co-authored-by: Oliver Byford <oliver.byford@digital.cabinet-office.gov.uk>
Co-authored-by: Oliver Byford <oliver.byford@digital.cabinet-office.gov.uk>
export { version } from './common/govuk-frontend-version.mjs' | ||
export { Accordion } from './components/accordion/accordion.mjs' | ||
export { Button } from './components/button/button.mjs' | ||
export { CharacterCount } from './components/character-count/character-count.mjs' | ||
export { Checkboxes } from './components/checkboxes/checkboxes.mjs' | ||
export { ErrorSummary } from './components/error-summary/error-summary.mjs' | ||
export { ExitThisPage } from './components/exit-this-page/exit-this-page.mjs' | ||
export { Header } from './components/header/header.mjs' | ||
export { NotificationBanner } from './components/notification-banner/notification-banner.mjs' | ||
export { PasswordInput } from './components/password-input/password-input.mjs' | ||
export { Radios } from './components/radios/radios.mjs' | ||
export { SkipLink } from './components/skip-link/skip-link.mjs' | ||
export { Tabs } from './components/tabs/tabs.mjs' | ||
export { initAll, createAll } from './init.mjs' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note With the removal of initAll
from all.mjs
, all the file does is re-exporting the difference pieces of our library that constitute our public API, so we can switch to the export { NAMED_IMPORT } from 'module'
syntax rather than have a bunch of import
s followed by an export
.
export { Accordion } from './components/accordion/accordion.mjs'; | ||
export { Button } from './components/button/button.mjs'; | ||
export { CharacterCount } from './components/character-count/character-count.mjs'; | ||
export { Checkboxes } from './components/checkboxes/checkboxes.mjs'; | ||
export { ErrorSummary } from './components/error-summary/error-summary.mjs'; | ||
export { ExitThisPage } from './components/exit-this-page/exit-this-page.mjs'; | ||
export { Header } from './components/header/header.mjs'; | ||
export { NotificationBanner } from './components/notification-banner/notification-banner.mjs'; | ||
export { PasswordInput } from './components/password-input/password-input.mjs'; | ||
export { Radios } from './components/radios/radios.mjs'; | ||
export { SkipLink } from './components/skip-link/skip-link.mjs'; | ||
export { Tabs } from './components/tabs/tabs.mjs'; | ||
export { createAll, initAll } from './init.mjs'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note This change is unrelated to our move to using export
in all.mjs
. Even keeping import
s + a final export
, Rollup would rewrite to the export { NAMED_EXPORT } from 'module'
syntax.
@@ -126,7 +126,7 @@ describe('dist/', () => { | |||
|
|||
it('should contain relative paths to sources', () => { | |||
expect(sourcemap.sources).toContain( | |||
'../packages/govuk-frontend/src/govuk/all.mjs' | |||
'../packages/govuk-frontend/src/govuk/init.mjs' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note sourcemap.sources
lists many other modules, which was already the case alongside all.mjs
, so we can just swap to another one of our files being imported.
Adds a new
createAll
function that lets users instantiate a given component on all the elements marked with the relevantdata-module
attribute on the page.The function allows users to provide a
config
that will be passed to each instance of the component when created. And can be limited look into only a specific part of the page.Similarly to
initAll
(from which the function's code was extracted, and uses that new function under the hood now), the function logs errors to the console rather than throw and break script execution. Users can still call the components' constrcutor themselfs should they want to catch errors.The feature was initially imagined as a static function, available on each component but ideally defined on our BaseComponent. However: