Skip to content

Refactor ds-search-navbar so the <form> is only rendered when expanded#5612

Draft
bram-atmire wants to merge 1 commit intoDSpace:mainfrom
bram-atmire:fix/5603-search-navbar-refactor
Draft

Refactor ds-search-navbar so the <form> is only rendered when expanded#5612
bram-atmire wants to merge 1 commit intoDSpace:mainfrom
bram-atmire:fix/5603-search-navbar-refactor

Conversation

@bram-atmire
Copy link
Copy Markdown
Member

References

Description

Refactor ds-search-navbar so the <form> is only rendered when the search bar is expanded. Removes the Pa11y H32.2 false positive while keeping all current behavior (Enter submits, clicking the icon submits when expanded, dsClickOutside collapses).

Instructions for Reviewers

Background

Pa11y / HTMLCS rule WCAG2AA.Principle3.Guideline3_2.3_2_2.H32.2 ("This form does not contain a submit button") fires on every page because the navbar search keeps a <form> in the DOM at all times, and the icon control inside it is type="button" (it has to be - while collapsed its job is to expand the input). Functionally Enter still submits via HTML5 implicit submission, so the audit report is a false positive in behavioral terms - but it points at a real structural ambiguity around the icon button's role.

Approach

Rather than suppressing a tool-specific rule, the issue's author (#5603) suggested restructuring so that "when collapsed, there simply is no form (yet)". This PR implements that: the collapsed state renders only the toggle <button type="button">, and the expanded state renders the full <form> containing <input> and a real <button type="submit">. Other auditing tools that check the same pattern (axe, WAVE, Lighthouse) will benefit equally.

List of changes

  • src/app/search-navbar/search-navbar.component.html - split the collapsed and expanded states into two @if branches. Both keep [data-test="header-search-icon"] on their icon button so the existing Cypress e2e tests (cypress/e2e/search-navbar.cy.ts) keep working.
  • src/app/search-navbar/search-navbar.component.ts - inject ChangeDetectorRef, call cdr.detectChanges() in expand() so the @ViewChild('searchInput') reference resolves before editSearch() focuses it (the input only exists after the conditional branch renders). Made the @ViewChild references null-safe in collapse() and editSearch().
  • src/app/search-navbar/search-navbar.component.spec.ts - updated to reflect the new structure: assert the collapsed state renders no <form>, assert the expanded state renders the form with a real submit button, exercise both submit paths (icon click and native Enter via triggerEventHandler('submit')).

Known minor regression

The slide-in animation defined by expandSearchInput is still applied to the [@toggleAnimation] state on the input while the form is rendered, but the entry transition (collapsed → expanded) is no longer animated (the form simply appears). The slide can be re-introduced via an Angular :enter/:leave transition on the conditional form in a follow-up PR.

How to test

  1. Start the UI (yarn start or pm2 start config/dspace-ui-test.json).
  2. Inspect the navbar in collapsed state - there is only a <button class="search-toggle">, no <form> element.
  3. Click the magnifying glass; the form appears with <input> and <button type="submit">. Focus is in the input.
  4. Type a query, press Enter - URL navigates to /search?query=<query>. Inspect the form's submit button while expanded: it is <button type="submit">.
  5. Type a query, click the magnifying glass - same submission behavior (now via native form submit, not a JS click handler).
  6. Click outside the navbar - search collapses (existing dsClickOutside behavior).
  7. npm run test -- --include='src/app/search-navbar/search-navbar.component.spec.ts' shows 6 specs pass.
  8. cypress run --spec cypress/e2e/search-navbar.cy.ts (or the project's standard e2e command) - existing scenarios still pass; selectors are unchanged.
  9. Run Pa11y / axe on any page; H32.2 no longer reports ds-search-navbar > form while collapsed (the <form> is no longer in the DOM in that state).

Checklist

  • My PR is created against the main branch of code (unless it is a backport or is fixing an issue specific to an older branch).
  • My PR is small in size (3 files, 80 insertions, 32 deletions).
  • My PR passes ESLint validation using npm run lint
  • My PR doesn't introduce circular dependencies (verified via npm run check-circ-deps)
  • My PR includes TypeDoc comments for all new (or modified) public methods and classes.
  • My PR passes all specs/tests and includes new/updated specs or tests based on the Code Testing Guide.
  • My PR aligns with Accessibility guidelines if it makes changes to the user interface.
  • My PR uses i18n (internationalization) keys instead of hardcoded English text, to allow for translations. (existing keys retained)
  • My PR includes details on how to test it.
  • If my PR includes new libraries/dependencies (in package.json), I've made sure their licenses align with the DSpace BSD License based on the Licensing of Contributions documentation. (n/a)
  • If my PR includes new features or configurations, I've provided basic technical documentation in the PR itself. (n/a)
  • If my PR fixes an issue ticket, I've linked them together.

Pa11y/HTMLCS rule WCAG2AA H32.2 ("This form does not contain a submit
button") fires on every page because the navbar search renders a <form>
even while collapsed, and the icon control inside it is type="button"
(it has to be: in collapsed state its job is to expand the input, not
to submit). Functionally, Enter still submits the search via HTML5
implicit submission on a single-input form, so this is a false positive
in behavioural terms. The rule is, however, pointing at a real semantic
ambiguity: the icon button's role depends on whether the search is
collapsed or expanded.

Restructure the template so:
- when collapsed, only a single <button type="button"> is rendered
  (the magnifying-glass toggle). No form is in the DOM.
- when expanded, the full <form> is rendered with <input> and a real
  <button type="submit"> as the icon. Enter submits natively; clicking
  the icon submits natively.

The icon's role is now unambiguous in each state and Pa11y H32.2 no
longer reports the navbar form. The cypress e2e tests
(`cypress/e2e/search-navbar.cy.ts`) keep working because they query the
icon by `[data-test="header-search-icon"]`, which is set on both the
collapsed-state toggle and the expanded-state submit button.

The expand-on-collapse slide animation defined by `expandSearchInput`
is still applied to the input element while expanded; it is no longer
applied to the entry transition (the form simply appears). A follow-up
can re-introduce a `:enter`/`:leave` slide on the conditional form if
desired.

Fixes DSpace#5603
@lgeggleston lgeggleston added bug improvement accessibility 1 APPROVAL pull request only requires a single approval to merge labels May 4, 2026
@lgeggleston lgeggleston moved this to 🙋 Needs Reviewers Assigned in DSpace 10.0 Release May 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1 APPROVAL pull request only requires a single approval to merge accessibility bug improvement

Projects

Status: 🙋 Needs Reviewers Assigned

Development

Successfully merging this pull request may close these issues.

Refactor ds-search-navbar so the <form> is only rendered when expanded (Pa11y H32 false positive)

2 participants