From 206d89abde8c3b3521b18c1fec9223975e680439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sun, 27 Sep 2026 12:08:44 -0400 Subject: [PATCH] Dashboard: a focused tile's Left and Right, and a control's own arrows, tested The arrows test also moves a focused tile right and back; a new test focuses a button inside a card and shows its arrows leave the tiles. A card given for a key with no tile fails loud, in both layouts. Co-Authored-By: Claude Opus 5.5 --- TASKS.md | 1 + src/dashboard/grid.rs | 10 ++++++++++ src/dashboard/tests.rs | 36 ++++++++++++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/TASKS.md b/TASKS.md index 55348e2..ca29f42 100644 --- a/TASKS.md +++ b/TASKS.md @@ -395,3 +395,4 @@ Per-component lines live in `tasks/`. Tags there: | T33c | 3 | PASS | | | T33d | 1 | FAIL | text fields never painted a highlight's wash; a tile's value ran past 280px; a stray + read as a hex digit; the WCAG thresholds, an exit's badge, a stopped row's load and the meter's clamp untested | | T33d | 2 | PASS | | +| T34 | 1 | FAIL | a focused tile's Left and Right, and the guard that leaves arrows to a focused control inside a card, untested | diff --git a/src/dashboard/grid.rs b/src/dashboard/grid.rs index f1b874e..788fbd3 100644 --- a/src/dashboard/grid.rs +++ b/src/dashboard/grid.rs @@ -137,6 +137,11 @@ fn stacked( .child(card) }) .collect(); + assert!( + cards.is_empty(), + "dashboard grid: cards with no tile: {:?}", + cards.iter().map(|(key, _)| key).collect::>() + ); div() .id(id) .relative() @@ -293,6 +298,11 @@ impl RenderOnce for DashboardGrid { .into_any_element() }) .collect(); + assert!( + cards.is_empty(), + "dashboard grid: cards with no tile: {:?}", + cards.iter().map(|(key, _)| key).collect::>() + ); let ghost = landing.as_ref().map(|tile| { div() .absolute() diff --git a/src/dashboard/tests.rs b/src/dashboard/tests.rs index fdc9444..35898f5 100644 --- a/src/dashboard/tests.rs +++ b/src/dashboard/tests.rs @@ -1,6 +1,6 @@ use gpui::{ - AnyElement, App, Context, Entity, IntoElement, KeyBinding, ParentElement, Render, Styled, - TestAppContext, VisualTestContext, Window, div, px, + AnyElement, App, Context, Entity, FocusHandle, IntoElement, KeyBinding, ParentElement, Render, + Styled, TestAppContext, VisualTestContext, Window, div, px, }; use super::{ @@ -8,6 +8,7 @@ use super::{ EventStream, MonitorAlert, StreamEvent, Tile, TimeWindow, }; use crate::{ + buttons::Button, data_display::Tone, forms::Choice, primitives::{FocusNext, Severity}, @@ -19,6 +20,7 @@ struct Wall { tiles: Vec, heard: Vec>, width: f32, + inside: FocusHandle, } impl Render for Wall { @@ -32,7 +34,14 @@ impl Render for Wall { cx.notify(); }) }), - |grid, tile| grid.card(tile.key.clone(), DashboardCard::new(tile.key.clone())), + |grid, tile| { + let card = DashboardCard::new(tile.key.clone()); + let card = match tile.key.as_ref() { + "b" => card.body(Button::new("inside", "Inside").focus_handle(&self.inside)), + _ => card, + }; + grid.card(tile.key.clone(), card) + }, ); div().w(px(self.width)).child(grid) } @@ -52,7 +61,7 @@ fn wall(cx: &mut TestAppContext) -> (Entity, &mut VisualTestContext) { Theme::update(cx, |theme| theme.reduced_motion = true); cx.bind_keys([KeyBinding::new("tab", FocusNext, None)]); }); - let (host, cx) = cx.add_window_view(|_, _| Wall { + let (host, cx) = cx.add_window_view(|_, cx| Wall { tiles: vec![ Tile::new("a", (0, 0), (6, 2)), Tile::new("b", (6, 0), (6, 2)), @@ -60,6 +69,7 @@ fn wall(cx: &mut TestAppContext) -> (Entity, &mut VisualTestContext) { ], heard: Vec::new(), width: 1200.0, + inside: cx.focus_handle(), }); settle(cx); (host, cx) @@ -95,6 +105,24 @@ fn arrows_step_a_focused_tile_past_its_neighbours_and_shift_grows_it(cx: &mut Te assert_eq!(place(&host, "a", cx), (0, 0, 7, 2), "a grew a column"); assert_eq!(place(&host, "b", cx), (6, 2, 6, 2), "pushing b below it"); assert_eq!(place(&host, "c", cx), (0, 4, 12, 2), "and c below b"); + press("right", cx); + assert_eq!(place(&host, "a", cx).0, 1, "Right moves a column over"); + press("left", cx); + assert_eq!(place(&host, "a", cx).0, 0, "and Left back"); +} + +#[gpui::test] +fn arrows_in_a_control_inside_a_card_leave_the_tiles_where_they_are(cx: &mut TestAppContext) { + let (host, cx) = wall(cx); + let inside = host.read_with(cx, |wall, _| wall.inside.clone()); + cx.update(|window, _| window.focus(&inside)); + settle(cx); + press("right", cx); + press("down", cx); + assert!( + host.read_with(cx, |wall, _| wall.heard.is_empty()), + "the button keeps its keys" + ); } type Part = fn(&Board, Entity) -> AnyElement;