Skip to content

CLARIN-DSpace v9/Restore the dropped accessibility labels on the suggestion inputs and reject reason (X1 sweep, X-02a part C) - #1517

Open
milanmajchrak wants to merge 1 commit into
dtq-dev-9-basefrom
ufal/port-x02a-suggestion-labels-9-base
Open

CLARIN-DSpace v9/Restore the dropped accessibility labels on the suggestion inputs and reject reason (X1 sweep, X-02a part C)#1517
milanmajchrak wants to merge 1 commit into
dtq-dev-9-basefrom
ufal/port-x02a-suggestion-labels-9-base

Conversation

@milanmajchrak

Copy link
Copy Markdown
Collaborator

What

Re-applies the remaining accessibility hunks of dataquest-dev/dspace-angular #1084
(5a92fdfa89) on dtq-dev-9-base. Sync card X-02a, part C of three.

Seven templates the v9 upgrade took wholesale from vanilla 9.3. The five autocomplete inputs
(input-suggestions and its dso, validation, person and org-unit variants) carried a
placeholder and nothing else — a placeholder is not a label, so a screen reader announces them as an
unnamed edit field. The workflow reject-reason <textarea> had the same problem. The dspace theme's
home banner image had alt="".

Changes

File Change
shared/input-suggestions/input-suggestions.component.html [id]="name + '-input'" + visually-hidden label bound with [attr.for]
shared/input-suggestions/dso-input-suggestions/…html same
shared/input-suggestions/validation-suggestions/…html same, and the leftover static id="name" removed
entity-groups/…/person-suggestions/person-input-suggestions.component.html same
entity-groups/…/org-unit-suggestions/org-unit-input-suggestions.component.html same
shared/mydspace-actions/claimed-task/reject/…html id="reason" on the textarea + a label carrying the reason key
themes/dspace/app/home-page/home-news/home-news.component.html alt=""[alt]="'home.news.image.banner' | translate"
shared/input-suggestions/input-suggestions.component.spec.ts new cases (the source commit ships none)

No i18n key is added, removed or renamed — home.news.image.banner already exists in en.json5 and
cs.json5 with zero references, which is the symptom of the dropped hunk.

Three fork mistakes ported fixed, not literally

  1. class="sr-only" does not exist in Bootstrap 5 and this branch defines it nowhere
    ("bootstrap": "^5.3"; git grep -nE '^[[:space:]]*\.sr-only' -- src → 0 hits;
    .visually-hidden is at src/styles/_global-styles.scss:280). A literal port would render a
    visible label above every autocomplete.
  2. <label for="name + '-input'"> is a static attribute, not a binding — Angular emits those 15
    characters literally and the label pairs with nothing. Ported as [attr.for]="name + '-input'",
    matching the input's [id] expression, so the association is real and per-instance.
  3. validation-suggestions also carried a static id="name" on the same input the fork gave a
    [id] binding. Keeping both would leave a second element answering to id="name" in the
    submission form, so the static one is removed and only the per-instance binding remains.

Deliberately not touched

The five suggestion templates keep their existing <button class="sr-only" type="submit">. That is
pre-existing vanilla markup on this branch (29 files still use the class), it is not part of these
hunks, and rewriting it would fold an unrelated Bootstrap-5 migration into an accessibility port.
Flagged for a separate card rather than smuggled in here.

validation-suggestions also carries a pre-existing attr.aria-labelledby="fieldName" written
without brackets — a literal attribute named attr.aria-labelledby, not an aria binding. It is
vanilla, out of scope, and left alone; the new <label> is what actually names the field.

No aria attribute is added that the fork did not have. The additions are <label> elements,
id/[id], and [alt] on an <img> — no role forbids any of them.

Tests added beyond the source commit

5a92fdfa89 ships no spec. input-suggestions.component.spec.ts gains two cases that assert the
association, not the presence of a string:

test asserts
should label the input with a label that resolves to it with name='author', placeholder='Search for an author': the input has a non-empty id, a <label> exists, label.htmlFor === input.id, the label text is the placeholder, and the label carries visually-hidden
should derive the id from the name input so two instances do not collide changing name from author to subject changes the input id, and the label follows it (label.htmlFor === input.id again)

The second case is what makes this per-instance rather than "an id exists".

Negative controls — each new case proven load-bearing

Applied on top of the commit, so git checkout HEAD -- <path> restores the ported file. Reverts and
restores are proved with git diff HEAD --stat, not the bare form (git checkout <ref> -- <path>
stages what it writes, so the bare git diff is empty even when the revert landed).

  • NC-1 — revert input-suggestions.component.html to the base2 FAILED, 36 SUCCESS

    should label the input with a label that resolves to it FAILED
    	Error: Expected '' to be truthy.       <- the input has no id at all
    	Error: Expected null to be truthy.     <- there is no label
    
  • NC-2 — write the label exactly the way the fork did ([attr.for]="name + '-input'"
    for="name + '-input'") → 2 FAILED, 36 SUCCESS

    should label the input with a label that resolves to it FAILED
    	Error: Expected 'name + '-input'' to equal 'author-input'.
    should derive the id from the name input so two instances do not collide FAILED
    	Error: Expected 'name + '-input'' to equal 'subject-input'.
    

    This is fork mistake Test first touch #2 proved at runtime rather than by reading the template. The rendered
    label.htmlFor is the literal 15-character string name + '-input' — the label pairs with nothing,
    on all five suggestion inputs. Porting that hunk verbatim would have shipped five labels that name
    no control, and a grep-style test ("does the template contain <label") would have called it a pass.

  • NC-3 — keep the label, drop the input's [id] binding2 FAILED, 36 SUCCESS

    	Error: Expected '' to be truthy.
    	Error: Expected 'author-input' to equal ''.
    

    The label now points at an id nothing carries — the mirror image of NC-2, and the reason both halves
    ([id] on the input and [attr.for] on the label) are asserted together.

Testing

$ npm run test:headless -- --include='…input-suggestions.component.spec.ts'     --include='…dso-input-suggestions.component.spec.ts' --include='…validation-suggestions.component.spec.ts'     --include='…org-unit-input-suggestions.component.spec.ts'     --include='…claimed-task-actions-reject.component.spec.ts' --code-coverage=false
      ✔ should label the input with a label that resolves to it
      ✔ should derive the id from the name input so two instances do not collide
    … 36 pre-existing cases …
TOTAL: 38 SUCCESS

$ npm run lint:nobuild -- --quiet
Linting "dspace-angular"...
All files pass linting.

Every subject spec is run, not only the extended one — the specs on this branch still assert vanilla
behaviour of these templates, so they are the real regression gate. person-input-suggestions and the
themed home-news have no spec file on 9-base (checked with git ls-tree, not assumed).

npm run build:prod is the CI Run build step (build.yml:110-111) and runs on this PR.

Card X-02a is split into three PRs by theme with disjoint file sets; this is part C. Part A (#1515)
carries the 6628aaf493 (#1221) unique DOM ids, part B the search boxes.

Source: 5a92fdfa89 (dtq-dev PR #1084), partial. Sync card: X-02a.

🤖 Generated with Claude Code

@milanmajchrak
milanmajchrak force-pushed the ufal/port-x02a-suggestion-labels-9-base branch from 857e188 to bf6773d Compare September 10, 2026 17:47
…tion inputs, reject reason and banner

Card X-02a, part C of three. Seven templates the v9 upgrade took wholesale from vanilla 9.3, so the
remaining accessibility hunks of 5a92fdf were never applied.

The five autocomplete inputs (input-suggestions and its dso, validation, person and org-unit
variants) carried a placeholder and nothing else: a placeholder is not a label, so a screen reader
announced them as an unnamed edit field. Each input now gets [id]="name + '-input'" and a
visually-hidden <label> bound to the same expression. The workflow reject-reason textarea had the
same problem and now has id="reason" and a label carrying the reason placeholder key. The dspace
theme's home banner image gets the translated [alt] the fork added.

Fork mistakes ported fixed rather than literally:

1. class="sr-only" does not exist in Bootstrap 5 and 9-base defines it nowhere
   (git grep -nE '^[[:space:]]*\.sr-only' -- src => 0 hits, "bootstrap": "^5.3"); a literal port would
   render a visible label above every autocomplete. visually-hidden is used, defined at
   src/styles/_global-styles.scss:280.
2. <label for="name + '-input'"> is a static attribute, not a binding - it emits those 15 characters
   literally and pairs with nothing. It is ported as [attr.for]="name + '-input'", matching the
   input's [id] expression, so the association is real.
The banner [alt] needs one file the card did not list: src/themes/dspace/app/home-page/home-news/
home-news.component.ts. That themed component declares no imports[] at all, because its template was
pure static HTML until now, so the first use of the translate pipe fails the production build with
NG8004 "No pipe found with name 'translate'". TranslateModule is added to its imports[]. The file has
no fork delta and is claimed by no other card.

3. validation-suggestions also carried a static id="name" on the same input as the fork's new [id]
   binding. Leaving both would keep a second element answering to id="name" in the submission form,
   so the static one is removed and only the per-instance binding remains.

The five suggestion templates keep their existing <button class="sr-only" type="submit">: that is
pre-existing vanilla markup on 9-base (29 files use the class), out of scope here, and touching it
would mix an unrelated Bootstrap-5 migration into an accessibility port.

Tests: 5a92fdf ships none. input-suggestions.component.spec.ts gains cases asserting the label
resolves to the input it names (label.htmlFor === input.id, id non-empty and derived from the name
input) rather than asserting that a string is present in the template. Proven load-bearing with
negative controls - see the PR description.

Source: 5a92fdf (dtq-dev PR #1084), partial - the search boxes from the same commit are in the
sibling PR for card X-02a part B.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@milanmajchrak
milanmajchrak force-pushed the ufal/port-x02a-suggestion-labels-9-base branch from bf6773d to cb7c40a Compare September 10, 2026 17:59
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.

1 participant