Onboarding · Help: articles in a list the keyboard walks, a long address cut
HelpPanel lists its articles in lists::SelectableList: the list is one stop whose cursor scrolls into view, and Enter or a double press opens an article, focus going to All articles as before. SelectableList now activates on Enter's release, armed by its own press, as the palette and menus pick: an activation that moves focus left the release to press what took it, here the way back. ContactSupport cuts a long address in the middle and puts a Copy beside it. The lists tests move into a folder, the release tests on their own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5f5e2f73e1
commit
e26b2d1407
9 files changed
+257
-70
No files matched your search
@@ -0,0 +1,94 @@
|
||||
use gpui::{
|
||||
AppContext, Context, Entity, FocusHandle, InteractiveElement, IntoElement, ParentElement,
|
||||
Render, TestAppContext, Window, div,
|
||||
};
|
||||
|
||||
use super::{enter, settle, setup};
|
||||
use crate::{
|
||||
buttons::Button,
|
||||
forms::{Enter, Input, TextInput},
|
||||
lists::{ListItem, SelectableList},
|
||||
};
|
||||
|
||||
/// A list whose open hands focus to a button beside it, and what each heard.
|
||||
struct Handoff {
|
||||
button: FocusHandle,
|
||||
heard: Vec<&'static str>,
|
||||
}
|
||||
|
||||
impl Render for Handoff {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let (to, opened, clicked) = (self.button.clone(), cx.entity(), cx.entity());
|
||||
div()
|
||||
.child(
|
||||
SelectableList::new("handoff")
|
||||
.row("a", ListItem::new("handoff-a", "A"))
|
||||
.on_activate(move |_, window, cx| {
|
||||
window.focus(&to);
|
||||
opened.update(cx, |view, _| view.heard.push("open"));
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
Button::new("after", "After")
|
||||
.focus_handle(&self.button)
|
||||
.on_click(move |_, _, cx| {
|
||||
clicked.update(cx, |view, _| view.heard.push("click"))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enter opens on its release, so an open that moves focus to a button leaves the button unpressed.
|
||||
#[gpui::test]
|
||||
fn an_open_that_moves_focus_leaves_the_release_alone(cx: &mut TestAppContext) {
|
||||
setup(cx);
|
||||
let (view, cx) = cx.add_window_view(|_, cx| Handoff {
|
||||
button: cx.focus_handle().tab_stop(true),
|
||||
heard: Vec::new(),
|
||||
});
|
||||
settle(cx);
|
||||
cx.update(|window, _| window.focus_next());
|
||||
settle(cx);
|
||||
enter(cx);
|
||||
settle(cx);
|
||||
assert_eq!(view.read_with(cx, |view, _| view.heard.clone()), ["open"]);
|
||||
}
|
||||
|
||||
/// A field whose Enter moves focus on to a list, and what the list opened.
|
||||
struct Relay {
|
||||
field: Entity<TextInput>,
|
||||
heard: Vec<&'static str>,
|
||||
}
|
||||
|
||||
impl Render for Relay {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let opened = cx.entity();
|
||||
div()
|
||||
.capture_action(|_: &Enter, window, _| window.focus_next())
|
||||
.child(Input::new(&self.field))
|
||||
.child(
|
||||
SelectableList::new("relay")
|
||||
.row("a", ListItem::new("relay-a", "A"))
|
||||
.on_activate(move |_, _, cx| {
|
||||
opened.update(cx, |view, _| view.heard.push("open"))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A release whose press fell elsewhere opens nothing.
|
||||
#[gpui::test]
|
||||
fn a_release_that_began_elsewhere_opens_nothing(cx: &mut TestAppContext) {
|
||||
setup(cx);
|
||||
cx.update(crate::forms::bind_keys);
|
||||
let (view, cx) = cx.add_window_view(|window, cx| Relay {
|
||||
field: cx.new(|cx| TextInput::new(window, cx)),
|
||||
heard: Vec::new(),
|
||||
});
|
||||
settle(cx);
|
||||
cx.update(|window, _| window.focus_next());
|
||||
settle(cx);
|
||||
enter(cx);
|
||||
settle(cx);
|
||||
assert!(view.read_with(cx, |view, _| view.heard.is_empty()));
|
||||
}
|
||||
Reference in new issue
Block a user