Skip to content

Keep keyboard focus after file upload and removal - #1351

Open
dougchestnut wants to merge 1 commit into
eclipse-pass:mainfrom
uvalib:1263-keep-file-upload-focus
Open

Keep keyboard focus after file upload and removal#1351
dougchestnut wants to merge 1 commit into
eclipse-pass:mainfrom
uvalib:1263-keep-file-upload-focus

Conversation

@dougchestnut

Copy link
Copy Markdown
Contributor

Summary

On the new-submission Files step, uploading or removing a file dropped keyboard focus (and could scroll the page to the top). Screen readers were not told that the file list changed.

  • After upload, focus moves to the new file’s Description field.
  • After Remove, if other files remain, focus moves to an adjacent file’s Remove button. If it was the last file, focus moves to the file picker.
  • SweetAlert2 no longer restores focus to the destroyed Remove button (returnFocus: false).
  • A visually hidden aria-live="polite" region announces "<fileName> uploaded successfully." and "<fileName> removed.".

Fixes eclipse-pass/main#1263

How to test

  1. Start a new submission and go to the Files step.
  2. Upload a file. Focus should be in that file’s Description field. The live region should contain "<name> uploaded successfully."
  3. Upload a second file. Focus should move to the new Description field.
  4. Remove the first file and confirm the SweetAlert. Focus should be on the remaining file’s Remove button. The live region should contain "<name> removed."
  5. Remove the last file and confirm. Focus should be on the file picker (#file-multiple-input). The page should not jump to the top.
  6. pnpm exec ember test --path dist --filter="workflow files" — 7 tests, including the three new focus cases.

SweetAlert2 restored focus to a destroyed Remove button, which dropped
focus to the document body. After upload or delete, move focus to the
next logical control and announce the change.
@sonarqubecloud

Copy link
Copy Markdown

@markpatton

Copy link
Copy Markdown
Contributor

I tested locally with success.

@jaredgalanis

Copy link
Copy Markdown
Contributor

Thanks for this — the happy path works well: removing a file and confirming now lands focus sensibly on the next file's Remove button (or back on the file input when none remain), which is a real improvement. Two behaviors introduced by the change regress the exact interaction it targets, though:

1. Cancelling the confirm dialog (or a failed delete) now strands keyboard focus on <body>. returnFocus: false is unconditional, but the manual focusSelector(...) restoration only runs on the successful-delete path. On main, sweetalert2's default (returnFocus: true) restored focus to the Remove button whenever the dialog closed, so:

  • Successful delete — focus was lost on main (swal targeted the now-removed button); this PR fixes it. 👍
  • Cancel / Esc / backdropresult.value is falsy, no focus call happens, and the default restoration is suppressed → focus drops to <body> and a keyboard user tabs from the top of the page. This worked on main.
  • Failed delete (deleted === false) — the Remove button still exists; main restored focus to it, this PR strands it (and no status message is announced alongside the flash error).

Suggested fix: restore focus to the triggering Remove button on cancel/dismiss and on failed delete — or only pass returnFocus: false when the trigger is actually going to be removed.

2. Multi-file uploads steal focus while the user is typing. Each completed upload unconditionally calls focusSelector('#file-description-<id>'). With the multiple file input (the page tip even suggests Control/Shift multi-select), each async completion yanks focus into its own description field — mid-keystroke if the user has started typing a description for an earlier file — and final focus is whichever upload finishes last. Each completion also overwrites statusMessage, so only the last "uploaded successfully" announcement is heard. Suggested fix: only move focus when the upload queue has drained, or when the user hasn't already focused something else (e.g. document.activeElement is still the body/file input).

Two smaller notes in the same area:

  • statusMessage is never cleared, so two consecutive identical messages (e.g. removing two files that share a display name back-to-back) produce no DOM mutation and the live region announces nothing the second time. Clearing the region before setting the message (or toggling paired regions) avoids it.
  • The id interpolations in focusSelector calls go through document.querySelector raw; document.getElementById(file-remove-${id}) is immune to any CSS-special characters ever appearing in ids, at zero cost.

🤖 Generated with Claude Code

@jaredgalanis

Copy link
Copy Markdown
Contributor

@dougchestnut, thank you for doing this work! Just flagging here that while I did have an agent generate the above comment I did run through and manually validate the concerns expressed therein.

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.

Keep keyboard focus after uploading or removing files in the submission wizard

3 participants