Misc · Terms and notices: tests for every way out; switch labels wrap

Tests now press Decline and the scrim, see focus return to the opener
after Accept, see necessary cookies stay on and off the Tab path, and
see the license list mark its row and read each version and license.
lists::ListItem marks its description and selected state for tests.
forms::Switch's label wraps in its own block, as Checkbox's does.
Ely's own row in the gallery shows both of its license texts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-09-28 04:09:49 -04:00
1 parent 6c589c3115
commit 067ef3f122
7 files changed
+123 -24

No files matched your search

+1
View File
@@ -63,6 +63,7 @@ pub const PAGE: Page = Page {
Step::Key("escape"),
Step::DownAt("cookies", 230.0, 84.0),
Step::UpAt("cookies", 230.0, 84.0),
Step::HoverAt("cookies", 20.0, 20.0),
Step::Shot("cookies-choosing"),
],
};
+5 -1
View File
@@ -17,7 +17,11 @@ fn packages() -> [Package; 4] {
"Ely GPUI Component",
"0.1.0",
"MIT OR Apache-2.0",
include_str!("../../../../LICENSE-MIT"),
concat!(
include_str!("../../../../LICENSE-MIT"),
"\n\n",
include_str!("../../../../LICENSE-APACHE")
),
),
Package::new(
"Lucide",
+7 -1
View File
@@ -140,6 +140,12 @@ impl RenderOnce for Switch {
})
})
.child(rail)
.children(self.label)
.children(self.label.map(|label| {
div()
.debug_selector(|| "switch-label".into())
.flex_1()
.min_w_0()
.child(label)
}))
}
}
+4 -3
View File
@@ -5,7 +5,7 @@ use gpui::{
use super::setup;
use crate::{
forms::{Checkbox, Choice, IconPicker, InlineEdit, RadioGroup, SignaturePad, Stroke},
forms::{Checkbox, Choice, IconPicker, InlineEdit, RadioGroup, SignaturePad, Stroke, Switch},
primitives::{FocusNext, IconName},
};
@@ -145,7 +145,7 @@ fn an_inline_edit_takes_tab_opens_on_enter_and_takes_focus_back(cx: &mut TestApp
);
}
/// A checkbox and a radio whose labels run past their narrow box.
/// A checkbox, a radio and a switch whose labels run past their narrow box.
struct Worded;
impl Render for Worded {
@@ -158,6 +158,7 @@ impl Render for Worded {
"worded-radios",
[Choice::new("one", label)],
))
.child(Switch::new("worded-switch", false).label(label))
}
}
@@ -166,7 +167,7 @@ fn long_labels_wrap_inside_their_box(cx: &mut TestAppContext) {
setup(cx);
let (_, cx) = cx.add_window_view(|_, _| Worded);
cx.run_until_parked();
for selector in ["checkbox-label", "radio-label"] {
for selector in ["checkbox-label", "radio-label", "switch-label"] {
let label = cx.debug_bounds(selector).expect("the label draws");
assert!(label.right() <= px(200.0), "{selector}: {label:?}");
}
+6 -2
View File
@@ -153,7 +153,10 @@ impl RenderOnce for ListItem {
} else {
gpui::transparent_black()
})
.when(self.selected, |row| row.bg(colors.active))
.when(self.selected, |row| {
row.debug_selector(|| format!("item-selected-{}", self.title))
.bg(colors.active)
})
.when(self.disabled, |row| row.opacity(0.5))
.when_some(self.on_click.filter(|_| pressable), |row, on_click| {
row.cursor_pointer()
@@ -182,7 +185,8 @@ impl RenderOnce for ListItem {
}),
)
.children(self.description.map(|text| {
line(text)
line(text.clone())
.debug_selector(|| format!("item-description-{text}"))
.text_size(theme.text_size(TextSize::Sm))
.text_color(colors.fg_muted)
}))
+14 -8
View File
@@ -1,7 +1,8 @@
use std::rc::Rc;
use gpui::{
App, ElementId, IntoElement, ParentElement, RenderOnce, SharedString, Styled, Window, div,
App, ElementId, InteractiveElement, IntoElement, ParentElement, RenderOnce, SharedString,
Styled, Window, div,
};
use crate::{
@@ -104,13 +105,18 @@ impl RenderOnce for CookieBanner {
.flex()
.flex_col()
.gap_3()
.child(row(
Switch::new((id.clone(), "necessary"), true)
.label("Necessary")
.disabled(true),
"Keeps the site working; always on.".into(),
cx,
))
.child({
let (on, rests) = (true, true);
div()
.debug_selector(|| format!("cookie-necessary-{on}-{rests}"))
.child(row(
Switch::new((id.clone(), "necessary"), on)
.label("Necessary")
.disabled(rests),
"Keeps the site working; always on.".into(),
cx,
))
})
.children(rows)
});
let (all, none, saved) = (answer.clone(), answer.clone(), answer);
+86 -9
View File
@@ -1,9 +1,13 @@
use std::cell::{Cell, RefCell};
use gpui::{AnyElement, IntoElement, TestAppContext};
use gpui::{
AnyElement, App, Context, FocusHandle, IntoElement, Modifiers, ParentElement, Render, Styled,
TestAppContext, Window, div, point, px,
};
use super::{at_root, press, shown, stage};
use super::{at_root, press, settle, shown, stage};
use crate::misc::{ConsentDialog, CookieBanner, LicenseViewer, Package};
use crate::{buttons::Button, primitives::FocusScope, theme::Theme};
thread_local! {
static SAID: RefCell<Vec<String>> = const { RefCell::new(Vec::new()) };
@@ -24,20 +28,25 @@ fn licenses() -> AnyElement {
[
Package::new("Alpha", "1.0", "MIT", "Permission is hereby granted"),
Package::new("Beta", "2.3", "ISC", "Permission to use, copy, modify"),
Package::new("Gamma", "0.9", "OFL-1.1", "Permission is hereby granted"),
],
)
.into_any_element()
}
/// The first package's license shows; a move down the list shows the next one's.
/// Each row reads its version and license; the first license shows, and moves down the list show the next ones, the list marking each.
#[gpui::test]
fn a_license_viewer_shows_the_chosen_license(cx: &mut TestAppContext) {
let (_, cx) = stage(licenses, cx);
assert!(shown("license-Alpha", cx));
assert!(shown("license-Alpha", cx) && shown("item-description-2.3 · ISC", cx));
press("tab", cx);
press("down", cx);
press("space", cx);
assert!(shown("license-Beta", cx));
assert!(shown("license-Beta", cx) && shown("item-selected-Beta", cx));
press("down", cx);
assert!(
shown("license-Gamma", cx),
"the list kept its mark on Beta, so its cursor went on from there"
);
}
fn consent() -> AnyElement {
@@ -78,13 +87,77 @@ fn consent_waits_for_the_box(cx: &mut TestAppContext) {
assert_eq!(said(), ["accepted"], "Accept answers alone");
}
/// Escape declines, once.
/// Escape, Decline and a press on the scrim each decline, once.
#[gpui::test]
fn escape_declines_consent(cx: &mut TestAppContext) {
fn every_other_way_out_declines(cx: &mut TestAppContext) {
let (_, cx) = stage(consent, cx);
press("tab", cx);
press("escape", cx);
assert_eq!(said(), ["declined"]);
OPEN.set(true);
cx.update(|window, _| window.refresh());
settle(cx);
press("tab", cx);
press("tab", cx);
press("enter", cx);
OPEN.set(true);
cx.update(|window, _| window.refresh());
settle(cx);
cx.simulate_click(point(px(3.0), px(3.0)), Modifiers::none());
settle(cx);
assert_eq!(said(), ["declined", "declined", "declined"]);
}
/// A page with a button that opens the consent dialog, which answers into `said`.
struct Opener {
root: FocusHandle,
button: FocusHandle,
}
impl Render for Opener {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let view = cx.entity();
FocusScope::new(&self.root)
.root()
.size_full()
.child(
Button::new("open", "Terms")
.focus_handle(&self.button)
.on_click(move |_, _, cx| {
OPEN.set(true);
view.update(cx, |_, cx| cx.notify());
}),
)
.child(div().child(consent()))
}
}
/// Accept hands focus back to the button that opened the terms.
#[gpui::test]
fn consent_hands_focus_back_to_its_opener(cx: &mut TestAppContext) {
OPEN.set(false);
cx.update(|cx: &mut App| {
Theme::init(cx);
crate::forms::bind_keys(cx);
cx.bind_keys([
gpui::KeyBinding::new("tab", crate::primitives::FocusNext, None),
gpui::KeyBinding::new("shift-tab", crate::primitives::FocusPrev, None),
]);
});
let (view, cx) = cx.add_window_view(|_, cx| Opener {
root: cx.focus_handle(),
button: cx.focus_handle(),
});
let button = view.read_with(cx, |opener, _| opener.button.clone());
cx.update(|window, _| window.focus(&button));
settle(cx);
press("enter", cx);
press("tab", cx);
press("space", cx);
press("tab", cx);
press("tab", cx);
press("enter", cx);
assert_eq!(said(), ["accepted"]);
assert!(cx.update(|window, _| button.is_focused(window)));
}
fn cookies() -> AnyElement {
@@ -106,6 +179,10 @@ fn a_cookie_banner_hands_over_the_kinds_chosen(cx: &mut TestAppContext) {
press("tab", cx);
press("enter", cx);
assert!(!at_root(cx), "Save choices took Choose's focus");
assert!(
shown("cookie-necessary-true-true", cx),
"necessary cookies stay on"
);
for _ in 0..3 {
press("shift-tab", cx);
}