Skip to content

Resolve listbox @ViewChild({ static: true }) queries that never resolve through *ngTemplateOutlet #662

Description

@fpigeonjr

Parent

#576

What to build

Decide and implement how the listbox component should resolve its view children, which are currently unreachable, then un-skip the tests parked on the defect.

listbox.component.html wraps its entire body in <ng-container *ngTemplateOutlet="listTemplate">. Three @ViewChild(..., { static: true }) queries — srOnly, checkboxList/checkboxListElement, and FieldsetWrapper — target nodes that only exist inside that embedded view. Static queries resolve before ngOnInit and never see content created by an outlet, so all three are permanently undefined.

This is not only a test-observability problem. srOnly is guarded before use, but checkboxListElement.nativeElement is dereferenced unguarded in the screen-reader announcement path and in the arrow-key scroll handling, so those paths throw as soon as they are reached. Four it.skip tests in listbox.component.spec.ts are parked on exactly this behavior ("should disable", arrow up/down keypresses, and the two boundary cases).

This slice is HITL: the fix is a design decision, not a mechanical change. Options to weigh — hoist the markup out of the *ngTemplateOutlet so the queries resolve normally; switch the queries to non-static and add guards or ngAfterViewInit handling; or restructure so the template is projected rather than outlet-rendered. *ngTemplateOutlet here appears to be indirection with no consumer-facing purpose, so removing it is worth considering first, but it needs a look at whether anything depends on the current structure. Whichever route is chosen, note it on this issue before implementing.

Out of scope for the earlier spec PRs by design — #660 deliberately left this alone and called it out as a deeper architectural issue.

Acceptance criteria

  • Chosen approach recorded as a comment on this issue before implementation
  • All three view-child queries resolve to real elements at the point the component first uses them
  • The screen-reader announcement path and the arrow-key scroll path no longer dereference an undefined view child
  • The four it.skip tests in listbox.component.spec.ts are un-skipped and pass unmodified, or their assertions are updated with a note explaining why the original expectation was wrong
  • Rendered behavior is unchanged for existing consumers — label, hint, error message, required marker, and option list all render as before
  • npm run coverage:check, npm run lint, npm run format:check, and the test-app build all pass; coverage-floor.json is not modified (see AGENTS.md — floor bumps land via Lock coverage-floor gate at 90% QASP target (epic top-off) #637)

Blocked by

None - can start immediately

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions