-
Notifications
You must be signed in to change notification settings - Fork 334
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 button element for entire input replacement #5609
base: spike-enhanced-file-upload
Are you sure you want to change the base?
Conversation
📋 StatsFile sizes
Modules
View stats and visualisations on the review app Action run for 8b5dd7b |
JavaScript changes to npm packagediff --git a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
index 341ec36d6..ce7616fb6 100644
--- a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
+++ b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
@@ -757,9 +757,13 @@ class FileUpload extends ConfigurableComponent {
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));
+ s.classList.add("govuk-file-upload__button"), s.type = "button";
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__pseudo-button", i.innerText = this.i18n.t("selectFilesButton");
+ const o = document.createElement("span");
+ o.className = "govuk-visually-hidden", o.innerText = ", ", s.appendChild(i), s.appendChild(o), s.addEventListener("click", this.onClick.bind(this));
+ const r = document.createElement("span");
+ r.className = "govuk-body govuk-file-upload__status", r.innerText = this.i18n.t("filesSelectedDefault"), s.appendChild(r), n.insertAdjacentElement("beforeend", s), this.$root.insertAdjacentElement("afterend", n), this.$root.setAttribute("tabindex", "-1"), this.$root.setAttribute("aria-hidden", "true"), n.insertAdjacentElement("afterbegin", this.$root), this.$wrapper = n, this.$button = s, this.$status = r, this.$root.addEventListener("change", this.onChange.bind(this)), this.updateDisabledState(), this.observeDisabledState(), 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;
Action run for 8b5dd7b |
Stylesheets changes to npm packagediff --git a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.css b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.css
index f1b48d569..e057b6a41 100644
--- a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.css
+++ b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.css
@@ -3397,7 +3397,7 @@ screen and (forced-colors:active) {
background-color: #fff
}
-.govuk-file-upload-wrapper--show-dropzone .govuk-file-upload__button,
+.govuk-file-upload-wrapper--show-dropzone .govuk-file-upload__pseudo-button,
.govuk-file-upload-wrapper--show-dropzone .govuk-file-upload__status {
pointer-events: none
}
@@ -3413,7 +3413,7 @@ screen and (forced-colors:active) {
opacity: 0
}
-.govuk-file-upload__button {
+.govuk-file-upload__pseudo-button {
width: auto;
margin-bottom: 0;
flex-grow: 0;
@@ -3425,6 +3425,53 @@ screen and (forced-colors:active) {
margin-left: 10px
}
+.govuk-file-upload__button:focus {
+ outline: none
+}
+
+.govuk-file-upload__button:focus .govuk-file-upload__pseudo-button {
+ outline: 3px solid transparent;
+ background-color: #fd0;
+ box-shadow: 0 2px 0 #0b0c0c
+}
+
+.govuk-file-upload__button:focus .govuk-file-upload__pseudo-button:hover {
+ border-color: #fd0;
+ outline: 3px solid transparent;
+ background-color: #c2c2c1;
+ box-shadow: inset 0 0 0 1px #fd0
+}
+
+.govuk-file-upload__button:active .govuk-file-upload__pseudo-button:hover {
+ background-color: #c2c2c1
+}
+
+.govuk-file-upload__button {
+ align-items: center;
+ display: flex;
+ padding: 0;
+ border: 0;
+ background-color: transparent
+}
+
+.govuk-file-upload:disabled+.govuk-file-upload__button {
+ pointer-events: none
+}
+
+.govuk-file-upload:disabled+.govuk-file-upload__button .govuk-file-upload__pseudo-button {
+ opacity: .5
+}
+
+.govuk-file-upload:disabled+.govuk-file-upload__button .govuk-file-upload__pseudo-button:hover {
+ background-color: #f3f2f1;
+ cursor: not-allowed
+}
+
+.govuk-file-upload:disabled+.govuk-file-upload__button .govuk-file-upload__pseudo-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 8b5dd7b |
Other changes to npm packagediff --git a/packages/govuk-frontend/dist/govuk/all.bundle.js b/packages/govuk-frontend/dist/govuk/all.bundle.js
index 63161d0ec..da9b0a9e3 100644
--- a/packages/govuk-frontend/dist/govuk/all.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/all.bundle.js
@@ -1681,25 +1681,32 @@
const $wrapper = document.createElement('div');
$wrapper.className = 'govuk-file-upload-wrapper';
const $button = document.createElement('button');
- $button.className = 'govuk-button govuk-button--secondary govuk-file-upload__button';
+ $button.classList.add('govuk-file-upload__button');
$button.type = 'button';
- $button.innerText = this.i18n.t('selectFilesButton');
+ const buttonSpan = document.createElement('span');
+ buttonSpan.className = 'govuk-button govuk-button--secondary govuk-file-upload__pseudo-button';
+ buttonSpan.innerText = this.i18n.t('selectFilesButton');
+ const commaSpan = document.createElement('span');
+ commaSpan.className = 'govuk-visually-hidden';
+ commaSpan.innerText = ', ';
+ $button.appendChild(buttonSpan);
+ $button.appendChild(commaSpan);
$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');
+ $button.appendChild($status);
$wrapper.insertAdjacentElement('beforeend', $button);
- $wrapper.insertAdjacentElement('beforeend', $status);
this.$root.insertAdjacentElement('afterend', $wrapper);
+ this.$root.setAttribute('tabindex', '-1');
+ this.$root.setAttribute('aria-hidden', 'true');
$wrapper.insertAdjacentElement('afterbegin', this.$root);
this.$wrapper = $wrapper;
this.$button = $button;
this.$status = $status;
- this.$root.setAttribute('tabindex', '-1');
+ this.$root.addEventListener('change', this.onChange.bind(this));
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));
diff --git a/packages/govuk-frontend/dist/govuk/all.bundle.mjs b/packages/govuk-frontend/dist/govuk/all.bundle.mjs
index 34f9a09f1..7d33c7599 100644
--- a/packages/govuk-frontend/dist/govuk/all.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/all.bundle.mjs
@@ -1675,25 +1675,32 @@ class FileUpload extends ConfigurableComponent {
const $wrapper = document.createElement('div');
$wrapper.className = 'govuk-file-upload-wrapper';
const $button = document.createElement('button');
- $button.className = 'govuk-button govuk-button--secondary govuk-file-upload__button';
+ $button.classList.add('govuk-file-upload__button');
$button.type = 'button';
- $button.innerText = this.i18n.t('selectFilesButton');
+ const buttonSpan = document.createElement('span');
+ buttonSpan.className = 'govuk-button govuk-button--secondary govuk-file-upload__pseudo-button';
+ buttonSpan.innerText = this.i18n.t('selectFilesButton');
+ const commaSpan = document.createElement('span');
+ commaSpan.className = 'govuk-visually-hidden';
+ commaSpan.innerText = ', ';
+ $button.appendChild(buttonSpan);
+ $button.appendChild(commaSpan);
$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');
+ $button.appendChild($status);
$wrapper.insertAdjacentElement('beforeend', $button);
- $wrapper.insertAdjacentElement('beforeend', $status);
this.$root.insertAdjacentElement('afterend', $wrapper);
+ this.$root.setAttribute('tabindex', '-1');
+ this.$root.setAttribute('aria-hidden', 'true');
$wrapper.insertAdjacentElement('afterbegin', this.$root);
this.$wrapper = $wrapper;
this.$button = $button;
this.$status = $status;
- this.$root.setAttribute('tabindex', '-1');
+ this.$root.addEventListener('change', this.onChange.bind(this));
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));
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..77b01af86 100644
--- a/packages/govuk-frontend/dist/govuk/components/file-upload/_index.scss
+++ b/packages/govuk-frontend/dist/govuk/components/file-upload/_index.scss
@@ -64,7 +64,7 @@
border: $govuk-border-width-form-element dashed $govuk-input-border-colour;
background-color: $govuk-body-background-colour;
- .govuk-file-upload__button,
+ .govuk-file-upload__pseudo-button,
.govuk-file-upload__status {
// When the dropzone is hovered over, make these aspects not accept
// mouse events, so dropped files fall through to the input beneath them
@@ -85,7 +85,7 @@
opacity: 0;
}
- .govuk-file-upload__button {
+ .govuk-file-upload__pseudo-button {
width: auto;
margin-bottom: 0;
flex-grow: 0;
@@ -98,4 +98,51 @@
}
}
+.govuk-file-upload__button:focus {
+ outline: none;
+}
+
+.govuk-file-upload__button:focus .govuk-file-upload__pseudo-button {
+ outline: 3px solid transparent;
+ background-color: $govuk-focus-colour;
+ box-shadow: 0 2px 0 govuk-colour("black");
+}
+
+.govuk-file-upload__button:focus .govuk-file-upload__pseudo-button:hover {
+ border-color: $govuk-focus-colour;
+ outline: 3px solid transparent;
+ background-color: govuk-shade(govuk-colour("light-grey"), 20%);
+ box-shadow: inset 0 0 0 1px $govuk-focus-colour;
+}
+
+.govuk-file-upload__button:active .govuk-file-upload__pseudo-button:hover {
+ background-color: govuk-shade(govuk-colour("light-grey"), 20%);
+}
+
+.govuk-file-upload__button {
+ align-items: center;
+ display: flex;
+ padding: 0;
+ border: 0;
+ background-color: transparent;
+}
+
+.govuk-file-upload:disabled + .govuk-file-upload__button {
+ pointer-events: none;
+}
+
+.govuk-file-upload:disabled + .govuk-file-upload__button .govuk-file-upload__pseudo-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..5f1dff7f0 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
@@ -508,25 +508,32 @@
const $wrapper = document.createElement('div');
$wrapper.className = 'govuk-file-upload-wrapper';
const $button = document.createElement('button');
- $button.className = 'govuk-button govuk-button--secondary govuk-file-upload__button';
+ $button.classList.add('govuk-file-upload__button');
$button.type = 'button';
- $button.innerText = this.i18n.t('selectFilesButton');
+ const buttonSpan = document.createElement('span');
+ buttonSpan.className = 'govuk-button govuk-button--secondary govuk-file-upload__pseudo-button';
+ buttonSpan.innerText = this.i18n.t('selectFilesButton');
+ const commaSpan = document.createElement('span');
+ commaSpan.className = 'govuk-visually-hidden';
+ commaSpan.innerText = ', ';
+ $button.appendChild(buttonSpan);
+ $button.appendChild(commaSpan);
$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');
+ $button.appendChild($status);
$wrapper.insertAdjacentElement('beforeend', $button);
- $wrapper.insertAdjacentElement('beforeend', $status);
this.$root.insertAdjacentElement('afterend', $wrapper);
+ this.$root.setAttribute('tabindex', '-1');
+ this.$root.setAttribute('aria-hidden', 'true');
$wrapper.insertAdjacentElement('afterbegin', this.$root);
this.$wrapper = $wrapper;
this.$button = $button;
this.$status = $status;
- this.$root.setAttribute('tabindex', '-1');
+ this.$root.addEventListener('change', this.onChange.bind(this));
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));
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..f8d999539 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
@@ -502,25 +502,32 @@ class FileUpload extends ConfigurableComponent {
const $wrapper = document.createElement('div');
$wrapper.className = 'govuk-file-upload-wrapper';
const $button = document.createElement('button');
- $button.className = 'govuk-button govuk-button--secondary govuk-file-upload__button';
+ $button.classList.add('govuk-file-upload__button');
$button.type = 'button';
- $button.innerText = this.i18n.t('selectFilesButton');
+ const buttonSpan = document.createElement('span');
+ buttonSpan.className = 'govuk-button govuk-button--secondary govuk-file-upload__pseudo-button';
+ buttonSpan.innerText = this.i18n.t('selectFilesButton');
+ const commaSpan = document.createElement('span');
+ commaSpan.className = 'govuk-visually-hidden';
+ commaSpan.innerText = ', ';
+ $button.appendChild(buttonSpan);
+ $button.appendChild(commaSpan);
$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');
+ $button.appendChild($status);
$wrapper.insertAdjacentElement('beforeend', $button);
- $wrapper.insertAdjacentElement('beforeend', $status);
this.$root.insertAdjacentElement('afterend', $wrapper);
+ this.$root.setAttribute('tabindex', '-1');
+ this.$root.setAttribute('aria-hidden', 'true');
$wrapper.insertAdjacentElement('afterbegin', this.$root);
this.$wrapper = $wrapper;
this.$button = $button;
this.$status = $status;
- this.$root.setAttribute('tabindex', '-1');
+ this.$root.addEventListener('change', this.onChange.bind(this));
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));
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..909f51397 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
@@ -31,25 +31,32 @@ class FileUpload extends ConfigurableComponent {
const $wrapper = document.createElement('div');
$wrapper.className = 'govuk-file-upload-wrapper';
const $button = document.createElement('button');
- $button.className = 'govuk-button govuk-button--secondary govuk-file-upload__button';
+ $button.classList.add('govuk-file-upload__button');
$button.type = 'button';
- $button.innerText = this.i18n.t('selectFilesButton');
+ const buttonSpan = document.createElement('span');
+ buttonSpan.className = 'govuk-button govuk-button--secondary govuk-file-upload__pseudo-button';
+ buttonSpan.innerText = this.i18n.t('selectFilesButton');
+ const commaSpan = document.createElement('span');
+ commaSpan.className = 'govuk-visually-hidden';
+ commaSpan.innerText = ', ';
+ $button.appendChild(buttonSpan);
+ $button.appendChild(commaSpan);
$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');
+ $button.appendChild($status);
$wrapper.insertAdjacentElement('beforeend', $button);
- $wrapper.insertAdjacentElement('beforeend', $status);
this.$root.insertAdjacentElement('afterend', $wrapper);
+ this.$root.setAttribute('tabindex', '-1');
+ this.$root.setAttribute('aria-hidden', 'true');
$wrapper.insertAdjacentElement('afterbegin', this.$root);
this.$wrapper = $wrapper;
this.$button = $button;
this.$status = $status;
- this.$root.setAttribute('tabindex', '-1');
+ this.$root.addEventListener('change', this.onChange.bind(this));
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));
Action run for 8b5dd7b |
086e37a
to
cf6cfad
Compare
I've done some testing with assistive technologies. Details are in the testing spreadsheet (in the '16 Jan 2025' tab).
I suspect the only items from that list that are potentially gnarly are the two screen reader issues. We should definitely try to fix them for a bit. But the VO macOS / Safari issue has the lowest priority because of the pre-existing issue with the native input. It's worth trying to isolate the issue with NVDA to see where the issue comes from. It's possible that it's a bug in Firefox or NVDA. |
Addressed a few comments:
|
What
Instead of replacing the native input with a collection of elements, replace the native input with a button that has the replacement encapsulated within it. Involves making a "pseudo-button" that has the same styling as a regular button.
Hide the file input with
display: none
andtabindex = '-1'
.Why
Fixes #5612