From d79ece2d0162ac46923c0b18fb31c932da6e26ce Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Wed, 26 Mar 2025 14:49:11 -0400 Subject: [PATCH 01/10] 2138 - Change cancel button to uploading button for CSV --- app/assets/javascripts/fileUpload.js | 20 +++++++++++++++---- .../uswds/{_legacy-styles.scss => _main.scss} | 14 +++++++++++++ app/assets/sass/uswds/styles.scss | 2 +- tests/javascripts/fileUpload.test.js | 9 +++++---- 4 files changed, 36 insertions(+), 9 deletions(-) rename app/assets/sass/uswds/{_legacy-styles.scss => _main.scss} (96%) diff --git a/app/assets/javascripts/fileUpload.js b/app/assets/javascripts/fileUpload.js index ea6c7ac62..4b49bf06e 100644 --- a/app/assets/javascripts/fileUpload.js +++ b/app/assets/javascripts/fileUpload.js @@ -5,9 +5,21 @@ this.submit = () => this.$form.trigger('submit'); - this.showCancelButton = () => $('.file-upload-button', this.$form).replaceWith(` - Cancel upload - `); + this.showCancelButton = () => { + $('.file-upload-button', this.$form).replaceWith(` + + `); + + // Screen reader live region + const $srStatus = $('#upload-status-live'); + if ($srStatus.length) { + $srStatus.text('File is uploading'); + } else { + this.$form.prepend(`File is uploading`); + } + }; this.start = function(component) { @@ -17,7 +29,7 @@ // users see a button that looks like the others on the site. this.$form.find('label.file-upload-button').addClass('usa-button margin-bottom-1').attr( {role: 'button', tabindex: '0'} ); - + // Clear the form if the user navigates back to the page $(window).on("pageshow", () => this.$form[0].reset()); diff --git a/app/assets/sass/uswds/_legacy-styles.scss b/app/assets/sass/uswds/_main.scss similarity index 96% rename from app/assets/sass/uswds/_legacy-styles.scss rename to app/assets/sass/uswds/_main.scss index 4af2cc6f6..db6b166ff 100644 --- a/app/assets/sass/uswds/_legacy-styles.scss +++ b/app/assets/sass/uswds/_main.scss @@ -344,3 +344,17 @@ h2.recipient-list { } } } + +// Button ellipses loading + +.dot-anim::after { + content: '.'; + animation: dotPulse 1.5s steps(3, end) infinite; +} + +@keyframes dotPulse { + 0% { content: ''; } + 33% { content: '.'; } + 66% { content: '..'; } + 100% { content: '...'; } +} diff --git a/app/assets/sass/uswds/styles.scss b/app/assets/sass/uswds/styles.scss index 304e2f346..ce6adcbbb 100644 --- a/app/assets/sass/uswds/styles.scss +++ b/app/assets/sass/uswds/styles.scss @@ -1,5 +1,5 @@ @forward "uswds-theme"; @forward "uswds"; @forward "uswds-theme-custom-styles"; -@forward "legacy-styles"; +@forward "main"; @forward "data-visualization"; diff --git a/tests/javascripts/fileUpload.test.js b/tests/javascripts/fileUpload.test.js index 224a9ea16..29c8b711e 100644 --- a/tests/javascripts/fileUpload.test.js +++ b/tests/javascripts/fileUpload.test.js @@ -72,10 +72,11 @@ describe('File upload', () => { }); - test("It should add a link to cancel the upload by reloading the page", () => { - - expect(form.querySelector("a[href='']")).not.toBeNull(); - + test("It should display a disabled Uploading button", () => { + const uploadingButton = form.querySelector("button.uploading-button"); + expect(uploadingButton).not.toBeNull(); + expect(uploadingButton.textContent).toMatch(/Uploading/); + expect(uploadingButton.getAttribute('aria-disabled')).toBe("true"); }); }); From 7813ce7914172577d3b1a1a33d6848a3aa44f2b4 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Fri, 28 Mar 2025 10:36:17 -0400 Subject: [PATCH 02/10] Accessibility updates --- app/assets/javascripts/fileUpload.js | 29 +++++++++++--------- app/templates/components/file-upload.html | 12 +++++--- app/templates/views/check/column-errors.html | 4 +-- app/templates/views/send.html | 1 + 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/app/assets/javascripts/fileUpload.js b/app/assets/javascripts/fileUpload.js index 4b49bf06e..c9fa79ff4 100644 --- a/app/assets/javascripts/fileUpload.js +++ b/app/assets/javascripts/fileUpload.js @@ -12,12 +12,13 @@ `); - // Screen reader live region const $srStatus = $('#upload-status-live'); if ($srStatus.length) { - $srStatus.text('File is uploading'); - } else { - this.$form.prepend(`File is uploading`); + // Clear and re-set the content to ensure it's treated as a change + $srStatus.html(''); + setTimeout(() => { + $srStatus.html('File is uploading'); + }, 50); } }; @@ -25,19 +26,21 @@ this.$form = $(component); - // The label gets styled like a button and is used to hide the native file upload control. This is so that - // users see a button that looks like the others on the site. - - this.$form.find('label.file-upload-button').addClass('usa-button margin-bottom-1').attr( {role: 'button', tabindex: '0'} ); + // Handle "Upload your file" button click — CSP-safe version + this.$form.on('click', '[data-module="upload-trigger"]', function () { + const inputId = $(this).data('file-input-id'); + const fileInput = document.getElementById(inputId); + if (fileInput) fileInput.click(); + }); // Clear the form if the user navigates back to the page $(window).on("pageshow", () => this.$form[0].reset()); - // Need to put the event on the container, not the input for it to work properly - this.$form.on( - 'change', '.file-upload-field', - () => this.submit() && this.showCancelButton() - ); + // Watch for file input changes + this.$form.on('change', '.file-upload-field', () => { + this.submit(); + this.showCancelButton(); + }); }; diff --git a/app/templates/components/file-upload.html b/app/templates/components/file-upload.html index 27989ee8f..9a52b9eb6 100644 --- a/app/templates/components/file-upload.html +++ b/app/templates/components/file-upload.html @@ -27,17 +27,21 @@ {{ field(**{ 'class': 'file-upload-field', + 'id': field.name, 'accept': allowed_file_extensions|format_list_items('.{item}')|join(',')|e }) }} - + {% if alternate_link and alternate_link_text %} or {{ alternate_link_text }} {% endif %} - + {{ usaButton({ "text": "Submit", "classes": "file-upload-submit" }) }} diff --git a/app/templates/views/check/column-errors.html b/app/templates/views/check/column-errors.html index a24b5de3e..3b527fed6 100644 --- a/app/templates/views/check/column-errors.html +++ b/app/templates/views/check/column-errors.html @@ -21,7 +21,6 @@ Error {% block maincolumn_content %}
- {% call banner_wrapper(type='dangerous') %} {% if recipients.too_many_rows %} @@ -132,7 +131,6 @@ Error {% endif %} - {% endcall %}
@@ -150,7 +148,7 @@ Error ) }} {% endif %} - Back to top + Back to top {% endif %} diff --git a/app/templates/views/send.html b/app/templates/views/send.html index eec54ed72..3e6c4af9b 100644 --- a/app/templates/views/send.html +++ b/app/templates/views/send.html @@ -27,6 +27,7 @@ show_errors=False )}} +

Your file needs to look like this example

From f55d47e3d15c47a61bc6adfbd0fee224b54cbfdc Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Fri, 28 Mar 2025 11:00:13 -0400 Subject: [PATCH 03/10] Update column-errors.html --- app/templates/views/check/column-errors.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/templates/views/check/column-errors.html b/app/templates/views/check/column-errors.html index 3b527fed6..bf4904eff 100644 --- a/app/templates/views/check/column-errors.html +++ b/app/templates/views/check/column-errors.html @@ -21,6 +21,7 @@ Error {% block maincolumn_content %}
+ {% call banner_wrapper(type='dangerous') %} {% if recipients.too_many_rows %} @@ -131,6 +132,7 @@ Error {% endif %} + {% endcall %}
From 2064fc864a65447972cc68efd56922f843acd982 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Tue, 1 Apr 2025 10:46:56 -0400 Subject: [PATCH 04/10] Added an sr-only alert to the site for upload errors (and any other errors we might want to only announce to the screen reader) --- app/assets/javascripts/fileUpload.js | 32 +++- app/templates/base.html | 5 + app/templates/components/banner.html | 4 - app/templates/views/check/column-errors.html | 3 + app/templates/views/check/ok.html | 2 + app/templates/views/check/row-errors.html | 2 + poetry.lock | 190 ++----------------- 7 files changed, 54 insertions(+), 184 deletions(-) diff --git a/app/assets/javascripts/fileUpload.js b/app/assets/javascripts/fileUpload.js index c9fa79ff4..a246651e5 100644 --- a/app/assets/javascripts/fileUpload.js +++ b/app/assets/javascripts/fileUpload.js @@ -1,3 +1,25 @@ +function announceUploadStatusFromElement() { + const srRegion = document.getElementById('upload-status-live'); + const success = document.getElementById('upload-success'); + const error = document.getElementById('upload-error'); + + if (!srRegion) return; + + const message = error?.textContent || success?.textContent; + + if (message) { + srRegion.textContent = ''; + setTimeout(() => { + srRegion.textContent = message; + console.log(message); + }, 50); + } +} + +document.addEventListener('DOMContentLoaded', () => { + announceUploadStatusFromElement(); +}); + (function(Modules) { "use strict"; @@ -11,19 +33,9 @@ Uploading `); - - const $srStatus = $('#upload-status-live'); - if ($srStatus.length) { - // Clear and re-set the content to ensure it's treated as a change - $srStatus.html(''); - setTimeout(() => { - $srStatus.html('File is uploading'); - }, 50); - } }; this.start = function(component) { - this.$form = $(component); // Handle "Upload your file" button click — CSP-safe version diff --git a/app/templates/base.html b/app/templates/base.html index c37937cdb..aeec8b6e9 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -11,6 +11,11 @@ {% include "components/head.html" %} +
+
{% block bodyStart %} {% block extra_javascripts_before_body %} diff --git a/app/templates/components/banner.html b/app/templates/components/banner.html index 5ea9383d9..ccbc24225 100644 --- a/app/templates/components/banner.html +++ b/app/templates/components/banner.html @@ -4,10 +4,6 @@ {% macro banner(body, type=None, with_tick=False, delete_button=None, subhead=None, context=None, action=None, id=None, thing=None) %}
{% call banner_wrapper(type='dangerous') %} + {# Alert for users of AT #} + File upload failed + {% if recipients.too_many_rows %}