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

use output #5609

Draft
wants to merge 1 commit into
base: spike-enhanced-file-upload
Choose a base branch
from
Draft

Conversation

patrickpatrickpatrick
Copy link
Contributor

  • [WIP] Spike progressively enhanced file upload
  • click label instead of input
  • Add aria-hidden and tabindex -1 to input
  • Add multiple Nunjucks parameter
  • Tweaks to dropzone styles
  • Remove setting aria-hidden
  • Syncronise disabled state between input and button
  • Add i18n parameters
  • Write JavaScript functionality tests
  • Fix component not rendering plural objects correctly
  • Add Nunjucks parameter documentation
  • Add tests for disable state syncronisation
  • Show dropzone when dragover page and not input
  • Change dragover to dragenter
  • Add tests for initialisation errors specific to the component
  • Make FileUpload extend ConfigurableComponent
  • Format error message consistently
  • Remove comments ignoring TypeScript or linting errors
  • Remove unnecessary if statements
  • Encapsulate lookup of the <label> element
  • Remove unnecessary check for existence of the files attribute
  • try using role button

@patrickpatrickpatrick patrickpatrickpatrick changed the base branch from main to spike-enhanced-file-upload January 15, 2025 15:03
Copy link

github-actions bot commented Jan 15, 2025

📋 Stats

File sizes

File Size
dist/govuk-frontend-development.min.css 119.31 KiB
dist/govuk-frontend-development.min.js 44.98 KiB
packages/govuk-frontend/dist/govuk/all.bundle.js 97.64 KiB
packages/govuk-frontend/dist/govuk/all.bundle.mjs 91.66 KiB
packages/govuk-frontend/dist/govuk/all.mjs 1.32 KiB
packages/govuk-frontend/dist/govuk/govuk-frontend-component.mjs 1.74 KiB
packages/govuk-frontend/dist/govuk/govuk-frontend.min.css 119.29 KiB
packages/govuk-frontend/dist/govuk/govuk-frontend.min.js 44.97 KiB
packages/govuk-frontend/dist/govuk/i18n.mjs 5.55 KiB
packages/govuk-frontend/dist/govuk/init.mjs 7.5 KiB

Modules

File Size (bundled) Size (minified)
all.mjs 86.83 KiB 42.54 KiB
accordion.mjs 26.58 KiB 13.41 KiB
button.mjs 9.09 KiB 3.78 KiB
character-count.mjs 25.39 KiB 10.9 KiB
checkboxes.mjs 7.81 KiB 3.42 KiB
error-summary.mjs 10.99 KiB 4.54 KiB
exit-this-page.mjs 20.2 KiB 10.34 KiB
file-upload.mjs 17.4 KiB 8.13 KiB
header.mjs 6.46 KiB 3.22 KiB
notification-banner.mjs 9.35 KiB 3.7 KiB
password-input.mjs 18.24 KiB 8.33 KiB
radios.mjs 6.81 KiB 2.98 KiB
service-navigation.mjs 6.44 KiB 3.26 KiB
skip-link.mjs 6.4 KiB 2.76 KiB
tabs.mjs 12.04 KiB 6.67 KiB

View stats and visualisations on the review app


Action run for cf6cfad

Copy link

github-actions bot commented Jan 15, 2025

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 341ec36d6..e7985b334 100644
--- a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
+++ b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
@@ -14,13 +14,13 @@ function getBreakpoint(t) {
 
 function setFocus(t, e = {}) {
     var n;
-    const s = t.getAttribute("tabindex");
+    const i = t.getAttribute("tabindex");
 
     function onBlur() {
         var n;
-        null == (n = e.onBlur) || n.call(t), s || t.removeAttribute("tabindex")
+        null == (n = e.onBlur) || n.call(t), i || t.removeAttribute("tabindex")
     }
-    s || t.setAttribute("tabindex", "-1"), t.addEventListener("focus", (function() {
+    i || t.setAttribute("tabindex", "-1"), t.addEventListener("focus", (function() {
         t.addEventListener("blur", onBlur, {
             once: !0
         })
@@ -64,11 +64,11 @@ class ElementError extends GOVUKFrontendError {
         if ("object" == typeof t) {
             const {
                 component: n,
-                identifier: s,
-                element: i,
+                identifier: i,
+                element: s,
                 expectedType: o
             } = t;
-            e = s, e += i ? ` is not of type ${null!=o?o:"HTMLElement"}` : " not found", e = formatErrorMessage(n, e)
+            e = i, e += s ? ` is not of type ${null!=o?o:"HTMLElement"}` : " not found", e = formatErrorMessage(n, e)
         }
         super(e), this.name = "ElementError"
     }
@@ -118,57 +118,57 @@ class ConfigurableComponent extends GOVUKFrontendComponent {
     }
     constructor(e, n) {
         super(e), this._config = void 0;
-        const s = this.constructor;
-        if (void 0 === s.defaults) throw new ConfigError(formatErrorMessage(s, "Config passed as parameter into constructor but no defaults defined"));
-        const i = function(Component, t) {
+        const i = this.constructor;
+        if (void 0 === i.defaults) throw new ConfigError(formatErrorMessage(i, "Config passed as parameter into constructor but no defaults defined"));
+        const s = function(Component, t) {
             if (void 0 === Component.schema) throw new ConfigError(formatErrorMessage(Component, "Config passed as parameter into constructor but no schema defined"));
             const e = {};
-            for (const [n, s] of Object.entries(Component.schema.properties)) n in t && (e[n] = normaliseString(t[n], s)), "object" === (null == s ? void 0 : s.type) && (e[n] = extractConfigByNamespace(Component.schema, t, n));
+            for (const [n, i] of Object.entries(Component.schema.properties)) n in t && (e[n] = normaliseString(t[n], i)), "object" === (null == i ? void 0 : i.type) && (e[n] = extractConfigByNamespace(Component.schema, t, n));
             return e
-        }(s, this._$root.dataset);
-        this._config = mergeConfigs(s.defaults, null != n ? n : {}, this[t](i), i)
+        }(i, this._$root.dataset);
+        this._config = mergeConfigs(i.defaults, null != n ? n : {}, this[t](s), s)
     }
 }
 
 function normaliseString(t, e) {
     const n = t ? t.trim() : "";
-    let s, i = null == e ? void 0 : e.type;
-    switch (i || (["true", "false"].includes(n) && (i = "boolean"), n.length > 0 && isFinite(Number(n)) && (i = "number")), i) {
+    let i, s = null == e ? void 0 : e.type;
+    switch (s || (["true", "false"].includes(n) && (s = "boolean"), n.length > 0 && isFinite(Number(n)) && (s = "number")), s) {
         case "boolean":
-            s = "true" === n;
+            i = "true" === n;
             break;
         case "number":
-            s = Number(n);
+            i = Number(n);
             break;
         default:
-            s = t
+            i = t
     }
-    return s
+    return i
 }
 
 function mergeConfigs(...t) {
     const e = {};
     for (const n of t)
         for (const t of Object.keys(n)) {
-            const s = e[t],
-                i = n[t];
-            isObject(s) && isObject(i) ? e[t] = mergeConfigs(s, i) : e[t] = i
+            const i = e[t],
+                s = n[t];
+            isObject(i) && isObject(s) ? e[t] = mergeConfigs(i, s) : e[t] = s
         }
     return e
 }
 
 function extractConfigByNamespace(t, e, n) {
-    const s = t.properties[n];
-    if ("object" !== (null == s ? void 0 : s.type)) return;
-    const i = {
+    const i = t.properties[n];
+    if ("object" !== (null == i ? void 0 : i.type)) return;
+    const s = {
         [n]: {}
     };
     for (const [o, r] of Object.entries(e)) {
-        let t = i;
+        let t = s;
         const e = o.split(".");
-        for (const [s, i] of e.entries()) "object" == typeof t && (s < e.length - 1 ? (isObject(t[i]) || (t[i] = {}), t = t[i]) : o !== n && (t[i] = normaliseString(r)))
+        for (const [i, s] of e.entries()) "object" == typeof t && (i < e.length - 1 ? (isObject(t[s]) || (t[s] = {}), t = t[s]) : o !== n && (t[s] = normaliseString(r)))
     }
-    return i[n]
+    return s[n]
 }
 class I18n {
     constructor(t = {}, e = {}) {
@@ -179,8 +179,8 @@ class I18n {
         if (!t) throw new Error("i18n: lookup key missing");
         let n = this.translations[t];
         if ("number" == typeof(null == e ? void 0 : e.count) && "object" == typeof n) {
-            const s = n[this.getPluralSuffix(t, e.count)];
-            s && (n = s)
+            const i = n[this.getPluralSuffix(t, e.count)];
+            i && (n = i)
         }
         if ("string" == typeof n) {
             if (n.match(/%{(.\S+)}/)) {
@@ -193,9 +193,9 @@ class I18n {
     }
     replacePlaceholders(t, e) {
         const n = Intl.NumberFormat.supportedLocalesOf(this.locale).length ? new Intl.NumberFormat(this.locale) : void 0;
-        return t.replace(/%{(.\S+)}/g, (function(t, s) {
-            if (Object.prototype.hasOwnProperty.call(e, s)) {
-                const t = e[s];
+        return t.replace(/%{(.\S+)}/g, (function(t, i) {
+            if (Object.prototype.hasOwnProperty.call(e, i)) {
+                const t = e[i];
                 return !1 === t || "number" != typeof t && "string" != typeof t ? "" : "number" == typeof t ? n ? n.format(t) : `${t}` : t
             }
             throw new Error(`i18n: no data found to replace ${t} placeholder in string`)
@@ -207,10 +207,10 @@ class I18n {
     getPluralSuffix(t, e) {
         if (e = Number(e), !isFinite(e)) return "other";
         const n = this.translations[t],
-            s = this.hasIntlPluralRulesSupport() ? new Intl.PluralRules(this.locale).select(e) : this.selectPluralFormUsingFallbackRules(e);
+            i = this.hasIntlPluralRulesSupport() ? new Intl.PluralRules(this.locale).select(e) : this.selectPluralFormUsingFallbackRules(e);
         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"
+            if (i in n) return i;
+            if ("other" in n) return console.warn(`i18n: Missing plural form ".${i}" for "${this.locale}" locale. Falling back to ".other".`), "other"
         }
         throw new Error(`i18n: Plural form ".other" is required for "${this.locale}" locale`)
     }
@@ -279,9 +279,9 @@ class Accordion extends ConfigurableComponent {
     }
     constructHeaderMarkup(t, e) {
         const n = t.querySelector(`.${this.sectionButtonClass}`),
-            s = t.querySelector(`.${this.sectionHeadingClass}`),
-            i = t.querySelector(`.${this.sectionSummaryClass}`);
-        if (!s) throw new ElementError({
+            i = t.querySelector(`.${this.sectionHeadingClass}`),
+            s = t.querySelector(`.${this.sectionSummaryClass}`);
+        if (!i) throw new ElementError({
             component: Accordion,
             identifier: `Section heading (\`.${this.sectionHeadingClass}\`)`
         });
@@ -302,14 +302,14 @@ class Accordion extends ConfigurableComponent {
         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()), i) {
+        if (u.classList.add(this.upChevronIconClass), c.appendChild(u), h.classList.add(this.sectionShowHideTextClass), c.appendChild(h), o.appendChild(r), o.appendChild(this.getButtonPunctuationEl()), s) {
             const t = document.createElement("span"),
                 e = document.createElement("span");
             e.classList.add(this.sectionSummaryFocusClass), t.appendChild(e);
-            for (const n of Array.from(i.attributes)) t.setAttribute(n.name, n.value);
-            Array.from(i.childNodes).forEach((t => e.appendChild(t))), i.remove(), o.appendChild(t), o.appendChild(this.getButtonPunctuationEl())
+            for (const n of Array.from(s.attributes)) t.setAttribute(n.name, n.value);
+            Array.from(s.childNodes).forEach((t => e.appendChild(t))), s.remove(), o.appendChild(t), o.appendChild(this.getButtonPunctuationEl())
         }
-        o.appendChild(l), s.removeChild(n), s.appendChild(o)
+        o.appendChild(l), i.removeChild(n), i.appendChild(o)
     }
     onBeforeMatch(t) {
         const e = t.target;
@@ -329,23 +329,23 @@ class Accordion extends ConfigurableComponent {
     }
     setExpanded(t, e) {
         const n = e.querySelector(`.${this.upChevronIconClass}`),
-            s = e.querySelector(`.${this.sectionShowHideTextClass}`),
-            i = e.querySelector(`.${this.sectionButtonClass}`),
+            i = e.querySelector(`.${this.sectionShowHideTextClass}`),
+            s = e.querySelector(`.${this.sectionButtonClass}`),
             o = e.querySelector(`.${this.sectionContentClass}`);
         if (!o) throw new ElementError({
             component: Accordion,
             identifier: `Section content (\`<div class="${this.sectionContentClass}">\`)`
         });
-        if (!n || !s || !i) return;
+        if (!n || !i || !s) return;
         const r = t ? this.i18n.t("hideSection") : this.i18n.t("showSection");
-        s.textContent = r, i.setAttribute("aria-expanded", `${t}`);
+        i.textContent = r, s.setAttribute("aria-expanded", `${t}`);
         const a = [],
             l = e.querySelector(`.${this.sectionHeadingTextClass}`);
         l && a.push(`${l.textContent}`.trim());
         const c = e.querySelector(`.${this.sectionSummaryClass}`);
         c && a.push(`${c.textContent}`.trim());
         const h = t ? this.i18n.t("hideSectionAriaLabel") : this.i18n.t("showSectionAriaLabel");
-        a.push(h), i.setAttribute("aria-label", a.join(" , ")), t ? (o.removeAttribute("hidden"), e.classList.add(this.sectionExpandedClass), n.classList.remove(this.downChevronIconClass)) : (o.setAttribute("hidden", "until-found"), e.classList.remove(this.sectionExpandedClass), n.classList.add(this.downChevronIconClass)), this.updateShowAllButton(this.areAllSectionsOpen())
+        a.push(h), s.setAttribute("aria-label", a.join(" , ")), t ? (o.removeAttribute("hidden"), e.classList.add(this.sectionExpandedClass), n.classList.remove(this.downChevronIconClass)) : (o.setAttribute("hidden", "until-found"), e.classList.remove(this.sectionExpandedClass), n.classList.add(this.downChevronIconClass)), this.updateShowAllButton(this.areAllSectionsOpen())
     }
     isExpanded(t) {
         return t.classList.contains(this.sectionExpandedClass)
@@ -365,7 +365,7 @@ class Accordion extends ConfigurableComponent {
         const n = this.getIdentifier(t);
         if (n) try {
             window.sessionStorage.setItem(n, e.toString())
-        } catch (s) {}
+        } catch (i) {}
     }
     setInitialState(t) {
         if (!this.config.rememberExpanded) return;
@@ -437,26 +437,26 @@ class CharacterCount extends ConfigurableComponent {
         }), e
     }
     constructor(t, e = {}) {
-        var n, s;
+        var n, i;
         super(t, e), this.$textarea = void 0, this.$visibleCountMessage = void 0, this.$screenReaderCountMessage = void 0, this.lastInputTimestamp = null, this.lastInputValue = "", this.valueChecker = null, this.i18n = void 0, this.maxLength = void 0;
-        const i = this.$root.querySelector(".govuk-js-character-count");
-        if (!(i instanceof HTMLTextAreaElement || i instanceof HTMLInputElement)) throw new ElementError({
+        const s = this.$root.querySelector(".govuk-js-character-count");
+        if (!(s instanceof HTMLTextAreaElement || s instanceof HTMLInputElement)) throw new ElementError({
             component: CharacterCount,
-            element: i,
+            element: s,
             expectedType: "HTMLTextareaElement or HTMLInputElement",
             identifier: "Form field (`.govuk-js-character-count`)"
         });
         const o = function(t, e) {
             const n = [];
-            for (const [s, i] of Object.entries(t)) {
+            for (const [i, s] of Object.entries(t)) {
                 const t = [];
-                if (Array.isArray(i)) {
+                if (Array.isArray(s)) {
                     for (const {
                             required: n,
-                            errorMessage: s
+                            errorMessage: i
                         }
-                        of i) n.every((t => !!e[t])) || t.push(s);
-                    "anyOf" !== s || i.length - t.length >= 1 || n.push(...t)
+                        of s) n.every((t => !!e[t])) || t.push(i);
+                    "anyOf" !== i || s.length - t.length >= 1 || n.push(...t)
                 }
             }
             return n
@@ -464,7 +464,7 @@ class CharacterCount extends ConfigurableComponent {
         if (o[0]) throw new ConfigError(formatErrorMessage(CharacterCount, o[0]));
         this.i18n = new I18n(this.config.i18n, {
             locale: closestAttributeValue(this.$root, "lang")
-        }), this.maxLength = null != (n = null != (s = this.config.maxwords) ? s : this.config.maxlength) ? n : 1 / 0, this.$textarea = i;
+        }), this.maxLength = null != (n = null != (i = this.config.maxwords) ? i : this.config.maxlength) ? n : 1 / 0, this.$textarea = s;
         const r = `${this.$textarea.id}-info`,
             a = document.getElementById(r);
         if (!a) throw new ElementError({
@@ -643,8 +643,8 @@ class ErrorSummary extends ConfigurableComponent {
         if (!e) return !1;
         const n = document.getElementById(e);
         if (!n) return !1;
-        const s = this.getAssociatedLegendOrLabel(n);
-        return !!s && (s.scrollIntoView(), n.focus({
+        const i = this.getAssociatedLegendOrLabel(n);
+        return !!i && (i.scrollIntoView(), n.focus({
             preventScroll: !0
         }), !0)
     }
@@ -656,10 +656,10 @@ class ErrorSummary extends ConfigurableComponent {
             if (e.length) {
                 const n = e[0];
                 if (t instanceof HTMLInputElement && ("checkbox" === t.type || "radio" === t.type)) return n;
-                const s = n.getBoundingClientRect().top,
-                    i = t.getBoundingClientRect();
-                if (i.height && window.innerHeight) {
-                    if (i.top + i.height - s < window.innerHeight / 2) return n
+                const i = n.getBoundingClientRect().top,
+                    s = t.getBoundingClientRect();
+                if (s.height && window.innerHeight) {
+                    if (s.top + s.height - i < window.innerHeight / 2) return n
                 }
             }
         }
@@ -686,8 +686,8 @@ class ExitThisPage extends ConfigurableComponent {
             identifier: "Button (`.govuk-exit-this-page__button`)"
         });
         this.i18n = new I18n(this.config.i18n), 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))
+        const i = document.querySelector(".govuk-js-exit-this-page-skiplink");
+        i instanceof HTMLAnchorElement && (this.$skiplinkButton = i), this.buildIndicator(), this.initUpdateSpan(), this.initButtonClickHandler(), "govukFrontendExitThisPageKeypress" in document.body.dataset || (document.addEventListener("keyup", this.handleKeypress.bind(this), !0), document.body.dataset.govukFrontendExitThisPageKeypress = "true"), window.addEventListener("pageshow", this.resetPage.bind(this))
     }
     initUpdateSpan() {
         this.$updateSpan = document.createElement("span"), this.$updateSpan.setAttribute("role", "status"), this.$updateSpan.className = "govuk-visually-hidden", this.$root.appendChild(this.$updateSpan)
@@ -756,10 +756,10 @@ class FileUpload extends ConfigurableComponent {
         }), this.$label = this.findLabel();
         const n = document.createElement("div");
         n.className = "govuk-file-upload-wrapper";
-        const s = document.createElement("button");
-        s.className = "govuk-button govuk-button--secondary govuk-file-upload__button", s.type = "button", s.innerText = this.i18n.t("selectFilesButton"), s.addEventListener("click", this.onClick.bind(this));
         const i = document.createElement("span");
-        i.className = "govuk-body govuk-file-upload__status", i.innerText = this.i18n.t("filesSelectedDefault"), i.setAttribute("role", "status"), n.insertAdjacentElement("beforeend", s), n.insertAdjacentElement("beforeend", i), this.$root.insertAdjacentElement("afterend", n), n.insertAdjacentElement("afterbegin", this.$root), this.$wrapper = n, this.$button = s, this.$status = i, this.$root.setAttribute("tabindex", "-1"), this.updateDisabledState(), this.observeDisabledState(), this.$root.addEventListener("change", this.onChange.bind(this)), this.$wrapper.addEventListener("drop", this.onDragLeaveOrDrop.bind(this)), document.addEventListener("dragenter", this.onDragEnter.bind(this)), document.addEventListener("dragleave", this.onDragLeaveOrDrop.bind(this))
+        i.className = "govuk-button govuk-button--secondary govuk-file-upload__button", i.innerText = this.i18n.t("selectFilesButton"), i.setAttribute("role", "button"), i.setAttribute("aria-hidden", "true"), i.addEventListener("click", this.onClick.bind(this));
+        const s = document.createElement("span");
+        s.className = "govuk-body govuk-file-upload__status", s.innerText = this.i18n.t("filesSelectedDefault"), s.setAttribute("aria-hidden", "true"), n.insertAdjacentElement("beforeend", i), n.insertAdjacentElement("beforeend", s), this.$root.insertAdjacentElement("afterend", n), n.insertAdjacentElement("afterbegin", this.$root), this.$wrapper = n, this.$button = i, this.$status = s, this.$root.addEventListener("change", this.onChange.bind(this)), this.$wrapper.addEventListener("drop", this.onDragLeaveOrDrop.bind(this)), document.addEventListener("dragenter", this.onDragEnter.bind(this)), document.addEventListener("dragleave", this.onDragLeaveOrDrop.bind(this))
     }
     onChange() {
         const t = this.$root.files.length;
@@ -784,16 +784,6 @@ class FileUpload extends ConfigurableComponent {
     onDragLeaveOrDrop() {
         this.$wrapper.classList.remove("govuk-file-upload-wrapper--show-dropzone")
     }
-    observeDisabledState() {
-        new MutationObserver((t => {
-            for (const e of t) console.log("mutation", e), "attributes" === e.type && "disabled" === e.attributeName && this.updateDisabledState()
-        })).observe(this.$root, {
-            attributes: !0
-        })
-    }
-    updateDisabledState() {
-        this.$button.disabled = this.$root.disabled
-    }
 }
 FileUpload.moduleName = "govuk-file-upload", FileUpload.defaults = Object.freeze({
     i18n: {
@@ -821,13 +811,13 @@ class Header extends GOVUKFrontendComponent {
             component: Header,
             identifier: 'Navigation button (`<button class="govuk-js-header-toggle">`) attribute (`aria-controls`)'
         });
-        const s = document.getElementById(n);
-        if (!s) throw new ElementError({
+        const i = document.getElementById(n);
+        if (!i) throw new ElementError({
             component: Header,
-            element: s,
+            element: i,
             identifier: `Navigation (\`<ul id="${n}">\`)`
         });
-        this.$menu = s, this.$menuButton = e, this.setupResponsiveChecks(), this.$menuButton.addEventListener("click", (() => this.handleMenuButtonClick()))
+        this.$menu = i, this.$menuButton = e, this.setupResponsiveChecks(), this.$menuButton.addEventListener("click", (() => this.handleMenuButtonClick()))
     }
     setupResponsiveChecks() {
         const t = getBreakpoint("desktop");
@@ -870,19 +860,19 @@ class PasswordInput extends ConfigurableComponent {
             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 = this.$root.querySelector(".govuk-js-password-input-toggle");
-        if (!(s instanceof HTMLButtonElement)) throw new ElementError({
+        const i = this.$root.querySelector(".govuk-js-password-input-toggle");
+        if (!(i instanceof HTMLButtonElement)) throw new ElementError({
             component: PasswordInput,
-            element: s,
+            element: i,
             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.$input = n, this.$showHideButton = s, this.i18n = new I18n(this.config.i18n, {
+        if ("button" !== i.type) throw new ElementError("Password input: Button (`.govuk-js-password-input-toggle`) must be of type `button`.");
+        this.$input = n, this.$showHideButton = i, this.i18n = new I18n(this.config.i18n, {
             locale: closestAttributeValue(this.$root, "lang")
         }), this.$showHideButton.removeAttribute("hidden");
-        const i = document.createElement("div");
-        i.className = "govuk-password-input__sr-status govuk-visually-hidden", i.setAttribute("aria-live", "polite"), this.$screenReaderStatusMessage = i, this.$input.insertAdjacentElement("afterend", i), this.$showHideButton.addEventListener("click", this.toggle.bind(this)), this.$input.form && this.$input.form.addEventListener("submit", (() => this.hide())), window.addEventListener("pageshow", (t => {
+        const s = document.createElement("div");
+        s.className = "govuk-password-input__sr-status govuk-visually-hidden", s.setAttribute("aria-live", "polite"), this.$screenReaderStatusMessage = s, this.$input.insertAdjacentElement("afterend", s), this.$showHideButton.addEventListener("click", this.toggle.bind(this)), this.$input.form && this.$input.form.addEventListener("submit", (() => this.hide())), window.addEventListener("pageshow", (t => {
             t.persisted && "password" !== this.$input.type && this.hide()
         })), this.hide()
     }
@@ -900,8 +890,8 @@ class PasswordInput extends ConfigurableComponent {
         this.$input.setAttribute("type", t);
         const e = "password" === t,
             n = e ? "show" : "hide",
-            s = e ? "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`)
+            i = e ? "passwordHidden" : "passwordShown";
+        this.$showHideButton.innerText = this.i18n.t(`${n}Password`), this.$showHideButton.setAttribute("aria-label", this.i18n.t(`${n}PasswordAriaLabel`)), this.$screenReaderStatusMessage.innerText = this.i18n.t(`${i}Announcement`)
     }
 }
 PasswordInput.moduleName = "govuk-password-input", PasswordInput.defaults = Object.freeze({
@@ -955,11 +945,11 @@ class Radios extends GOVUKFrontendComponent {
         const e = t.target;
         if (!(e instanceof HTMLInputElement) || "radio" !== e.type) return;
         const n = document.querySelectorAll('input[type="radio"][aria-controls]'),
-            s = e.form,
-            i = e.name;
+            i = e.form,
+            s = e.name;
         n.forEach((t => {
-            const e = t.form === s;
-            t.name === i && e && this.syncConditionalRevealWithInputState(t)
+            const e = t.form === i;
+            t.name === s && e && this.syncConditionalRevealWithInputState(t)
         }))
     }
 }
@@ -974,13 +964,13 @@ class ServiceNavigation extends GOVUKFrontendComponent {
             component: ServiceNavigation,
             identifier: 'Navigation button (`<button class="govuk-js-service-navigation-toggle">`) attribute (`aria-controls`)'
         });
-        const s = document.getElementById(n);
-        if (!s) throw new ElementError({
+        const i = document.getElementById(n);
+        if (!i) throw new ElementError({
             component: ServiceNavigation,
-            element: s,
+            element: i,
             identifier: `Navigation (\`<ul id="${n}">\`)`
         });
-        this.$menu = s, this.$menuButton = e, this.setupResponsiveChecks(), this.$menuButton.addEventListener("click", (() => this.handleMenuButtonClick()))
+        this.$menu = i, this.$menuButton = e, this.setupResponsiveChecks(), this.$menuButton.addEventListener("click", (() => this.handleMenuButtonClick()))
     }
     setupResponsiveChecks() {
         const t = getBreakpoint("tablet");
@@ -1003,16 +993,16 @@ class SkipLink extends GOVUKFrontendComponent {
         var e;
         super(t);
         const n = this.$root.hash,
-            s = null != (e = this.$root.getAttribute("href")) ? e : "";
-        let i;
+            i = null != (e = this.$root.getAttribute("href")) ? e : "";
+        let s;
         try {
-            i = new window.URL(this.$root.href)
+            s = new window.URL(this.$root.href)
         } catch (a) {
-            throw new ElementError(`Skip link: Target link (\`href="${s}"\`) is invalid`)
+            throw new ElementError(`Skip link: Target link (\`href="${i}"\`) is invalid`)
         }
-        if (i.origin !== window.location.origin || i.pathname !== window.location.pathname) return;
+        if (s.origin !== window.location.origin || s.pathname !== window.location.pathname) return;
         const o = getFragmentFromUrl(n);
-        if (!o) throw new ElementError(`Skip link: Target link (\`href="${s}"\`) has no hash fragment`);
+        if (!o) throw new ElementError(`Skip link: Target link (\`href="${i}"\`) has no hash fragment`);
         const r = document.getElementById(o);
         if (!r) throw new ElementError({
             component: SkipLink,
@@ -1040,16 +1030,16 @@ class Tabs extends GOVUKFrontendComponent {
         });
         this.$tabs = e, this.boundTabClick = this.onTabClick.bind(this), this.boundTabKeydown = this.onTabKeydown.bind(this), this.boundOnHashChange = this.onHashChange.bind(this);
         const n = this.$root.querySelector(".govuk-tabs__list"),
-            s = this.$root.querySelectorAll("li.govuk-tabs__list-item");
+            i = this.$root.querySelectorAll("li.govuk-tabs__list-item");
         if (!n) throw new ElementError({
             component: Tabs,
             identifier: 'List (`<ul class="govuk-tabs__list">`)'
         });
-        if (!s.length) throw new ElementError({
+        if (!i.length) throw new ElementError({
             component: Tabs,
             identifier: 'List items (`<li class="govuk-tabs__list-item">`)'
         });
-        this.$tabList = n, this.$tabListItems = s, this.setupResponsiveChecks()
+        this.$tabList = n, this.$tabListItems = i, this.setupResponsiveChecks()
     }
     setupResponsiveChecks() {
         const t = getBreakpoint("tablet");
@@ -1191,30 +1181,30 @@ function initAll(t) {
             [SkipLink],
             [Tabs]
         ],
-        s = {
+        i = {
             scope: null != (e = t.scope) ? e : document,
             onError: t.onError
         };
     n.forEach((([Component, t]) => {
-        createAll(Component, t, s)
+        createAll(Component, t, i)
     }))
 }
 
 function createAll(Component, t, e) {
-    let n, s = document;
-    var i;
-    "object" == typeof e && (s = null != (i = e.scope) ? i : s, n = e.onError);
-    "function" == typeof e && (n = e), e instanceof HTMLElement && (s = e);
-    const o = s.querySelectorAll(`[data-module="${Component.moduleName}"]`);
+    let n, i = document;
+    var s;
+    "object" == typeof e && (i = null != (s = e.scope) ? s : i, n = e.onError);
+    "function" == typeof e && (n = e), e instanceof HTMLElement && (i = e);
+    const o = i.querySelectorAll(`[data-module="${Component.moduleName}"]`);
     return isSupported() ? Array.from(o).map((e => {
         try {
             return void 0 !== t ? new Component(e, t) : new Component(e)
-        } catch (s) {
-            return n ? n(s, {
+        } catch (i) {
+            return n ? n(i, {
                 element: e,
                 component: Component,
                 config: t
-            }) : console.log(s), null
+            }) : console.log(i), null
         }
     })).filter(Boolean) : (n ? n(new SupportError, {
         component: Component,

Action run for cf6cfad

Copy link

github-actions bot commented Jan 15, 2025

Stylesheets changes to npm package

diff --git a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.css b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.css
index f1b48d569..4d71e4193 100644
--- a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.css
+++ b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.css
@@ -3425,6 +3425,24 @@ screen and (forced-colors:active) {
     margin-left: 10px
 }
 
+.govuk-file-upload:focus+.govuk-button {
+    background-color: #fd0
+}
+
+.govuk-file-upload:disabled+.govuk-button {
+    opacity: .5
+}
+
+.govuk-file-upload:disabled+.govuk-button:hover {
+    background-color: #f3f2f1;
+    cursor: not-allowed
+}
+
+.govuk-file-upload:disabled+.govuk-button:active {
+    top: 0;
+    box-shadow: 0 2px 0 #666
+}
+
 .govuk-footer {
     font-family: GDS Transport, arial, sans-serif;
     -webkit-font-smoothing: antialiased;

Action run for cf6cfad

Copy link

github-actions bot commented Jan 15, 2025

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 63161d0ec..c9c3f336e 100644
--- a/packages/govuk-frontend/dist/govuk/all.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/all.bundle.js
@@ -1680,15 +1680,16 @@
       this.$label = this.findLabel();
       const $wrapper = document.createElement('div');
       $wrapper.className = 'govuk-file-upload-wrapper';
-      const $button = document.createElement('button');
+      const $button = document.createElement('span');
       $button.className = 'govuk-button govuk-button--secondary govuk-file-upload__button';
-      $button.type = 'button';
       $button.innerText = this.i18n.t('selectFilesButton');
+      $button.setAttribute('role', 'button');
+      $button.setAttribute('aria-hidden', 'true');
       $button.addEventListener('click', this.onClick.bind(this));
       const $status = document.createElement('span');
       $status.className = 'govuk-body govuk-file-upload__status';
       $status.innerText = this.i18n.t('filesSelectedDefault');
-      $status.setAttribute('role', 'status');
+      $status.setAttribute('aria-hidden', 'true');
       $wrapper.insertAdjacentElement('beforeend', $button);
       $wrapper.insertAdjacentElement('beforeend', $status);
       this.$root.insertAdjacentElement('afterend', $wrapper);
@@ -1696,9 +1697,6 @@
       this.$wrapper = $wrapper;
       this.$button = $button;
       this.$status = $status;
-      this.$root.setAttribute('tabindex', '-1');
-      this.updateDisabledState();
-      this.observeDisabledState();
       this.$root.addEventListener('change', this.onChange.bind(this));
       this.$wrapper.addEventListener('drop', this.onDragLeaveOrDrop.bind(this));
       document.addEventListener('dragenter', this.onDragEnter.bind(this));
@@ -1743,22 +1741,6 @@
     onDragLeaveOrDrop() {
       this.$wrapper.classList.remove('govuk-file-upload-wrapper--show-dropzone');
     }
-    observeDisabledState() {
-      const observer = new MutationObserver(mutationList => {
-        for (const mutation of mutationList) {
-          console.log('mutation', mutation);
-          if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') {
-            this.updateDisabledState();
-          }
-        }
-      });
-      observer.observe(this.$root, {
-        attributes: true
-      });
-    }
-    updateDisabledState() {
-      this.$button.disabled = this.$root.disabled;
-    }
   }
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/all.bundle.mjs b/packages/govuk-frontend/dist/govuk/all.bundle.mjs
index 34f9a09f1..15af5d0f0 100644
--- a/packages/govuk-frontend/dist/govuk/all.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/all.bundle.mjs
@@ -1674,15 +1674,16 @@ class FileUpload extends ConfigurableComponent {
     this.$label = this.findLabel();
     const $wrapper = document.createElement('div');
     $wrapper.className = 'govuk-file-upload-wrapper';
-    const $button = document.createElement('button');
+    const $button = document.createElement('span');
     $button.className = 'govuk-button govuk-button--secondary govuk-file-upload__button';
-    $button.type = 'button';
     $button.innerText = this.i18n.t('selectFilesButton');
+    $button.setAttribute('role', 'button');
+    $button.setAttribute('aria-hidden', 'true');
     $button.addEventListener('click', this.onClick.bind(this));
     const $status = document.createElement('span');
     $status.className = 'govuk-body govuk-file-upload__status';
     $status.innerText = this.i18n.t('filesSelectedDefault');
-    $status.setAttribute('role', 'status');
+    $status.setAttribute('aria-hidden', 'true');
     $wrapper.insertAdjacentElement('beforeend', $button);
     $wrapper.insertAdjacentElement('beforeend', $status);
     this.$root.insertAdjacentElement('afterend', $wrapper);
@@ -1690,9 +1691,6 @@ class FileUpload extends ConfigurableComponent {
     this.$wrapper = $wrapper;
     this.$button = $button;
     this.$status = $status;
-    this.$root.setAttribute('tabindex', '-1');
-    this.updateDisabledState();
-    this.observeDisabledState();
     this.$root.addEventListener('change', this.onChange.bind(this));
     this.$wrapper.addEventListener('drop', this.onDragLeaveOrDrop.bind(this));
     document.addEventListener('dragenter', this.onDragEnter.bind(this));
@@ -1737,22 +1735,6 @@ class FileUpload extends ConfigurableComponent {
   onDragLeaveOrDrop() {
     this.$wrapper.classList.remove('govuk-file-upload-wrapper--show-dropzone');
   }
-  observeDisabledState() {
-    const observer = new MutationObserver(mutationList => {
-      for (const mutation of mutationList) {
-        console.log('mutation', mutation);
-        if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') {
-          this.updateDisabledState();
-        }
-      }
-    });
-    observer.observe(this.$root, {
-      attributes: true
-    });
-  }
-  updateDisabledState() {
-    this.$button.disabled = this.$root.disabled;
-  }
 }
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/file-upload/_index.scss b/packages/govuk-frontend/dist/govuk/components/file-upload/_index.scss
index 34e781c51..541b5c8bd 100644
--- a/packages/govuk-frontend/dist/govuk/components/file-upload/_index.scss
+++ b/packages/govuk-frontend/dist/govuk/components/file-upload/_index.scss
@@ -98,4 +98,22 @@
   }
 }
 
+.govuk-file-upload:focus + .govuk-button {
+  background-color: $govuk-focus-colour;
+}
+
+.govuk-file-upload:disabled + .govuk-button {
+  opacity: (0.5);
+
+  &:hover {
+    background-color: govuk-colour("light-grey");
+    cursor: not-allowed;
+  }
+
+  &:active {
+    top: 0;
+    box-shadow: 0 $govuk-border-width-form-element 0 govuk-shade(govuk-colour("white"), 60%); // s0
+  }
+}
+
 /*# sourceMappingURL=_index.scss.map */
diff --git a/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js b/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js
index 87cf44235..5f9b35422 100644
--- a/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js
@@ -507,15 +507,16 @@
       this.$label = this.findLabel();
       const $wrapper = document.createElement('div');
       $wrapper.className = 'govuk-file-upload-wrapper';
-      const $button = document.createElement('button');
+      const $button = document.createElement('span');
       $button.className = 'govuk-button govuk-button--secondary govuk-file-upload__button';
-      $button.type = 'button';
       $button.innerText = this.i18n.t('selectFilesButton');
+      $button.setAttribute('role', 'button');
+      $button.setAttribute('aria-hidden', 'true');
       $button.addEventListener('click', this.onClick.bind(this));
       const $status = document.createElement('span');
       $status.className = 'govuk-body govuk-file-upload__status';
       $status.innerText = this.i18n.t('filesSelectedDefault');
-      $status.setAttribute('role', 'status');
+      $status.setAttribute('aria-hidden', 'true');
       $wrapper.insertAdjacentElement('beforeend', $button);
       $wrapper.insertAdjacentElement('beforeend', $status);
       this.$root.insertAdjacentElement('afterend', $wrapper);
@@ -523,9 +524,6 @@
       this.$wrapper = $wrapper;
       this.$button = $button;
       this.$status = $status;
-      this.$root.setAttribute('tabindex', '-1');
-      this.updateDisabledState();
-      this.observeDisabledState();
       this.$root.addEventListener('change', this.onChange.bind(this));
       this.$wrapper.addEventListener('drop', this.onDragLeaveOrDrop.bind(this));
       document.addEventListener('dragenter', this.onDragEnter.bind(this));
@@ -570,22 +568,6 @@
     onDragLeaveOrDrop() {
       this.$wrapper.classList.remove('govuk-file-upload-wrapper--show-dropzone');
     }
-    observeDisabledState() {
-      const observer = new MutationObserver(mutationList => {
-        for (const mutation of mutationList) {
-          console.log('mutation', mutation);
-          if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') {
-            this.updateDisabledState();
-          }
-        }
-      });
-      observer.observe(this.$root, {
-        attributes: true
-      });
-    }
-    updateDisabledState() {
-      this.$button.disabled = this.$root.disabled;
-    }
   }
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs
index 253fa0afd..94044a0fe 100644
--- a/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs
@@ -501,15 +501,16 @@ class FileUpload extends ConfigurableComponent {
     this.$label = this.findLabel();
     const $wrapper = document.createElement('div');
     $wrapper.className = 'govuk-file-upload-wrapper';
-    const $button = document.createElement('button');
+    const $button = document.createElement('span');
     $button.className = 'govuk-button govuk-button--secondary govuk-file-upload__button';
-    $button.type = 'button';
     $button.innerText = this.i18n.t('selectFilesButton');
+    $button.setAttribute('role', 'button');
+    $button.setAttribute('aria-hidden', 'true');
     $button.addEventListener('click', this.onClick.bind(this));
     const $status = document.createElement('span');
     $status.className = 'govuk-body govuk-file-upload__status';
     $status.innerText = this.i18n.t('filesSelectedDefault');
-    $status.setAttribute('role', 'status');
+    $status.setAttribute('aria-hidden', 'true');
     $wrapper.insertAdjacentElement('beforeend', $button);
     $wrapper.insertAdjacentElement('beforeend', $status);
     this.$root.insertAdjacentElement('afterend', $wrapper);
@@ -517,9 +518,6 @@ class FileUpload extends ConfigurableComponent {
     this.$wrapper = $wrapper;
     this.$button = $button;
     this.$status = $status;
-    this.$root.setAttribute('tabindex', '-1');
-    this.updateDisabledState();
-    this.observeDisabledState();
     this.$root.addEventListener('change', this.onChange.bind(this));
     this.$wrapper.addEventListener('drop', this.onDragLeaveOrDrop.bind(this));
     document.addEventListener('dragenter', this.onDragEnter.bind(this));
@@ -564,22 +562,6 @@ class FileUpload extends ConfigurableComponent {
   onDragLeaveOrDrop() {
     this.$wrapper.classList.remove('govuk-file-upload-wrapper--show-dropzone');
   }
-  observeDisabledState() {
-    const observer = new MutationObserver(mutationList => {
-      for (const mutation of mutationList) {
-        console.log('mutation', mutation);
-        if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') {
-          this.updateDisabledState();
-        }
-      }
-    });
-    observer.observe(this.$root, {
-      attributes: true
-    });
-  }
-  updateDisabledState() {
-    this.$button.disabled = this.$root.disabled;
-  }
 }
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs b/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs
index 1ddbc3dc5..c24e80538 100644
--- a/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs
@@ -30,15 +30,16 @@ class FileUpload extends ConfigurableComponent {
     this.$label = this.findLabel();
     const $wrapper = document.createElement('div');
     $wrapper.className = 'govuk-file-upload-wrapper';
-    const $button = document.createElement('button');
+    const $button = document.createElement('span');
     $button.className = 'govuk-button govuk-button--secondary govuk-file-upload__button';
-    $button.type = 'button';
     $button.innerText = this.i18n.t('selectFilesButton');
+    $button.setAttribute('role', 'button');
+    $button.setAttribute('aria-hidden', 'true');
     $button.addEventListener('click', this.onClick.bind(this));
     const $status = document.createElement('span');
     $status.className = 'govuk-body govuk-file-upload__status';
     $status.innerText = this.i18n.t('filesSelectedDefault');
-    $status.setAttribute('role', 'status');
+    $status.setAttribute('aria-hidden', 'true');
     $wrapper.insertAdjacentElement('beforeend', $button);
     $wrapper.insertAdjacentElement('beforeend', $status);
     this.$root.insertAdjacentElement('afterend', $wrapper);
@@ -46,9 +47,6 @@ class FileUpload extends ConfigurableComponent {
     this.$wrapper = $wrapper;
     this.$button = $button;
     this.$status = $status;
-    this.$root.setAttribute('tabindex', '-1');
-    this.updateDisabledState();
-    this.observeDisabledState();
     this.$root.addEventListener('change', this.onChange.bind(this));
     this.$wrapper.addEventListener('drop', this.onDragLeaveOrDrop.bind(this));
     document.addEventListener('dragenter', this.onDragEnter.bind(this));
@@ -93,22 +91,6 @@ class FileUpload extends ConfigurableComponent {
   onDragLeaveOrDrop() {
     this.$wrapper.classList.remove('govuk-file-upload-wrapper--show-dropzone');
   }
-  observeDisabledState() {
-    const observer = new MutationObserver(mutationList => {
-      for (const mutation of mutationList) {
-        console.log('mutation', mutation);
-        if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') {
-          this.updateDisabledState();
-        }
-      }
-    });
-    observer.observe(this.$root, {
-      attributes: true
-    });
-  }
-  updateDisabledState() {
-    this.$button.disabled = this.$root.disabled;
-  }
 }
 
 /**

Action run for cf6cfad

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.

2 participants