MemberJunction / MemberJunction/MJ

mj-dropdown: complete the APG combobox pattern (listbox children, aria-activedescendant, axe gate) + two Close() bugs

Open
#4,115 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.