CLARIN-DSpace v9/Restore the dropped accessibility labels and per-component ids for the search boxes (X1 sweep, X-02a part B) - #1516
Open
milanmajchrak wants to merge 1 commit into
Conversation
milanmajchrak
force-pushed
the
ufal/port-x02a-search-labels-9-base
branch
from
September 10, 2026 17:47
aefe116 to
cabdb94
Compare
…ent ids for the search boxes Card X-02a, part B of three. Seven templates that the v9 upgrade took wholesale from vanilla 9.3, so the accessibility hunks of 5a92fdf were never applied. Every one of these inputs is either unnamed for a screen reader or named in hardcoded English regardless of the UI language. What changes: * a visually-hidden <label> is added for each control - two in eperson-search-box (the scope select and the query input), three in starts-with-date (year, month, free-text date), one each in group-search-box, members-list, subgroups-list, groups-registry and search-navbar; * aria-label="Search input" and aria-label="Search scope", which were hardcoded English strings even in the Czech UI, become bindings on the keys that already exist unreferenced in en.json5: labelPrefix + 'search.input', labelPrefix + 'search.scope' and messagePrefix + '.search.input'. No i18n key is added, removed or renamed; * the hardcoded id="query" is replaced by a per-component id in all five components that carry it in this set. formControlName and name stay "query"/"scope"; only the DOM id changes. Three fork mistakes are ported fixed rather than literally, each for a reason that is checkable on this branch: 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 in the middle of every search form. The class used is visually-hidden, which is defined at src/styles/_global-styles.scss:280 and is position:absolute, so it adds nothing to the input-group layout. 2. <label for="startsWith + 'input'"> and <label for="'query' + '-input'"> are static attributes, not bindings: they emit those characters literally and pair with nothing. The ids here are static, so the labels use plain static for= values that match the real id. 3. The group edit page renders ds-members-list and ds-subgroups-list side by side (group-form.component.html:60 and :65) and both hardcoded id="query". Adding <label for="query"> without disambiguating first would have bound both labels to the first input. The fork also gave starts-with-date [id]="startsWith + 'input'". startsWith is mutated as the user types (starts-with-date.component.ts:123), so the id would change on every keystroke and the label association would break. A static id="startsWith-input" is used instead. Tests: 5a92fdf ships none. eperson-search-box.component.spec.ts gains cases asserting that each control's label resolves to that control (label.htmlFor === input.id, id non-empty) and that the accessible name is the translated key rather than the literal "Search input". Proven load-bearing with negative controls - see the PR description. Source: 5a92fdf (dtq-dev PR #1084), partial - the suggestion inputs from the same commit are in the sibling PR for card X-02a part C. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
milanmajchrak
force-pushed
the
ufal/port-x02a-search-labels-9-base
branch
from
September 10, 2026 17:49
cabdb94 to
3bf96c3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Re-applies the search-box half of
dataquest-dev/dspace-angular#1084 (5a92fdfa89, "Lindat todtq sync251021") on
dtq-dev-9-base. Sync card X-02a, part B of three.Seven templates that the v9 upgrade took wholesale from vanilla 9.3, so the fork's accessibility hunks
were never applied. Every one of these controls is either unnamed for a screen reader — a
placeholderis not a label — or named in hardcoded English regardless of the UI language:Changes
shared/eperson-group-list/eperson-search-box/…htmlaria-labels bound tolabelPrefix + 'search.scope' / 'search.input', ids →eperson-search-scope/eperson-search-queryshared/eperson-group-list/group-search-box/…htmlaria-labelbound, id →group-search-queryaccess-control/…/members-list/…htmlaria-labelbound tomessagePrefix + '.search.input', id →members-list-queryaccess-control/…/subgroup-list/…htmlsubgroups-list-queryaccess-control/group-registry/groups-registry.component.htmlgroups-registry-querysearch-navbar/search-navbar.component.htmlid="search-navbar-query"shared/starts-with/date/starts-with-date.component.htmlid="startsWith-input"formControlNameandnamestayquery/scopeeverywhere — only the DOM id changes, so no formwiring moves. No i18n key is added, removed or renamed: every key used here already exists in
en.json5andcs.json5with zero references, which is exactly the symptom of the dropped hunks.(
cs=2is the project's convention of keeping the English original as a comment line above thetranslation, not a duplicate key.)
Three fork mistakes ported fixed, not literally
class="sr-only"does not exist in Bootstrap 5, and this branch defines it nowhere:visually-hiddenis used instead; it is
position: absolute !important, so it adds nothing to the.input-groupflex layout.
<label for="startsWith + 'input'">and<label for="'query' + '-input'">are staticattributes, not bindings — Angular emits those characters literally and the label pairs with
nothing. The ids here are static, so plain
for="…"values matching the real id are used.ds-members-listandds-subgroups-listside by side(
group-form.component.html:60and:65) and both hardcodedid="query". Adding<label for="query">before disambiguating would have bound both labels to the first input.Hence the per-component ids.
One more, which the card calls out and the fork got wrong: the fork bound the date input as
[id]="startsWith + 'input'".startsWithis reassigned as the user types(
starts-with-date.component.ts:123), so the id — and therefore the label association — would changeon every keystroke. A static
id="startsWith-input"is used.No aria attribute is added that the fork did not have, and nothing gains an aria attribute its role
forbids: the changes are
<label>elements plusaria-labelon<input type="text">and<select>,both of which permit it.
Tests added beyond the source commit
eperson-search-boxshould label the scope select and the query input, each resolving to its own controllabel.htmlForis non-empty and resolves to an element whoseidequals it; each label carriesvisually-hiddeneperson-search-boxshould name both controls from the translated keys rather than hardcoded Englisharia-labels are the translation keys (an empty catalogue echoes the key), and the ids are no longer the genericquery/scopemembers-listshould label its search input with an id of its own, not the generic id="query"query, and avisually-hiddenlabel resolves to itsubgroups-listThey assert the association (
label.htmlFor === input.id) rather than the presence of a string,so they survive a rename of the id and fail on a mis-pointed label.
Negative controls — each new case proven load-bearing
Applied on top of the commit, so
git checkout HEAD -- <path>restores the ported file. Every revertand restore is proved with
git diff HEAD --stat, not the baregit diff --stat:git checkout <ref> -- <path>stages what it writes, so the bare form is empty even when the revert landed.NC-1 — revert
eperson-search-box.component.htmlto the base → 2 FAILED, 49 SUCCESSThe failure text is the defect report: the accessible name is the literal English
Search input,the ids are the generic
query/scope, and there are zero labels.NC-2 — keep the labels, point them at controls that do not exist
(
for="eperson-search-scope"→for="not-the-scope-control", same for the query label)→ 1 FAILED, 50 SUCCESS
Only the pairing test goes red — the translated-name test correctly stays green, since ids and
aria-labels are untouched. This proves the test asserts the association
(
label.htmlFor === input.id) rather than the presence of a<label>, which is exactly the forkmistake Test first touch #2 shape (
for="name + '-input'"written as a literal).NC-3 — revert
members-list+subgroups-list(the sharedid="query"returns)→ 2 FAILED, 49 SUCCESS
Both of the components that render side by side on the group edit page go red on the exact property
that produced the duplicate id.
Testing
All seven subject specs are run, not only the three that were extended — the specs on this branch
still assert vanilla behaviour of these templates, so they are the real regression gate:
47 pre-existing assertions still pass, so rewriting the ids and aria-labels broke none of them.
npm run build:prodis the CIRun buildstep (build.yml:110-111) and runs on this PR.Label count per template, which is what the card's acceptance grep checks
(
grep -c '<label[^>]*visually-hidden', expected3 2 1 1 1 1 1for these seven):Card X-02a is split into three PRs by theme with disjoint file sets; this is part B. Part A
(#1515) carries the
6628aaf493(#1221) unique DOM ids; part C carries the suggestion inputs.Source:
5a92fdfa89(dtq-dev PR #1084), partial. Sync card: X-02a.🤖 Generated with Claude Code