MemberJunction / MemberJunction/MJ
mj-dropdown: complete the APG combobox pattern (listbox children, aria-activedescendant, axe gate) + two Close() bugs
- Dominant language
- TSQL
- Stars
- 29
- Forks
- 6
- Avg merge
- 2d 1h
- Merged PRs (30d)
- 323
Description
Follow-up from #3863. That PR gave `mj-dropdown` an accessible *name*; these are the remaining gaps in its ARIA *structure*, which naming alone does not fix.
## 1. `role="listbox"` has non-option children — fails axe at critical severity
The filter input (and the "No data found" div) live directly inside the element carrying `role="listbox"`, whose only permitted children are `option` / `group`. The repo-pinned axe-core 4.11.1 fires **`aria-required-children` at critical** on the open panel, so the first page-level scan with an open filterable dropdown fails.
Canonical fix per the APG combobox pattern: move the filter input **outside** the listbox element, keeping it inside the overlay panel.
## 2. No `aria-activedescendant`, and options have no ids
Arrow-key highlight is purely visual today: `HighlightedIndex` drives a CSS class and nothing else. A screen-reader user arrowing through the list hears nothing change. Needs per-option ids plus `aria-activedescendant` on whichever element holds focus (the trigger when closed-to-open, the filter input while filtering).
## 3. `AriaDescribedBy` is unassociated during the filterable open state
Opening moves focus into the filter input, which carries no `aria-describedby` — so hint/error text goes unannounced exactly while the user is typing and choosing. Either propagate it to the filter input or document why not. Low severity.
## 4. Make this the reference a11y spec
`ExpectNoAxeViolations` in `@memberjunction/ng-test-utils` currently has **zero adopters**, and it scans `fixture.nativeElement`, which can never see the CDK overlay panel. The dropdown specs are the natural first adopter, but they need an **overlay-aware scan root**. Doing that here would both prove the helper and stop the next structural regression.
Note: #3863's six naming specs assert attribute strings only — they would pass if `role="combobox"` were deleted. An axe gate is what closes that.
## Two pre-existing behaviour bugs found in the same file
Separate from ARIA, noticed during review of #3863:
- **`Close()` never restores focus to the trigger.** Selecting from the filter input drops focus to `` when the overlay detaches.
- **`Close()` clears `filterText` without emitting `FilterChange`.** Hosts that narrow `[Data]` externally — live example: `mcp-test-tool-dialog`, three dropdowns — reopen to a stale narrowed list with an empty filter box.
Contributor guide
Research direction
Start with the mj-dropdown implementation around HighlightedIndex and Close(), then review the dropdown specs and ExpectNoAxeViolations in @memberjunction/ng-test-utils. Use the open overlay as the scan root and verify the APG structure, active descendant, described-by behavior, focus restoration, and FilterChange behavior; done means the dropdown specs pass with no axe violations.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- angular
- Domain
- accessibility, frontend, testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 65/100