Skip to content
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

Refactor the accordion JavaScript #5043

Merged
merged 9 commits into from
Jun 11, 2024
Merged

Refactor the accordion JavaScript #5043

merged 9 commits into from
Jun 11, 2024

Conversation

36degrees
Copy link
Contributor

I've given the accordion JavaScript a bit of TLC and fixed a few things that have been bugging me ✨

(There are a few more things I'd like to do that are a bit 🌶️ spicy so I'll raise them as separate PRs stacked on this one.)

See individual commits for details.

Copy link

github-actions bot commented Jun 4, 2024

📋 Stats

File sizes

File Size
dist/govuk-frontend-development.min.css 113.37 KiB
dist/govuk-frontend-development.min.js 42.25 KiB
packages/govuk-frontend/dist/govuk/all.bundle.js 87.82 KiB
packages/govuk-frontend/dist/govuk/all.bundle.mjs 82.5 KiB
packages/govuk-frontend/dist/govuk/all.mjs 981 B
packages/govuk-frontend/dist/govuk/govuk-frontend-component.mjs 359 B
packages/govuk-frontend/dist/govuk/govuk-frontend.min.css 113.36 KiB
packages/govuk-frontend/dist/govuk/govuk-frontend.min.js 42.24 KiB
packages/govuk-frontend/dist/govuk/i18n.mjs 5.55 KiB
packages/govuk-frontend/dist/govuk/init.mjs 4.86 KiB

Modules

File Size (bundled) Size (minified)
all.mjs 78.12 KiB 40.21 KiB
accordion.mjs 22.38 KiB 12.76 KiB
button.mjs 5.98 KiB 2.69 KiB
character-count.mjs 22.4 KiB 9.92 KiB
checkboxes.mjs 5.83 KiB 2.83 KiB
error-summary.mjs 7.89 KiB 3.46 KiB
exit-this-page.mjs 17.1 KiB 9.26 KiB
header.mjs 4.46 KiB 2.6 KiB
notification-banner.mjs 6.26 KiB 2.62 KiB
password-input.mjs 15.15 KiB 7.25 KiB
radios.mjs 4.83 KiB 2.38 KiB
skip-link.mjs 4.39 KiB 2.18 KiB
tabs.mjs 10.13 KiB 6.11 KiB

View stats and visualisations on the review app


Action run for bed231c

Copy link

github-actions bot commented Jun 4, 2024

JavaScript changes to npm package

diff --git a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
index 9192c3082..83477260f 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 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) {
+    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) {
         case "boolean":
-            s = "true" === n;
+            n = "true" === s;
             break;
         case "number":
-            s = Number(n);
+            n = Number(s);
             break;
         default:
-            s = e
+            n = e
     }
-    return s
+    return n
 }
 
 function mergeConfigs(...e) {
     const t = {};
-    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
+    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
         }
     return t
 }
 
-function extractConfigByNamespace(e, t, n) {
-    const s = e.schema.properties[n];
-    if ("object" !== (null == s ? void 0 : s.type)) return;
+function extractConfigByNamespace(e, t, s) {
+    const n = e.schema.properties[s];
+    if ("object" !== (null == n ? void 0 : n.type)) return;
     const i = {
-        [n]: {}
+        [s]: {}
     };
     for (const [o, r] of Object.entries(t)) {
         let e = i;
         const t = o.split(".");
-        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)))
+        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)))
     }
-    return i[n]
+    return i[s]
 }
 
 function getFragmentFromUrl(e) {
@@ -54,20 +54,20 @@ function getBreakpoint(e) {
 }
 
 function setFocus(e, t = {}) {
-    var n;
-    const s = e.getAttribute("tabindex");
+    var s;
+    const n = e.getAttribute("tabindex");
 
     function onBlur() {
-        var n;
-        null == (n = t.onBlur) || n.call(e), s || e.removeAttribute("tabindex")
+        var s;
+        null == (s = t.onBlur) || s.call(e), n || e.removeAttribute("tabindex")
     }
-    s || e.setAttribute("tabindex", "-1"), e.addEventListener("focus", (function() {
+    n || e.setAttribute("tabindex", "-1"), e.addEventListener("focus", (function() {
         e.addEventListener("blur", onBlur, {
             once: !0
         })
     }), {
         once: !0
-    }), null == (n = t.onBeforeFocus) || n.call(e), e.focus()
+    }), null == (s = t.onBeforeFocus) || s.call(e), e.focus()
 }
 
 function isSupported(e = document.body) {
@@ -81,9 +81,9 @@ function isObject(e) {
 }
 
 function normaliseDataset(e, t) {
-    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
+    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
 }
 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: n,
-                identifier: s,
+                componentName: s,
+                identifier: n,
                 element: i,
                 expectedType: o
             } = e;
-            t = `${n}: ${s}`, t += i ? ` is not of type ${null!=o?o:"HTMLElement"}` : " not found"
+            t = `${s}: ${n}`, 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 n;
-        this.translations = void 0, this.locale = void 0, this.translations = e, this.locale = null != (n = t.locale) ? n : document.documentElement.lang || "en"
+        var s;
+        this.translations = void 0, this.locale = void 0, this.translations = e, this.locale = null != (s = t.locale) ? s : 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 s = n[this.getPluralSuffix(e, t.count)];
-            s && (n = s)
+        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)
         }
-        if ("string" == typeof n) {
-            if (n.match(/%{(.\S+)}/)) {
+        if ("string" == typeof s) {
+            if (s.match(/%{(.\S+)}/)) {
                 if (!t) throw new Error("i18n: cannot replace placeholders in string if no option data provided");
-                return this.replacePlaceholders(n, t)
+                return this.replacePlaceholders(s, t)
             }
-            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, 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
+        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
             }
             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 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"
+        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"
         }
         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 n = I18n.pluralRulesMap[t];
-            if (n.includes(this.locale) || n.includes(e)) return t
+            const s = I18n.pluralRulesMap[t];
+            if (s.includes(this.locale) || s.includes(e)) return t
         }
     }
 }
@@ -199,29 +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,
-            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"
+            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"
     },
     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, n = {}) {
+    constructor(t, s = {}) {
         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, 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({
+        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({
             componentName: "Accordion",
             identifier: `Sections (\`<div class="${this.sectionClass}">\`)`
         });
-        this.$sections = s, this.browserSupportsSessionStorage = e.checkForSessionStorage(), this.initControls(), this.initSectionHeaders();
-        const i = this.checkIfAllSectionsOpen();
-        this.updateShowAllButton(i)
+        this.$sections = n, this.browserSupportsSessionStorage = e.checkForSessionStorage(), 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);
@@ -230,102 +228,99 @@ class Accordion extends GOVUKFrontendComponent {
     }
     initSectionHeaders() {
         this.$sections.forEach(((e, t) => {
-            const n = e.querySelector(`.${this.sectionHeaderClass}`);
-            if (!n) throw new ElementError({
+            const s = e.querySelector(`.${this.sectionHeaderClass}`);
+            if (!s) throw new ElementError({
                 componentName: "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, t), this.setExpanded(this.isExpanded(e), e), s.addEventListener("click", (() => this.onSectionToggle(e))), this.setInitialState(e)
         }))
     }
     constructHeaderMarkup(e, t) {
-        const n = e.querySelector(`.${this.sectionButtonClass}`),
-            s = e.querySelector(`.${this.sectionHeadingClass}`),
+        const s = e.querySelector(`.${this.sectionButtonClass}`),
+            n = e.querySelector(`.${this.sectionHeadingClass}`),
             i = e.querySelector(`.${this.sectionSummaryClass}`);
-        if (!s) throw new ElementError({
+        if (!n) throw new ElementError({
             componentName: "Accordion",
             identifier: `Section heading (\`.${this.sectionHeadingClass}\`)`
         });
-        if (!n) throw new ElementError({
+        if (!s) 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(n.attributes)) "id" !== d.nodeName && o.setAttribute(d.nodeName, `${d.nodeValue}`);
+        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), a.innerHTML = n.innerHTML;
+        a.classList.add(this.sectionHeadingTextFocusClass), r.appendChild(a), Array.from(s.childNodes).forEach((e => a.appendChild(e)));
         const l = document.createElement("span");
         l.classList.add(this.sectionShowHideToggleClass), l.setAttribute("data-nosnippet", "");
         const c = document.createElement("span");
         c.classList.add(this.sectionShowHideToggleFocusClass), l.appendChild(c);
         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()), null != i && i.parentNode) {
+        if (u.classList.add(this.upChevronIconClass), c.appendChild(u), h.classList.add(this.sectionShowHideTextClass), c.appendChild(h), o.appendChild(r), o.appendChild(this.getButtonPunctuationEl()), i) {
             const e = document.createElement("span"),
                 t = document.createElement("span");
             t.classList.add(this.sectionSummaryFocusClass), e.appendChild(t);
-            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())
+            for (const s of Array.from(i.attributes)) e.setAttribute(s.name, s.value);
+            Array.from(i.childNodes).forEach((e => t.appendChild(e))), i.remove(), o.appendChild(e), o.appendChild(this.getButtonPunctuationEl())
         }
-        o.appendChild(l), s.removeChild(n), s.appendChild(o)
+        o.appendChild(l), 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)
+        const s = t.closest(`.${this.sectionClass}`);
+        s && this.setExpanded(!0, s)
     }
     onSectionToggle(e) {
-        const t = this.isExpanded(e);
-        this.setExpanded(!t, e), this.storeState(e)
+        const t = !this.isExpanded(e);
+        this.setExpanded(t, e), this.storeState(e, t)
     }
     onShowOrHideAllToggle() {
-        const e = !this.checkIfAllSectionsOpen();
+        const e = !this.areAllSectionsOpen();
         this.$sections.forEach((t => {
-            this.setExpanded(e, t), this.storeState(t)
+            this.setExpanded(e, t), this.storeState(t, e)
         })), this.updateShowAllButton(e)
     }
     setExpanded(e, t) {
-        const n = t.querySelector(`.${this.upChevronIconClass}`),
-            s = t.querySelector(`.${this.sectionShowHideTextClass}`),
+        const s = t.querySelector(`.${this.upChevronIconClass}`),
+            n = 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 (!n || !s || !i) return;
+        if (!s || !n || !i) return;
         const r = e ? this.i18n.t("hideSection") : this.i18n.t("showSection");
-        s.textContent = r, i.setAttribute("aria-expanded", `${e}`);
+        n.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), 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)
+        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)), this.updateShowAllButton(this.areAllSectionsOpen())
     }
     isExpanded(e) {
         return e.classList.contains(this.sectionExpandedClass)
     }
-    checkIfAllSectionsOpen() {
-        return this.$sections.length === this.$module.querySelectorAll(`.${this.sectionExpandedClass}`).length
+    areAllSectionsOpen() {
+        return Array.from(this.$sections).every((e => this.isExpanded(e)))
     }
     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))
     }
-    storeState(e) {
+    storeState(e, t) {
         if (this.browserSupportsSessionStorage && this.config.rememberExpanded) {
-            const t = e.querySelector(`.${this.sectionButtonClass}`);
-            if (t) {
-                const e = t.getAttribute("aria-controls"),
-                    n = t.getAttribute("aria-expanded");
-                e && n && window.sessionStorage.setItem(e, n)
+            const s = e.querySelector(`.${this.sectionButtonClass}`);
+            if (s) {
+                const e = s.getAttribute("aria-controls");
+                e && window.sessionStorage.setItem(e, t.toString())
             }
         }
     }
@@ -333,15 +328,15 @@ class Accordion extends GOVUKFrontendComponent {
         if (this.browserSupportsSessionStorage && this.config.rememberExpanded) {
             const t = e.querySelector(`.${this.sectionButtonClass}`);
             if (t) {
-                const n = t.getAttribute("aria-controls"),
-                    s = n ? window.sessionStorage.getItem(n) : null;
-                null !== s && this.setExpanded("true" === s, e)
+                const s = t.getAttribute("aria-controls"),
+                    n = s ? window.sessionStorage.getItem(s) : null;
+                null !== n && this.setExpanded("true" === n, e)
             }
         }
     }
     getButtonPunctuationEl() {
         const e = document.createElement("span");
-        return e.classList.add("govuk-visually-hidden", this.sectionHeadingDividerClass), e.innerHTML = ", ", e
+        return e.classList.add("govuk-visually-hidden", this.sectionHeadingDividerClass), e.textContent = ", ", e
     }
 }
 Accordion.moduleName = "govuk-accordion", Accordion.defaults = Object.freeze({
@@ -370,7 +365,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 (n) {
+        } catch (s) {
             return !1
         }
     }
@@ -396,8 +391,8 @@ class Button extends GOVUKFrontendComponent {
 }
 
 function closestAttributeValue(e, t) {
-    const n = e.closest(`[${t}]`);
-    return n ? n.getAttribute(t) : null
+    const s = e.closest(`[${t}]`);
+    return s ? s.getAttribute(t) : null
 }
 Button.moduleName = "govuk-button", Button.defaults = Object.freeze({
     preventDoubleClick: !1
@@ -410,7 +405,7 @@ Button.moduleName = "govuk-button", Button.defaults = Object.freeze({
 });
 class CharacterCount extends GOVUKFrontendComponent {
     constructor(e, t = {}) {
-        var n, s;
+        var s, n;
         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 +425,24 @@ class CharacterCount extends GOVUKFrontendComponent {
             maxwords: void 0
         }), this.config = mergeConfigs(CharacterCount.defaults, t, r, o);
         const a = function(e, t) {
-            const n = [];
-            for (const [s, i] of Object.entries(e)) {
+            const s = [];
+            for (const [n, i] of Object.entries(e)) {
                 const e = [];
                 if (Array.isArray(i)) {
                     for (const {
-                            required: n,
-                            errorMessage: s
+                            required: s,
+                            errorMessage: n
                         }
-                        of i) n.every((e => !!t[e])) || e.push(s);
-                    "anyOf" !== s || i.length - e.length >= 1 || n.push(...e)
+                        of i) s.every((e => !!t[e])) || e.push(n);
+                    "anyOf" !== n || i.length - e.length >= 1 || s.push(...e)
                 }
             }
-            return n
+            return s
         }(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 != (n = null != (s = this.config.maxwords) ? s : this.config.maxlength) ? n : 1 / 0, this.$module = e, this.$textarea = i;
+        }), this.maxLength = null != (s = null != (n = this.config.maxwords) ? n : this.config.maxlength) ? s : 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 +499,8 @@ class CharacterCount extends GOVUKFrontendComponent {
     }
     formatCountMessage(e, t) {
         if (0 === e) return this.i18n.t(`${t}AtLimit`);
-        const n = e < 0 ? "OverLimit" : "UnderLimit";
-        return this.i18n.t(`${t}${n}`, {
+        const s = e < 0 ? "OverLimit" : "UnderLimit";
+        return this.i18n.t(`${t}${s}`, {
             count: Math.abs(e)
         })
     }
@@ -592,10 +587,10 @@ class Checkboxes extends GOVUKFrontendComponent {
     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 s = document.getElementById(t);
+        if (null != s && s.classList.contains("govuk-checkboxes__conditional")) {
             const t = e.checked;
-            e.setAttribute("aria-expanded", t.toString()), n.classList.toggle("govuk-checkboxes__conditional--hidden", !t)
+            e.setAttribute("aria-expanded", t.toString()), s.classList.toggle("govuk-checkboxes__conditional--hidden", !t)
         }
     }
     unCheckAllInputsExcept(e) {
@@ -633,25 +628,25 @@ class ErrorSummary extends GOVUKFrontendComponent {
         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 s = this.getAssociatedLegendOrLabel(n);
-        return !!s && (s.scrollIntoView(), n.focus({
+        const s = document.getElementById(t);
+        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");
+        const s = e.closest("fieldset");
+        if (s) {
+            const t = s.getElementsByTagName("legend");
             if (t.length) {
-                const n = t[0];
-                if (e instanceof HTMLInputElement && ("checkbox" === e.type || "radio" === e.type)) return n;
-                const s = n.getBoundingClientRect().top,
+                const s = t[0];
+                if (e instanceof HTMLInputElement && ("checkbox" === e.type || "radio" === e.type)) return s;
+                const n = s.getBoundingClientRect().top,
                     i = e.getBoundingClientRect();
                 if (i.height && window.innerHeight) {
-                    if (i.top + i.height - s < window.innerHeight / 2) return n
+                    if (i.top + i.height - n < window.innerHeight / 2) return s
                 }
             }
         }
@@ -674,16 +669,16 @@ class ExitThisPage extends GOVUKFrontendComponent {
             element: e,
             identifier: "Root element (`$module`)"
         });
-        const n = e.querySelector(".govuk-exit-this-page__button");
-        if (!(n instanceof HTMLAnchorElement)) throw new ElementError({
+        const s = e.querySelector(".govuk-exit-this-page__button");
+        if (!(s instanceof HTMLAnchorElement)) throw new ElementError({
             componentName: "Exit this page",
-            element: n,
+            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 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))
+        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))
     }
     initUpdateSpan() {
         this.$updateSpan = document.createElement("span"), this.$updateSpan.setAttribute("role", "status"), this.$updateSpan.className = "govuk-visually-hidden", this.$module.appendChild(this.$updateSpan)
@@ -754,18 +749,18 @@ class Header extends GOVUKFrontendComponent {
         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({
+        const s = t.getAttribute("aria-controls");
+        if (!s) throw new ElementError({
             componentName: "Header",
             identifier: 'Navigation button (`<button class="govuk-js-header-toggle">`) attribute (`aria-controls`)'
         });
-        const s = document.getElementById(n);
-        if (!s) throw new ElementError({
+        const n = document.getElementById(s);
+        if (!n) throw new ElementError({
             componentName: "Header",
-            element: s,
-            identifier: `Navigation (\`<ul id="${n}">\`)`
+            element: n,
+            identifier: `Navigation (\`<ul id="${s}">\`)`
         });
-        this.$menu = s, this.$menuButton = t, this.setupResponsiveChecks(), this.$menuButton.addEventListener("click", (() => this.handleMenuButtonClick()))
+        this.$menu = n, this.$menuButton = t, this.setupResponsiveChecks(), this.$menuButton.addEventListener("click", (() => this.handleMenuButtonClick()))
     }
     setupResponsiveChecks() {
         const e = getBreakpoint("desktop");
@@ -809,23 +804,23 @@ class PasswordInput extends GOVUKFrontendComponent {
             element: e,
             identifier: "Root element (`$module`)"
         });
-        const n = e.querySelector(".govuk-js-password-input-input");
-        if (!(n instanceof HTMLInputElement)) throw new ElementError({
+        const s = e.querySelector(".govuk-js-password-input-input");
+        if (!(s instanceof HTMLInputElement)) throw new ElementError({
             componentName: "Password input",
-            element: n,
+            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 s = e.querySelector(".govuk-js-password-input-toggle");
-        if (!(s instanceof HTMLButtonElement)) throw new ElementError({
+        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({
             componentName: "Password input",
-            element: s,
+            element: n,
             expectedType: "HTMLButtonElement",
             identifier: "Button (`.govuk-js-password-input-toggle`)"
         });
-        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, {
+        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, {
             locale: closestAttributeValue(e, "lang")
         }), this.$showHideButton.removeAttribute("hidden");
         const i = document.createElement("div");
@@ -846,9 +841,9 @@ class PasswordInput extends GOVUKFrontendComponent {
         if (e === this.$input.type) return;
         this.$input.setAttribute("type", e);
         const t = "password" === e,
-            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`)
+            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`)
     }
 }
 PasswordInput.moduleName = "govuk-password-input", PasswordInput.defaults = Object.freeze({
@@ -896,20 +891,20 @@ class Radios extends GOVUKFrontendComponent {
     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 s = document.getElementById(t);
+        if (null != s && s.classList.contains("govuk-radios__conditional")) {
             const t = e.checked;
-            e.setAttribute("aria-expanded", t.toString()), n.classList.toggle("govuk-radios__conditional--hidden", !t)
+            e.setAttribute("aria-expanded", t.toString()), s.classList.toggle("govuk-radios__conditional--hidden", !t)
         }
     }
     handleClick(e) {
         const t = e.target;
         if (!(t instanceof HTMLInputElement) || "radio" !== t.type) return;
-        const n = document.querySelectorAll('input[type="radio"][aria-controls]'),
-            s = t.form,
+        const s = document.querySelectorAll('input[type="radio"][aria-controls]'),
+            n = t.form,
             i = t.name;
-        n.forEach((e => {
-            const t = e.form === s;
+        s.forEach((e => {
+            const t = e.form === n;
             e.name === i && t && this.syncConditionalRevealWithInputState(e)
         }))
     }
@@ -925,17 +920,17 @@ class SkipLink extends GOVUKFrontendComponent {
             identifier: "Root element (`$module`)"
         });
         this.$module = e;
-        const n = this.$module.hash,
-            s = null != (t = this.$module.getAttribute("href")) ? t : "";
+        const s = this.$module.hash,
+            n = 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="${s}"\`) is invalid`)
+            throw new ElementError(`Skip link: Target link (\`href="${n}"\`) is invalid`)
         }
         if (i.origin !== window.location.origin || i.pathname !== window.location.pathname) return;
-        const o = getFragmentFromUrl(n);
-        if (!o) throw new ElementError(`Skip link: Target link (\`href="${s}"\`) has no hash fragment`);
+        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",
@@ -966,17 +961,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 n = this.$module.querySelector(".govuk-tabs__list"),
-            s = this.$module.querySelectorAll("li.govuk-tabs__list-item");
-        if (!n) throw new ElementError({
+        const s = this.$module.querySelector(".govuk-tabs__list"),
+            n = this.$module.querySelectorAll("li.govuk-tabs__list-item");
+        if (!s) throw new ElementError({
             componentName: "Tabs",
             identifier: 'List (`<ul class="govuk-tabs__list">`)'
         });
-        if (!s.length) throw new ElementError({
+        if (!n.length) throw new ElementError({
             componentName: "Tabs",
             identifier: 'List items (`<li class="govuk-tabs__list-item">`)'
         });
-        this.$tabList = n, this.$tabListItems = s, this.setupResponsiveChecks()
+        this.$tabList = s, this.$tabListItems = n, this.setupResponsiveChecks()
     }
     setupResponsiveChecks() {
         const e = getBreakpoint("tablet");
@@ -1012,8 +1007,8 @@ class Tabs extends GOVUKFrontendComponent {
             t = this.getTab(e);
         if (!t) return;
         if (this.changingHash) return void(this.changingHash = !1);
-        const n = this.getCurrentTab();
-        n && (this.hideTab(n), this.showTab(t), t.focus())
+        const s = this.getCurrentTab();
+        s && (this.hideTab(s), this.showTab(t), t.focus())
     }
     hideTab(e) {
         this.unhighlightTab(e), this.hidePanel(e)
@@ -1028,8 +1023,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 n = this.getPanel(e);
-        n && (n.setAttribute("role", "tabpanel"), n.setAttribute("aria-labelledby", e.id), n.classList.add(this.jsHiddenClass))
+        const s = this.getPanel(e);
+        s && (s.setAttribute("role", "tabpanel"), s.setAttribute("aria-labelledby", e.id), s.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 +1033,14 @@ class Tabs extends GOVUKFrontendComponent {
     }
     onTabClick(e) {
         const t = this.getCurrentTab(),
-            n = e.currentTarget;
-        t && n instanceof HTMLAnchorElement && (e.preventDefault(), this.hideTab(t), this.showTab(n), this.createHistoryEntry(n))
+            s = e.currentTarget;
+        t && s instanceof HTMLAnchorElement && (e.preventDefault(), this.hideTab(t), this.showTab(s), this.createHistoryEntry(s))
     }
     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
+        const s = t.id;
+        t.id = "", this.changingHash = !0, window.location.hash = s, t.id = s
     }
     onTabKeydown(e) {
         switch (e.key) {
@@ -1067,16 +1062,16 @@ class Tabs extends GOVUKFrontendComponent {
         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 s = t.querySelector("a.govuk-tabs__tab");
+        s && (this.hideTab(e), 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 s = t.querySelector("a.govuk-tabs__tab");
+        s && (this.hideTab(e), this.showTab(s), s.focus(), this.createHistoryEntry(s))
     }
     getPanel(e) {
         const t = getFragmentFromUrl(e.href);
@@ -1104,7 +1099,7 @@ class Tabs extends GOVUKFrontendComponent {
 function initAll(e) {
     var t;
     if (e = void 0 !== e ? e : {}, !isSupported()) return void console.log(new SupportError);
-    const n = [
+    const s = [
             [Accordion, e.accordion],
             [Button, e.button],
             [CharacterCount, e.characterCount],
@@ -1118,19 +1113,19 @@ function initAll(e) {
             [SkipLink],
             [Tabs]
         ],
-        s = null != (t = e.scope) ? t : document;
-    n.forEach((([e, t]) => {
-        createAll(e, t, s)
+        n = null != (t = e.scope) ? t : document;
+    s.forEach((([e, t]) => {
+        createAll(e, t, n)
     }))
 }
 
-function createAll(e, t, n = document) {
-    const s = n.querySelectorAll(`[data-module="${e.moduleName}"]`);
-    return Array.from(s).map((n => {
+function createAll(e, t, s = document) {
+    const n = s.querySelectorAll(`[data-module="${e.moduleName}"]`);
+    return Array.from(n).map((s => {
         try {
-            return "defaults" in e && void 0 !== t ? new e(n, t) : new e(n)
-        } catch (s) {
-            return console.log(s), null
+            return "defaults" in e && void 0 !== t ? new e(s, t) : new e(s)
+        } catch (n) {
+            return console.log(n), null
         }
     })).filter(Boolean)
 }

Action run for bed231c

Copy link

github-actions bot commented Jun 4, 2024

Other changes to npm package

diff --git a/packages/govuk-frontend/dist/govuk/all.bundle.js b/packages/govuk-frontend/dist/govuk/all.bundle.js
index 8a123ccac..267a57bfc 100644
--- a/packages/govuk-frontend/dist/govuk/all.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/all.bundle.js
@@ -498,8 +498,7 @@
       this.browserSupportsSessionStorage = helper.checkForSessionStorage();
       this.initControls();
       this.initSectionHeaders();
-      const areAllSectionsOpen = this.checkIfAllSectionsOpen();
-      this.updateShowAllButton(areAllSectionsOpen);
+      this.updateShowAllButton(this.areAllSectionsOpen());
     }
     initControls() {
       this.$showAllButton = document.createElement('button');
@@ -556,8 +555,8 @@
       $button.setAttribute('type', 'button');
       $button.setAttribute('aria-controls', `${this.$module.id}-content-${index + 1}`);
       for (const attr of Array.from($span.attributes)) {
-        if (attr.nodeName !== 'id') {
-          $button.setAttribute(attr.nodeName, `${attr.nodeValue}`);
+        if (attr.name !== 'id') {
+          $button.setAttribute(attr.name, attr.value);
         }
       }
       const $headingText = document.createElement('span');
@@ -566,7 +565,7 @@
       const $headingTextFocus = document.createElement('span');
       $headingTextFocus.classList.add(this.sectionHeadingTextFocusClass);
       $headingText.appendChild($headingTextFocus);
-      $headingTextFocus.innerHTML = $span.innerHTML;
+      Array.from($span.childNodes).forEach($child => $headingTextFocus.appendChild($child));
       const $showHideToggle = document.createElement('span');
       $showHideToggle.classList.add(this.sectionShowHideToggleClass);
       $showHideToggle.setAttribute('data-nosnippet', '');
@@ -581,16 +580,16 @@
       $showHideToggleFocus.appendChild($showHideText);
       $button.appendChild($headingText);
       $button.appendChild(this.getButtonPunctuationEl());
-      if ($summary != null && $summary.parentNode) {
+      if ($summary) {
         const $summarySpan = document.createElement('span');
         const $summarySpanFocus = document.createElement('span');
         $summarySpanFocus.classList.add(this.sectionSummaryFocusClass);
         $summarySpan.appendChild($summarySpanFocus);
         for (const attr of Array.from($summary.attributes)) {
-          $summarySpan.setAttribute(attr.nodeName, `${attr.nodeValue}`);
+          $summarySpan.setAttribute(attr.name, attr.value);
         }
-        $summarySpanFocus.innerHTML = $summary.innerHTML;
-        $summary.parentNode.replaceChild($summarySpan, $summary);
+        Array.from($summary.childNodes).forEach($child => $summarySpanFocus.appendChild($child));
+        $summary.remove();
         $button.appendChild($summarySpan);
         $button.appendChild(this.getButtonPunctuationEl());
       }
@@ -609,15 +608,15 @@
       }
     }
     onSectionToggle($section) {
-      const expanded = this.isExpanded($section);
-      this.setExpanded(!expanded, $section);
-      this.storeState($section);
+      const nowExpanded = !this.isExpanded($section);
+      this.setExpanded(nowExpanded, $section);
+      this.storeState($section, nowExpanded);
     }
     onShowOrHideAllToggle() {
-      const nowExpanded = !this.checkIfAllSectionsOpen();
+      const nowExpanded = !this.areAllSectionsOpen();
       this.$sections.forEach($section => {
         this.setExpanded(nowExpanded, $section);
-        this.storeState($section);
+        this.storeState($section, nowExpanded);
       });
       this.updateShowAllButton(nowExpanded);
     }
@@ -659,17 +658,13 @@
         $section.classList.remove(this.sectionExpandedClass);
         $showHideIcon.classList.add(this.downChevronIconClass);
       }
-      const areAllSectionsOpen = this.checkIfAllSectionsOpen();
-      this.updateShowAllButton(areAllSectionsOpen);
+      this.updateShowAllButton(this.areAllSectionsOpen());
     }
     isExpanded($section) {
       return $section.classList.contains(this.sectionExpandedClass);
     }
-    checkIfAllSectionsOpen() {
-      const sectionsCount = this.$sections.length;
-      const expandedSectionCount = this.$module.querySelectorAll(`.${this.sectionExpandedClass}`).length;
-      const areAllSectionsOpen = sectionsCount === expandedSectionCount;
-      return areAllSectionsOpen;
+    areAllSectionsOpen() {
+      return Array.from(this.$sections).every($section => this.isExpanded($section));
     }
     updateShowAllButton(expanded) {
       if (!this.$showAllButton || !this.$showAllText || !this.$showAllIcon) {
@@ -679,14 +674,13 @@
       this.$showAllText.textContent = expanded ? this.i18n.t('hideAllSections') : this.i18n.t('showAllSections');
       this.$showAllIcon.classList.toggle(this.downChevronIconClass, !expanded);
     }
-    storeState($section) {
+    storeState($section, isExpanded) {
       if (this.browserSupportsSessionStorage && this.config.rememberExpanded) {
         const $button = $section.querySelector(`.${this.sectionButtonClass}`);
         if ($button) {
           const contentId = $button.getAttribute('aria-controls');
-          const contentState = $button.getAttribute('aria-expanded');
-          if (contentId && contentState) {
-            window.sessionStorage.setItem(contentId, contentState);
+          if (contentId) {
+            window.sessionStorage.setItem(contentId, isExpanded.toString());
           }
         }
       }
@@ -706,7 +700,7 @@
     getButtonPunctuationEl() {
       const $punctuationEl = document.createElement('span');
       $punctuationEl.classList.add('govuk-visually-hidden', this.sectionHeadingDividerClass);
-      $punctuationEl.innerHTML = ', ';
+      $punctuationEl.textContent = ', ';
       return $punctuationEl;
     }
   }
diff --git a/packages/govuk-frontend/dist/govuk/all.bundle.mjs b/packages/govuk-frontend/dist/govuk/all.bundle.mjs
index 79ce345b0..cbc00c0b8 100644
--- a/packages/govuk-frontend/dist/govuk/all.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/all.bundle.mjs
@@ -492,8 +492,7 @@ class Accordion extends GOVUKFrontendComponent {
     this.browserSupportsSessionStorage = helper.checkForSessionStorage();
     this.initControls();
     this.initSectionHeaders();
-    const areAllSectionsOpen = this.checkIfAllSectionsOpen();
-    this.updateShowAllButton(areAllSectionsOpen);
+    this.updateShowAllButton(this.areAllSectionsOpen());
   }
   initControls() {
     this.$showAllButton = document.createElement('button');
@@ -550,8 +549,8 @@ class Accordion extends GOVUKFrontendComponent {
     $button.setAttribute('type', 'button');
     $button.setAttribute('aria-controls', `${this.$module.id}-content-${index + 1}`);
     for (const attr of Array.from($span.attributes)) {
-      if (attr.nodeName !== 'id') {
-        $button.setAttribute(attr.nodeName, `${attr.nodeValue}`);
+      if (attr.name !== 'id') {
+        $button.setAttribute(attr.name, attr.value);
       }
     }
     const $headingText = document.createElement('span');
@@ -560,7 +559,7 @@ class Accordion extends GOVUKFrontendComponent {
     const $headingTextFocus = document.createElement('span');
     $headingTextFocus.classList.add(this.sectionHeadingTextFocusClass);
     $headingText.appendChild($headingTextFocus);
-    $headingTextFocus.innerHTML = $span.innerHTML;
+    Array.from($span.childNodes).forEach($child => $headingTextFocus.appendChild($child));
     const $showHideToggle = document.createElement('span');
     $showHideToggle.classList.add(this.sectionShowHideToggleClass);
     $showHideToggle.setAttribute('data-nosnippet', '');
@@ -575,16 +574,16 @@ class Accordion extends GOVUKFrontendComponent {
     $showHideToggleFocus.appendChild($showHideText);
     $button.appendChild($headingText);
     $button.appendChild(this.getButtonPunctuationEl());
-    if ($summary != null && $summary.parentNode) {
+    if ($summary) {
       const $summarySpan = document.createElement('span');
       const $summarySpanFocus = document.createElement('span');
       $summarySpanFocus.classList.add(this.sectionSummaryFocusClass);
       $summarySpan.appendChild($summarySpanFocus);
       for (const attr of Array.from($summary.attributes)) {
-        $summarySpan.setAttribute(attr.nodeName, `${attr.nodeValue}`);
+        $summarySpan.setAttribute(attr.name, attr.value);
       }
-      $summarySpanFocus.innerHTML = $summary.innerHTML;
-      $summary.parentNode.replaceChild($summarySpan, $summary);
+      Array.from($summary.childNodes).forEach($child => $summarySpanFocus.appendChild($child));
+      $summary.remove();
       $button.appendChild($summarySpan);
       $button.appendChild(this.getButtonPunctuationEl());
     }
@@ -603,15 +602,15 @@ class Accordion extends GOVUKFrontendComponent {
     }
   }
   onSectionToggle($section) {
-    const expanded = this.isExpanded($section);
-    this.setExpanded(!expanded, $section);
-    this.storeState($section);
+    const nowExpanded = !this.isExpanded($section);
+    this.setExpanded(nowExpanded, $section);
+    this.storeState($section, nowExpanded);
   }
   onShowOrHideAllToggle() {
-    const nowExpanded = !this.checkIfAllSectionsOpen();
+    const nowExpanded = !this.areAllSectionsOpen();
     this.$sections.forEach($section => {
       this.setExpanded(nowExpanded, $section);
-      this.storeState($section);
+      this.storeState($section, nowExpanded);
     });
     this.updateShowAllButton(nowExpanded);
   }
@@ -653,17 +652,13 @@ class Accordion extends GOVUKFrontendComponent {
       $section.classList.remove(this.sectionExpandedClass);
       $showHideIcon.classList.add(this.downChevronIconClass);
     }
-    const areAllSectionsOpen = this.checkIfAllSectionsOpen();
-    this.updateShowAllButton(areAllSectionsOpen);
+    this.updateShowAllButton(this.areAllSectionsOpen());
   }
   isExpanded($section) {
     return $section.classList.contains(this.sectionExpandedClass);
   }
-  checkIfAllSectionsOpen() {
-    const sectionsCount = this.$sections.length;
-    const expandedSectionCount = this.$module.querySelectorAll(`.${this.sectionExpandedClass}`).length;
-    const areAllSectionsOpen = sectionsCount === expandedSectionCount;
-    return areAllSectionsOpen;
+  areAllSectionsOpen() {
+    return Array.from(this.$sections).every($section => this.isExpanded($section));
   }
   updateShowAllButton(expanded) {
     if (!this.$showAllButton || !this.$showAllText || !this.$showAllIcon) {
@@ -673,14 +668,13 @@ class Accordion extends GOVUKFrontendComponent {
     this.$showAllText.textContent = expanded ? this.i18n.t('hideAllSections') : this.i18n.t('showAllSections');
     this.$showAllIcon.classList.toggle(this.downChevronIconClass, !expanded);
   }
-  storeState($section) {
+  storeState($section, isExpanded) {
     if (this.browserSupportsSessionStorage && this.config.rememberExpanded) {
       const $button = $section.querySelector(`.${this.sectionButtonClass}`);
       if ($button) {
         const contentId = $button.getAttribute('aria-controls');
-        const contentState = $button.getAttribute('aria-expanded');
-        if (contentId && contentState) {
-          window.sessionStorage.setItem(contentId, contentState);
+        if (contentId) {
+          window.sessionStorage.setItem(contentId, isExpanded.toString());
         }
       }
     }
@@ -700,7 +694,7 @@ class Accordion extends GOVUKFrontendComponent {
   getButtonPunctuationEl() {
     const $punctuationEl = document.createElement('span');
     $punctuationEl.classList.add('govuk-visually-hidden', this.sectionHeadingDividerClass);
-    $punctuationEl.innerHTML = ', ';
+    $punctuationEl.textContent = ', ';
     return $punctuationEl;
   }
 }
diff --git a/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js b/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js
index aa392371c..3dcdb7217 100644
--- a/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js
@@ -432,8 +432,7 @@
       this.browserSupportsSessionStorage = helper.checkForSessionStorage();
       this.initControls();
       this.initSectionHeaders();
-      const areAllSectionsOpen = this.checkIfAllSectionsOpen();
-      this.updateShowAllButton(areAllSectionsOpen);
+      this.updateShowAllButton(this.areAllSectionsOpen());
     }
     initControls() {
       this.$showAllButton = document.createElement('button');
@@ -490,8 +489,8 @@
       $button.setAttribute('type', 'button');
       $button.setAttribute('aria-controls', `${this.$module.id}-content-${index + 1}`);
       for (const attr of Array.from($span.attributes)) {
-        if (attr.nodeName !== 'id') {
-          $button.setAttribute(attr.nodeName, `${attr.nodeValue}`);
+        if (attr.name !== 'id') {
+          $button.setAttribute(attr.name, attr.value);
         }
       }
       const $headingText = document.createElement('span');
@@ -500,7 +499,7 @@
       const $headingTextFocus = document.createElement('span');
       $headingTextFocus.classList.add(this.sectionHeadingTextFocusClass);
       $headingText.appendChild($headingTextFocus);
-      $headingTextFocus.innerHTML = $span.innerHTML;
+      Array.from($span.childNodes).forEach($child => $headingTextFocus.appendChild($child));
       const $showHideToggle = document.createElement('span');
       $showHideToggle.classList.add(this.sectionShowHideToggleClass);
       $showHideToggle.setAttribute('data-nosnippet', '');
@@ -515,16 +514,16 @@
       $showHideToggleFocus.appendChild($showHideText);
       $button.appendChild($headingText);
       $button.appendChild(this.getButtonPunctuationEl());
-      if ($summary != null && $summary.parentNode) {
+      if ($summary) {
         const $summarySpan = document.createElement('span');
         const $summarySpanFocus = document.createElement('span');
         $summarySpanFocus.classList.add(this.sectionSummaryFocusClass);
         $summarySpan.appendChild($summarySpanFocus);
         for (const attr of Array.from($summary.attributes)) {
-          $summarySpan.setAttribute(attr.nodeName, `${attr.nodeValue}`);
+          $summarySpan.setAttribute(attr.name, attr.value);
         }
-        $summarySpanFocus.innerHTML = $summary.innerHTML;
-        $summary.parentNode.replaceChild($summarySpan, $summary);
+        Array.from($summary.childNodes).forEach($child => $summarySpanFocus.appendChild($child));
+        $summary.remove();
         $button.appendChild($summarySpan);
         $button.appendChild(this.getButtonPunctuationEl());
       }
@@ -543,15 +542,15 @@
       }
     }
     onSectionToggle($section) {
-      const expanded = this.isExpanded($section);
-      this.setExpanded(!expanded, $section);
-      this.storeState($section);
+      const nowExpanded = !this.isExpanded($section);
+      this.setExpanded(nowExpanded, $section);
+      this.storeState($section, nowExpanded);
     }
     onShowOrHideAllToggle() {
-      const nowExpanded = !this.checkIfAllSectionsOpen();
+      const nowExpanded = !this.areAllSectionsOpen();
       this.$sections.forEach($section => {
         this.setExpanded(nowExpanded, $section);
-        this.storeState($section);
+        this.storeState($section, nowExpanded);
       });
       this.updateShowAllButton(nowExpanded);
     }
@@ -593,17 +592,13 @@
         $section.classList.remove(this.sectionExpandedClass);
         $showHideIcon.classList.add(this.downChevronIconClass);
       }
-      const areAllSectionsOpen = this.checkIfAllSectionsOpen();
-      this.updateShowAllButton(areAllSectionsOpen);
+      this.updateShowAllButton(this.areAllSectionsOpen());
     }
     isExpanded($section) {
       return $section.classList.contains(this.sectionExpandedClass);
     }
-    checkIfAllSectionsOpen() {
-      const sectionsCount = this.$sections.length;
-      const expandedSectionCount = this.$module.querySelectorAll(`.${this.sectionExpandedClass}`).length;
-      const areAllSectionsOpen = sectionsCount === expandedSectionCount;
-      return areAllSectionsOpen;
+    areAllSectionsOpen() {
+      return Array.from(this.$sections).every($section => this.isExpanded($section));
     }
     updateShowAllButton(expanded) {
       if (!this.$showAllButton || !this.$showAllText || !this.$showAllIcon) {
@@ -613,14 +608,13 @@
       this.$showAllText.textContent = expanded ? this.i18n.t('hideAllSections') : this.i18n.t('showAllSections');
       this.$showAllIcon.classList.toggle(this.downChevronIconClass, !expanded);
     }
-    storeState($section) {
+    storeState($section, isExpanded) {
       if (this.browserSupportsSessionStorage && this.config.rememberExpanded) {
         const $button = $section.querySelector(`.${this.sectionButtonClass}`);
         if ($button) {
           const contentId = $button.getAttribute('aria-controls');
-          const contentState = $button.getAttribute('aria-expanded');
-          if (contentId && contentState) {
-            window.sessionStorage.setItem(contentId, contentState);
+          if (contentId) {
+            window.sessionStorage.setItem(contentId, isExpanded.toString());
           }
         }
       }
@@ -640,7 +634,7 @@
     getButtonPunctuationEl() {
       const $punctuationEl = document.createElement('span');
       $punctuationEl.classList.add('govuk-visually-hidden', this.sectionHeadingDividerClass);
-      $punctuationEl.innerHTML = ', ';
+      $punctuationEl.textContent = ', ';
       return $punctuationEl;
     }
   }
diff --git a/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs
index 74fcadec2..c78c0e64e 100644
--- a/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs
@@ -426,8 +426,7 @@ class Accordion extends GOVUKFrontendComponent {
     this.browserSupportsSessionStorage = helper.checkForSessionStorage();
     this.initControls();
     this.initSectionHeaders();
-    const areAllSectionsOpen = this.checkIfAllSectionsOpen();
-    this.updateShowAllButton(areAllSectionsOpen);
+    this.updateShowAllButton(this.areAllSectionsOpen());
   }
   initControls() {
     this.$showAllButton = document.createElement('button');
@@ -484,8 +483,8 @@ class Accordion extends GOVUKFrontendComponent {
     $button.setAttribute('type', 'button');
     $button.setAttribute('aria-controls', `${this.$module.id}-content-${index + 1}`);
     for (const attr of Array.from($span.attributes)) {
-      if (attr.nodeName !== 'id') {
-        $button.setAttribute(attr.nodeName, `${attr.nodeValue}`);
+      if (attr.name !== 'id') {
+        $button.setAttribute(attr.name, attr.value);
       }
     }
     const $headingText = document.createElement('span');
@@ -494,7 +493,7 @@ class Accordion extends GOVUKFrontendComponent {
     const $headingTextFocus = document.createElement('span');
     $headingTextFocus.classList.add(this.sectionHeadingTextFocusClass);
     $headingText.appendChild($headingTextFocus);
-    $headingTextFocus.innerHTML = $span.innerHTML;
+    Array.from($span.childNodes).forEach($child => $headingTextFocus.appendChild($child));
     const $showHideToggle = document.createElement('span');
     $showHideToggle.classList.add(this.sectionShowHideToggleClass);
     $showHideToggle.setAttribute('data-nosnippet', '');
@@ -509,16 +508,16 @@ class Accordion extends GOVUKFrontendComponent {
     $showHideToggleFocus.appendChild($showHideText);
     $button.appendChild($headingText);
     $button.appendChild(this.getButtonPunctuationEl());
-    if ($summary != null && $summary.parentNode) {
+    if ($summary) {
       const $summarySpan = document.createElement('span');
       const $summarySpanFocus = document.createElement('span');
       $summarySpanFocus.classList.add(this.sectionSummaryFocusClass);
       $summarySpan.appendChild($summarySpanFocus);
       for (const attr of Array.from($summary.attributes)) {
-        $summarySpan.setAttribute(attr.nodeName, `${attr.nodeValue}`);
+        $summarySpan.setAttribute(attr.name, attr.value);
       }
-      $summarySpanFocus.innerHTML = $summary.innerHTML;
-      $summary.parentNode.replaceChild($summarySpan, $summary);
+      Array.from($summary.childNodes).forEach($child => $summarySpanFocus.appendChild($child));
+      $summary.remove();
       $button.appendChild($summarySpan);
       $button.appendChild(this.getButtonPunctuationEl());
     }
@@ -537,15 +536,15 @@ class Accordion extends GOVUKFrontendComponent {
     }
   }
   onSectionToggle($section) {
-    const expanded = this.isExpanded($section);
-    this.setExpanded(!expanded, $section);
-    this.storeState($section);
+    const nowExpanded = !this.isExpanded($section);
+    this.setExpanded(nowExpanded, $section);
+    this.storeState($section, nowExpanded);
   }
   onShowOrHideAllToggle() {
-    const nowExpanded = !this.checkIfAllSectionsOpen();
+    const nowExpanded = !this.areAllSectionsOpen();
     this.$sections.forEach($section => {
       this.setExpanded(nowExpanded, $section);
-      this.storeState($section);
+      this.storeState($section, nowExpanded);
     });
     this.updateShowAllButton(nowExpanded);
   }
@@ -587,17 +586,13 @@ class Accordion extends GOVUKFrontendComponent {
       $section.classList.remove(this.sectionExpandedClass);
       $showHideIcon.classList.add(this.downChevronIconClass);
     }
-    const areAllSectionsOpen = this.checkIfAllSectionsOpen();
-    this.updateShowAllButton(areAllSectionsOpen);
+    this.updateShowAllButton(this.areAllSectionsOpen());
   }
   isExpanded($section) {
     return $section.classList.contains(this.sectionExpandedClass);
   }
-  checkIfAllSectionsOpen() {
-    const sectionsCount = this.$sections.length;
-    const expandedSectionCount = this.$module.querySelectorAll(`.${this.sectionExpandedClass}`).length;
-    const areAllSectionsOpen = sectionsCount === expandedSectionCount;
-    return areAllSectionsOpen;
+  areAllSectionsOpen() {
+    return Array.from(this.$sections).every($section => this.isExpanded($section));
   }
   updateShowAllButton(expanded) {
     if (!this.$showAllButton || !this.$showAllText || !this.$showAllIcon) {
@@ -607,14 +602,13 @@ class Accordion extends GOVUKFrontendComponent {
     this.$showAllText.textContent = expanded ? this.i18n.t('hideAllSections') : this.i18n.t('showAllSections');
     this.$showAllIcon.classList.toggle(this.downChevronIconClass, !expanded);
   }
-  storeState($section) {
+  storeState($section, isExpanded) {
     if (this.browserSupportsSessionStorage && this.config.rememberExpanded) {
       const $button = $section.querySelector(`.${this.sectionButtonClass}`);
       if ($button) {
         const contentId = $button.getAttribute('aria-controls');
-        const contentState = $button.getAttribute('aria-expanded');
-        if (contentId && contentState) {
-          window.sessionStorage.setItem(contentId, contentState);
+        if (contentId) {
+          window.sessionStorage.setItem(contentId, isExpanded.toString());
         }
       }
     }
@@ -634,7 +628,7 @@ class Accordion extends GOVUKFrontendComponent {
   getButtonPunctuationEl() {
     const $punctuationEl = document.createElement('span');
     $punctuationEl.classList.add('govuk-visually-hidden', this.sectionHeadingDividerClass);
-    $punctuationEl.innerHTML = ', ';
+    $punctuationEl.textContent = ', ';
     return $punctuationEl;
   }
 }
diff --git a/packages/govuk-frontend/dist/govuk/components/accordion/accordion.mjs b/packages/govuk-frontend/dist/govuk/components/accordion/accordion.mjs
index 1c57601ca..86d7fc295 100644
--- a/packages/govuk-frontend/dist/govuk/components/accordion/accordion.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/accordion/accordion.mjs
@@ -73,8 +73,7 @@ class Accordion extends GOVUKFrontendComponent {
     this.browserSupportsSessionStorage = helper.checkForSessionStorage();
     this.initControls();
     this.initSectionHeaders();
-    const areAllSectionsOpen = this.checkIfAllSectionsOpen();
-    this.updateShowAllButton(areAllSectionsOpen);
+    this.updateShowAllButton(this.areAllSectionsOpen());
   }
   initControls() {
     this.$showAllButton = document.createElement('button');
@@ -131,8 +130,8 @@ class Accordion extends GOVUKFrontendComponent {
     $button.setAttribute('type', 'button');
     $button.setAttribute('aria-controls', `${this.$module.id}-content-${index + 1}`);
     for (const attr of Array.from($span.attributes)) {
-      if (attr.nodeName !== 'id') {
-        $button.setAttribute(attr.nodeName, `${attr.nodeValue}`);
+      if (attr.name !== 'id') {
+        $button.setAttribute(attr.name, attr.value);
       }
     }
     const $headingText = document.createElement('span');
@@ -141,7 +140,7 @@ class Accordion extends GOVUKFrontendComponent {
     const $headingTextFocus = document.createElement('span');
     $headingTextFocus.classList.add(this.sectionHeadingTextFocusClass);
     $headingText.appendChild($headingTextFocus);
-    $headingTextFocus.innerHTML = $span.innerHTML;
+    Array.from($span.childNodes).forEach($child => $headingTextFocus.appendChild($child));
     const $showHideToggle = document.createElement('span');
     $showHideToggle.classList.add(this.sectionShowHideToggleClass);
     $showHideToggle.setAttribute('data-nosnippet', '');
@@ -156,16 +155,16 @@ class Accordion extends GOVUKFrontendComponent {
     $showHideToggleFocus.appendChild($showHideText);
     $button.appendChild($headingText);
     $button.appendChild(this.getButtonPunctuationEl());
-    if ($summary != null && $summary.parentNode) {
+    if ($summary) {
       const $summarySpan = document.createElement('span');
       const $summarySpanFocus = document.createElement('span');
       $summarySpanFocus.classList.add(this.sectionSummaryFocusClass);
       $summarySpan.appendChild($summarySpanFocus);
       for (const attr of Array.from($summary.attributes)) {
-        $summarySpan.setAttribute(attr.nodeName, `${attr.nodeValue}`);
+        $summarySpan.setAttribute(attr.name, attr.value);
       }
-      $summarySpanFocus.innerHTML = $summary.innerHTML;
-      $summary.parentNode.replaceChild($summarySpan, $summary);
+      Array.from($summary.childNodes).forEach($child => $summarySpanFocus.appendChild($child));
+      $summary.remove();
       $button.appendChild($summarySpan);
       $button.appendChild(this.getButtonPunctuationEl());
     }
@@ -184,15 +183,15 @@ class Accordion extends GOVUKFrontendComponent {
     }
   }
   onSectionToggle($section) {
-    const expanded = this.isExpanded($section);
-    this.setExpanded(!expanded, $section);
-    this.storeState($section);
+    const nowExpanded = !this.isExpanded($section);
+    this.setExpanded(nowExpanded, $section);
+    this.storeState($section, nowExpanded);
   }
   onShowOrHideAllToggle() {
-    const nowExpanded = !this.checkIfAllSectionsOpen();
+    const nowExpanded = !this.areAllSectionsOpen();
     this.$sections.forEach($section => {
       this.setExpanded(nowExpanded, $section);
-      this.storeState($section);
+      this.storeState($section, nowExpanded);
     });
     this.updateShowAllButton(nowExpanded);
   }
@@ -234,17 +233,13 @@ class Accordion extends GOVUKFrontendComponent {
       $section.classList.remove(this.sectionExpandedClass);
       $showHideIcon.classList.add(this.downChevronIconClass);
     }
-    const areAllSectionsOpen = this.checkIfAllSectionsOpen();
-    this.updateShowAllButton(areAllSectionsOpen);
+    this.updateShowAllButton(this.areAllSectionsOpen());
   }
   isExpanded($section) {
     return $section.classList.contains(this.sectionExpandedClass);
   }
-  checkIfAllSectionsOpen() {
-    const sectionsCount = this.$sections.length;
-    const expandedSectionCount = this.$module.querySelectorAll(`.${this.sectionExpandedClass}`).length;
-    const areAllSectionsOpen = sectionsCount === expandedSectionCount;
-    return areAllSectionsOpen;
+  areAllSectionsOpen() {
+    return Array.from(this.$sections).every($section => this.isExpanded($section));
   }
   updateShowAllButton(expanded) {
     if (!this.$showAllButton || !this.$showAllText || !this.$showAllIcon) {
@@ -254,14 +249,13 @@ class Accordion extends GOVUKFrontendComponent {
     this.$showAllText.textContent = expanded ? this.i18n.t('hideAllSections') : this.i18n.t('showAllSections');
     this.$showAllIcon.classList.toggle(this.downChevronIconClass, !expanded);
   }
-  storeState($section) {
+  storeState($section, isExpanded) {
     if (this.browserSupportsSessionStorage && this.config.rememberExpanded) {
       const $button = $section.querySelector(`.${this.sectionButtonClass}`);
       if ($button) {
         const contentId = $button.getAttribute('aria-controls');
-        const contentState = $button.getAttribute('aria-expanded');
-        if (contentId && contentState) {
-          window.sessionStorage.setItem(contentId, contentState);
+        if (contentId) {
+          window.sessionStorage.setItem(contentId, isExpanded.toString());
         }
       }
     }
@@ -281,7 +275,7 @@ class Accordion extends GOVUKFrontendComponent {
   getButtonPunctuationEl() {
     const $punctuationEl = document.createElement('span');
     $punctuationEl.classList.add('govuk-visually-hidden', this.sectionHeadingDividerClass);
-    $punctuationEl.innerHTML = ', ';
+    $punctuationEl.textContent = ', ';
     return $punctuationEl;
   }
 }

Action run for bed231c

@owenatgov
Copy link
Contributor

I approved but I had a question I forgot to include: Do we think this change warrants a changelog entry? It does technically impact user-facing code but it's just a tidyup rather than an explicit bugfix.

@romaricpascal
Copy link
Member

Looking at the CHANGELOG, we do have a few refactoring mentioned in 'Fixes' sections (like the first item in 4.4.0). Thinking it's helpful to mention it in the changelog: in case an unforeseen bug crops up in the accordion, the entry may give pointers to the person looking into what's happening. @36degrees Would you mind adding a 'Fixes' entry, please ? 😊

@36degrees 36degrees changed the title Tidy up the accordion JavaScript Refactor the accordion JavaScript Jun 11, 2024
@36degrees
Copy link
Contributor Author

@owenatgov @romaricpascal done 👍🏻

Copy link
Member

@romaricpascal romaricpascal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@36degrees Cheers! I think it'll need a little rebase for Percy and then good to go!

Rather than checking how many sections have the expanded class, check if all sections have the class.
`Element.attributes` returns a NamedNodeMap [1] which represents a collection of of `Attr` objects [1].

`Attr` objects are a specialised class of `Node` which have `name` and `value` properties [2].

Prefer the specialised `name` and `value` properties over the `nodeName` and `nodeValue` properties which are inherited from `Node` [3].

Both `nodeName` and `name` return the qualified name of the attribute (including any namespace) [4].

As Typescript knows that `Attr.value` will always return a string, we no longer need to cast it using the template literal.

[1]: https://developer.mozilla.org/en-US/docs/Web/API/Element/attributes
[2]: https://dom.spec.whatwg.org/#attr
[3]: https://dom.spec.whatwg.org/#node
[4]: https://dom.spec.whatwg.org/#dom-node-nodename
We ‘know’ the expanded state in both places we call storeState, so pass it to the function rather than reading the state back out of the DOM.
It’d be nice to use `replaceChildren`, but alas the browser support is not there.
There is no point replacing the existing `$summary` with the `$summarySpan` [^1] as we end up moving the `$summarySpan` into the `$button` on the next line anyway.

All we really need to do is remove the existing summary, which is much simpler. Because we don’t need to traverse to the parent we can also simplify the guard for this block of code by removing the check for `$summary.parentNode`.

[^1]: Note that the argument order for `Node.replaceChild` may not be what you expect (`replaceChild(newChild, oldChild)`)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants