[autocomplete] Wrap the no results and loading messages in a status element#48690
Conversation
Deploy previewBundle size
Check out the code infra dashboard for more information about this PR. |
There was a problem hiding this comment.
Pull request overview
This PR updates Autocomplete to improve assistive-technology announcements when there are no matching options by introducing a new noOptions slot that provides a live-region status container around the existing “no options” content.
Changes:
- Add a
noOptionsslot (andslotProps.noOptions) and wrap the no-options content in arole="status"live region (aria-live="polite",aria-atomic="true"). - Extend TypeScript typings (slot + overrides augmentation) and PropTypes to include the new slot.
- Add/adjust tests and regenerate API docs reflecting the new slot.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/mui-material/src/Autocomplete/Autocomplete.js | Adds noOptions slot via useSlot and wraps the no-options message in a status live region. |
| packages/mui-material/src/Autocomplete/Autocomplete.d.ts | Adds noOptions to AutocompleteSlots and slot-props typing + overrides interface. |
| packages/mui-material/src/Autocomplete/Autocomplete.spec.tsx | Adds TS coverage for slots.noOptions / slotProps.noOptions usage (including ref). |
| packages/mui-material/src/Autocomplete/Autocomplete.test.js | Adds tests around noOptionsText rendering and the status container behavior. |
| docs/translations/api-docs/autocomplete/autocomplete.json | Updates slot descriptions to include noOptions. |
| docs/pages/material-ui/api/autocomplete.json | Updates API docs output for the new noOptions slot and slotProps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
docs/translations/api-docs/autocomplete/autocomplete.json:323
- In the translations JSON,
noOptionshas been moved fromclassDescriptionstoslotDescriptions, implyingnoOptionsis now a slot (and no longer a documented utility class). However, the current implementation still appliesclasses.noOptionsto the inner message wrapper (<AutocompleteNoOptions className={classes.noOptions} ...>), while the newnoOptionsslot container has no class.
This makes the generated docs inconsistent with the actual DOM/class placement. Consider aligning the implementation with the slot documentation (or vice-versa) and regenerating these files.
"slotDescriptions": {
"clearIndicator": "The component used to render the clear indicator element.",
"listbox": "The component used to render the listbox.",
"noOptions": "The component used to render the "no options" container.",
"paper": "The component used to render the body of the popup.",
"popper": "The component used to position the popup.",
"popupIndicator": "The component used to render the popup indicator element.",
"root": "The component that renders the root."
}
e7139c4 to
29583ca
Compare
ZeeshanTamboli
left a comment
There was a problem hiding this comment.
I inspected the DOM. The popup now always has an extra empty accessible live region sibling before the listbox:
<div role="status" ...></div>
<ul role="listbox">...</ul>Is this expected? Wouldn't it be better to render the status slot only when it has content ("no options" or "loading") within it?
Screen readers have a hard time picking up updates when elements are added and removed. When using live regions, it is recommended to leave the live region element always in the DOM, update with content when needed, then remove / replace the content. |
This is the behaviour I found in Base UI Combobox as well: https://base-ui.com/react/components/combobox. 👍 |
ZeeshanTamboli
left a comment
There was a problem hiding this comment.
Looks good to me.
I tested with Narrator and NVDA screen readers on Windows 11 at https://deploy-preview-48690--material-ui.netlify.app/material-ui/react-autocomplete/#githubs-picker. Narrator is not announcing "No labels", while NVDA is announcing it. Maybe Narrator does not support aria-live yet.
Fixes #48666
Add a new
statusslot that wraps the "no options" and "loading" elements with a container element. The container renders withrole="status",aria-live="polite", andaria-atomic="true"so assistive technologies announce the absence of options without requiring the popup to be open.Changes
statusslot viauseSlotbacked by a plain'div'(no styled wrapper needed)statusCSS class to the container element so consumers can style itstatustoautocompleteClassesandAutocompleteClassesAutocompleteStatusSlotPropsOverridesaugmentation interfacestatustoAutocompleteSlotsandAutocompleteSlotsAndSlotPropsslots(status) andslotProps(status)