You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
select: run on_dismiss however the popup closes (#2984)
Follow-up to #2973, which is already merged. That PR is good work — this
closes the gaps a post-merge review turned up.
## `on_dismiss` was skipped by the accessible close
`Select`'s `Cancel` handler runs `on_dismiss` before asking the
controlled open state to close. The `on_a11y_action(Click)` path added
in #2973 called `on_open_change(false)` alone, so a consumer wiring
`on_dismiss` saw Escape and an outside click but *not* a screen-reader
user pressing the same control to close.
That is not hypothetical:
`crates/shell/src/materialize/components/select.rs:240` forwards
`on_dismiss` to JS as `onDismiss`, so a shell app silently lost the
callback on exactly the interaction #2973 was written to support.
Both paths now share one `close` closure, so they cannot drift apart
again:
```rust
let close: ActionHandler = Rc::new({ /* on_dismiss → on_open_change(false) → focus trigger */ });
```
## Also in this patch
- **zh-CN docs.** #2973 changed `website/docs/components/select.md` and
`website/base/primitives/select.md`; both zh-CN counterparts exist and
were left behind. Synced.
- **`SearchableListItem` docs.** The rule that the accessible value
reports `title()` while `display_title()` stays presentational was
documented only on the site. Moved onto the trait, where someone
implementing `display_title` will actually read it.
- **A test assertion that asserted nothing.**
`projects_application_owned_accessible_state` checked
`disabled.is_expanded() == Some(false)` on a `Select` that was never
opened, so it passed for a reason unrelated to `disabled`. It is now
opened, and asserts a disabled control still reports the state it is in.
## Testing
```
cargo test -p gpui-base -p gpui-component --lib # 777 + 418 pass
cargo clippy -p gpui-base -p gpui-component --all-targets --locked # clean
cargo fmt --all --check # clean
```
GPUI exposes no way to dispatch an accessibility action from a test —
`Window::handle_a11y_action` is `pub(crate)` — so
`every_close_dismisses_before_it_closes` covers the shared close path
through Escape, the route a test can reach. I confirmed it fails
(`["open", "close"]` vs `["open", "dismiss", "close"]`) when the
`on_dismiss` call is removed again.
## Left open deliberately
Two findings from the same review are **not** in this PR, because both
are judgement calls rather than defects:
1. **The accessible value and the drawn trigger disagree during a
search.** `accessibility_value()` reads the committed `state.selection`;
`display_title()` reads the list cursor, which `set_query` clears.
Verified on `main`:
```
after set_selected_value("Rust") then set_query("Go"):
a11y value = "Rust" trigger draws = <placeholder>
```
I think the committed value is right for assistive technology and the
*display* is the bug — a Select with a committed selection should not
visually revert to its placeholder while you type in the popup's search
box. That is pre-existing behaviour, so fixing it is a separate change.
Worth deciding whether to fix it or stop documenting the mismatch as
intended.
2. **Placeholder as the accessible value.** With nothing selected the AX
value becomes the placeholder, so AT reads "Programming language, Choose
a language". GPUI has `aria_placeholder` (`gpui-pre-0.3.2`,
`src/elements/div.rs:1391`), which is the semantically correct slot —
that would also keep name, placeholder and value as three separate
things, the way
`an_explicit_accessibility_label_does_not_replace_the_placeholder`
already insists.
## AI assistance
The post-merge review that found these and this patch were produced with
Claude Code. I reviewed the diff and the checks above before opening
this.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01EC7fHTjQ6nGPKqq4WdryL8
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
0 commit comments