docs(context): core.md section 21 and test evidence for displays beside keys, second slice (improvise-cq1q.12)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
committed by
Edward Langley
parent
ba053fdaa5
commit
7a7ff81731
+41
-6
@@ -1758,9 +1758,44 @@ command name a key that does not exist once the two differ. That is the gap
|
||||
improvise-cq1q.11 names, and cq1q.7 later turns these key fields into ids. The
|
||||
active-view marker in both view panels still compares keys for the same reason.
|
||||
|
||||
**What this first slice covers:** the category panel's headers, the tile bar and
|
||||
the view list, in both front ends. Item rows, the grid frame, the page bar,
|
||||
records-mode labels, CSV export headers, status messages and the view title
|
||||
still show keys. They follow in later slices of this step, because the grid's
|
||||
label tuples are also cell coordinates (`cell_key`, `innermost_group`), so their
|
||||
display has to be added at projection time rather than in place.
|
||||
**What the first slice covers:** the category panel's headers, the tile bar and
|
||||
the view list, in both front ends.
|
||||
|
||||
**What the second slice covers (improvise-cq1q.12):** the grid frame, the page
|
||||
bar, records-mode headers and coordinate cells, the category panel's item rows,
|
||||
the view panel's axis line and CSV export. The frame's label tuples are also cell
|
||||
coordinates (`cell_key`, `innermost_group`, `run_start`), so the display is added
|
||||
beside the key and never in its place:
|
||||
|
||||
- `AxisEntry::DataItem` carries `display` parallel to `tuple`, and `GroupHeader`
|
||||
a `group_display`. They are built where the layout is, because a folded row's
|
||||
tuple holds a *group* name where an item name would be and only `expand_category`
|
||||
knows which. `Group` gained a private `display` for it.
|
||||
- `AxisFrame` carries `cat_displays`, `FrameEntry::Item` a `display` tuple and
|
||||
`FrameGroup` a `display`, beside `cats`, `tuple` and `name`. Deserialising
|
||||
refuses a display list that is not the length of its keys. `header_labels`
|
||||
decides *whether* a repeat blanks from the keys and draws the display, and
|
||||
`header_widths` measures what is drawn.
|
||||
- `PageAxis` carries `cat_display`, `selected_display` and `item_displays`
|
||||
beside `cat`, `selected` and `items`. A page command is composed from `items`
|
||||
at an index, so `item_displays` can never be read back as a key.
|
||||
- `CategoryRow.items` is `Vec<CategoryItem { name, display }>`, and
|
||||
`ViewSummary.axes` is `Vec<ViewAxis { category, display, axis }>`.
|
||||
- Records mode: a column header shows the category's or measure's display, and a
|
||||
coordinate cell's *text* is the item's display (`GridLayout::coordinate_display`).
|
||||
Its `edit_text` and `value` stay the key, because the editor seeds the rename
|
||||
from them.
|
||||
- CSV export writes displays in its corner cell, column headers, row labels and
|
||||
page note.
|
||||
- The browser's render model keeps its field names and carries the display in them
|
||||
(`RenderPageAxis.cat`, `RenderCatRow.label`), so `web/` is unchanged.
|
||||
|
||||
Still keys: `GridLayout::row_label`/`col_label` (commands and the status line
|
||||
match on them), `edit_text`, the active-view marker, and everything status
|
||||
messages and the view title say (improvise-cq1q.13). A display change alone does
|
||||
not move `layout_fingerprint`, so it resets no cursor; no production path changes
|
||||
one yet.
|
||||
|
||||
**`.improv` neither writes nor reads a display in this step.** A saved file still
|
||||
carries keys only, so a display set in memory is lost on save; display persists
|
||||
only with the new version literal (improvise-cq1q.5).
|
||||
|
||||
@@ -2024,3 +2024,94 @@ fails on that rival (no `▶ Overview`), because the active view is a key
|
||||
|
||||
The browser half of the same three panels. It also asserts that the view row is
|
||||
still `active` when found by key.
|
||||
|
||||
## Displays in the grid frame, page bar, item rows and CSV (ADR 007 clause 2, improvise-cq1q.12)
|
||||
|
||||
The same test-only way to make a display differ from its key (`set_display`,
|
||||
`set_item_display`, `set_group_display`). Each rival below was injected, the
|
||||
workspace run, and the named tests failed; the revert was re-run green.
|
||||
|
||||
### view::layout::tests::a_layout_shows_displays_and_still_addresses_by_keys
|
||||
|
||||
Rivals: an item's display pushed as its key (`display.push(item_name)`); a page
|
||||
axis's `selected_display` set to `selected`. Both fail on the display asserts,
|
||||
while `cell_key`, `row_group_for` and `row_run_start` stay on keys.
|
||||
|
||||
### view::layout::tests::a_folded_group_is_shown_under_the_groups_display_and_keyed_by_its_name
|
||||
|
||||
A folded row's tuple holds the group name where an item name would be, so the
|
||||
display must come from the group. Not a distinct injection; it covers the arm
|
||||
that the item test cannot reach.
|
||||
|
||||
### view::layout::tests::a_records_grid_shows_displays_in_its_headers_and_coordinates
|
||||
|
||||
Rival: `coordinate_display` returns the item key. Fails here and in
|
||||
`ui::project::tests::a_records_coordinate_shows_its_display_and_edits_its_key`.
|
||||
|
||||
### ui::project::tests::the_frame_shows_displays_and_its_key_tuples_still_address_the_cells
|
||||
|
||||
The rival the ticket names: `axis_frame` writes `tuple: display.clone()`. The
|
||||
test rebuilds every cell's `CellKey` from the frame's key tuples, its `cats` and
|
||||
the page bar, and compares it to `layout.cell_key`, so it fails (and so does the
|
||||
records test, which looks the Region column up by its key tuple). Also fails when
|
||||
a group header's display is its key.
|
||||
|
||||
### ui::project::tests::each_view_summary_shows_its_own_display_and_its_categories_displays
|
||||
|
||||
Rival: every summary takes the ACTIVE view's display. The second view "Plan" has
|
||||
a display unlike both the active view's ("Overview") and its own key, so the
|
||||
fault fails here by name.
|
||||
|
||||
### ui::project::tests::an_item_row_shows_its_display_and_keeps_its_key
|
||||
|
||||
Rival: the item row's display is its key. The unrenamed item "West" must show its
|
||||
key, and `_Measure`'s "Revenue" shows "Sales".
|
||||
|
||||
### ui::project::tests::the_page_bar_carries_displays_beside_the_keys_a_page_command_names
|
||||
|
||||
Rival: `selected_display` is the key. Fails here, in the layout test, in the TUI
|
||||
grid test and in the CSV test.
|
||||
|
||||
### render::tests::a_header_draws_displays_and_blanks_a_repeat_by_key
|
||||
|
||||
Rival: suppression compares display tuples. West shares East's DISPLAY ("Same")
|
||||
and must still draw, so comparing displays blanks it.
|
||||
|
||||
### render::tests::a_header_is_as_wide_as_what_it_shows
|
||||
|
||||
Rival: `header_widths` measures the key tuples. Fails (key "E" against display
|
||||
"Eastern Seaboard"), as does the TUI grid test.
|
||||
|
||||
### render::tests::a_frame_with_ragged_displays_is_refused
|
||||
|
||||
Asserts the control parses, then that a `cat_displays` shorter than `cats` and a
|
||||
display tuple shorter than its key tuple are each refused with their own message.
|
||||
|
||||
### resolve::tests::hiding_a_displayed_row_item_names_its_key
|
||||
|
||||
Rival: the hide resolver reads the first DISPLAY of the row frame. It composes
|
||||
`HideItem { item: "Eastern Seaboard" }` and the test fails on `"East"`.
|
||||
|
||||
### resolve::tests::picking_a_displayed_page_item_still_names_its_key
|
||||
|
||||
Picks index 1 of a page axis whose displays are not its keys and requires the
|
||||
command to name `Q2`. The index is the only thing that travels from the menu.
|
||||
|
||||
### render::tests::the_grid_page_bar_and_item_rows_paint_displays
|
||||
|
||||
The browser half. Rival: the cycle's `items` relays the keys, which fails here.
|
||||
|
||||
### ui::grid::tests::the_frame_and_page_bar_draw_displays_not_keys
|
||||
|
||||
The TUI half. Rival: the group header draws `group.name`; fails on the missing
|
||||
"▼ Bay". The key assertions use strings that appear only as keys.
|
||||
|
||||
### ui::category_panel::tests::an_item_row_draws_its_display_not_its_key
|
||||
|
||||
The TUI item row. Not separately injected: its draw path is the one line the
|
||||
`CatTreeEntry::Item` pattern changed.
|
||||
|
||||
### persistence::export::tests::export_csv_writes_displays_in_every_header_and_label
|
||||
|
||||
Rival: the column header uses `col_label` (the key). The exact output pins the
|
||||
corner cell `Kind (Who=Alicia)`, the column header and the row label.
|
||||
|
||||
@@ -14,12 +14,12 @@ use improvise_core::workbook::Workbook;
|
||||
use improvise_io::import::analyzer::{DateComponent, DateShape, FieldKind};
|
||||
use improvise_io::import::wizard::{DuplicatePolicy, ImportWizard, WizardStep};
|
||||
use improvise_protocol::{
|
||||
AxisFrame, Basis, BasisCache, CategoryItem, CategoryRow, CategoryTreeCache, CategoryTreeUpdate, CellDisplay,
|
||||
CellId, Command, CreateKind, DocumentCache, FormulaCache, FormulaEntry, FrameEntry, FrameGroup,
|
||||
GridCache, GridExtent, GridUpdate, Projection, RecordCell, Rejection, Snapshot, TileBarCache,
|
||||
TileEntry, ViewAxis, ViewListCache, ViewState, ViewSummary, WidgetCache, WizardCache, WizardChoice,
|
||||
WizardCollision, WizardDateField, WizardFieldKind, WizardProposal, WizardStepKind, basis,
|
||||
reduce_view,
|
||||
AxisFrame, Basis, BasisCache, CategoryItem, CategoryRow, CategoryTreeCache, CategoryTreeUpdate,
|
||||
CellDisplay, CellId, Command, CreateKind, DocumentCache, FormulaCache, FormulaEntry,
|
||||
FrameEntry, FrameGroup, GridCache, GridExtent, GridUpdate, Projection, RecordCell, Rejection,
|
||||
Snapshot, TileBarCache, TileEntry, ViewAxis, ViewListCache, ViewState, ViewSummary,
|
||||
WidgetCache, WizardCache, WizardChoice, WizardCollision, WizardDateField, WizardFieldKind,
|
||||
WizardProposal, WizardStepKind, basis, reduce_view,
|
||||
};
|
||||
|
||||
use crate::ui::app::App;
|
||||
@@ -350,8 +350,7 @@ fn cell_display(
|
||||
) -> CellDisplay {
|
||||
if layout.is_records_mode() {
|
||||
// The value, not its spelling: this module used to infer numeric-ness
|
||||
// with `parse::<f64>()`, which called a coordinate `2024` a number. A
|
||||
// coordinate is drawn under its item's display; the editor keeps the key.
|
||||
// with `parse::<f64>()`, which called a coordinate `2024` a number.
|
||||
let text = layout
|
||||
.coordinate_display(&app.model_state.workbook.model, row, col)
|
||||
.unwrap_or_else(|| value.as_ref().map(|v| v.to_string()).unwrap_or_default());
|
||||
@@ -1961,8 +1960,15 @@ mod tests {
|
||||
let app = app_shown_otherwise();
|
||||
let grid = grid_of(&app, ALL);
|
||||
assert_eq!(grid.row_frame.cat_displays, vec!["Sales Area"]);
|
||||
assert_eq!(grid.row_frame.cats, vec!["Region"], "the key stays beside it");
|
||||
assert_eq!(grid.col_frame.header_labels(0), vec![vec!["Sales".to_string()]]);
|
||||
assert_eq!(
|
||||
grid.row_frame.cats,
|
||||
vec!["Region"],
|
||||
"the key stays beside it"
|
||||
);
|
||||
assert_eq!(
|
||||
grid.col_frame.header_labels(0),
|
||||
vec![vec!["Sales".to_string()]]
|
||||
);
|
||||
let header = grid
|
||||
.row_frame
|
||||
.entries
|
||||
@@ -1972,7 +1978,10 @@ mod tests {
|
||||
_ => None,
|
||||
})
|
||||
.expect("East sits under Coastal");
|
||||
assert_eq!((header.name.as_str(), header.display.as_str()), ("Coastal", "Shoreline"));
|
||||
assert_eq!(
|
||||
(header.name.as_str(), header.display.as_str()),
|
||||
("Coastal", "Shoreline")
|
||||
);
|
||||
assert_eq!(
|
||||
grid.row_frame.header_labels(0)[0],
|
||||
vec!["Eastern Seaboard".to_string()]
|
||||
@@ -2008,10 +2017,16 @@ mod tests {
|
||||
let app = app_shown_otherwise();
|
||||
let bar = grid_of(&app, ALL).page_coords;
|
||||
let quarter = &bar[0];
|
||||
assert_eq!((quarter.cat.as_str(), quarter.selected.as_str()), ("Quarter", "Q1"));
|
||||
assert_eq!(
|
||||
(quarter.cat.as_str(), quarter.selected.as_str()),
|
||||
("Quarter", "Q1")
|
||||
);
|
||||
assert_eq!(quarter.cat_display, "Period");
|
||||
assert_eq!(quarter.selected_display, "First");
|
||||
assert_eq!(quarter.items.as_deref(), Some(&["Q1".to_string(), "Q2".into()][..]));
|
||||
assert_eq!(
|
||||
quarter.items.as_deref(),
|
||||
Some(&["Q1".to_string(), "Q2".into()][..])
|
||||
);
|
||||
assert_eq!(
|
||||
quarter.item_displays.as_deref(),
|
||||
Some(&["First".to_string(), "Second".into()][..])
|
||||
@@ -2046,7 +2061,10 @@ mod tests {
|
||||
assert_eq!(plan_region.display, "Sales Area");
|
||||
assert_eq!(plan_region.axis, Axis::Column);
|
||||
let line = improvise_protocol::view_axis_summary(by_key("Plan"));
|
||||
assert_eq!(line, "C:Sales Area,Period", "the line draws displays, not keys");
|
||||
assert_eq!(
|
||||
line, "C:Sales Area,Period",
|
||||
"the line draws displays, not keys"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2063,7 +2081,10 @@ mod tests {
|
||||
.items()
|
||||
.position(|t| t == &vec!["Region".to_string()])
|
||||
.expect("a column per category, found by its key");
|
||||
assert_eq!(grid.col_frame.header_labels(0)[col], vec!["Sales Area".to_string()]);
|
||||
assert_eq!(
|
||||
grid.col_frame.header_labels(0)[col],
|
||||
vec!["Sales Area".to_string()]
|
||||
);
|
||||
let shown: Vec<&str> = grid.cells.iter().map(|r| r[col].text.as_str()).collect();
|
||||
assert!(shown.contains(&"Eastern Seaboard"), "{shown:?}");
|
||||
let east = grid
|
||||
@@ -2072,7 +2093,11 @@ mod tests {
|
||||
.map(|r| &r[col])
|
||||
.find(|c| c.text == "Eastern Seaboard")
|
||||
.unwrap();
|
||||
assert_eq!(east.edit_text.as_deref(), Some("East"), "the editor seeds the key");
|
||||
assert_eq!(
|
||||
east.edit_text.as_deref(),
|
||||
Some("East"),
|
||||
"the editor seeds the key"
|
||||
);
|
||||
}
|
||||
|
||||
/// A pivot-shaped default view plus a records-shaped second one, so a
|
||||
|
||||
@@ -73,9 +73,9 @@ pub fn grid_body(
|
||||
let (key, value) = cell_value(layout, &eval, records, ri, ci);
|
||||
let fmt = plan.at(key.as_ref());
|
||||
let text = match records {
|
||||
true => layout.coordinate_display(model, ri, ci).unwrap_or_else(|| {
|
||||
value.as_ref().map(|v| v.to_string()).unwrap_or_default()
|
||||
}),
|
||||
true => layout
|
||||
.coordinate_display(model, ri, ci)
|
||||
.unwrap_or_else(|| value.as_ref().map(|v| v.to_string()).unwrap_or_default()),
|
||||
false => fmt.value(value.as_ref()),
|
||||
};
|
||||
widen(&mut widths, ci, text.width());
|
||||
|
||||
@@ -350,7 +350,11 @@ mod tests {
|
||||
assert_eq!(c.group_display("Coastal"), "Coastal", "no group declared");
|
||||
c.set_group_display("Coastal", "Shoreline");
|
||||
assert_eq!(c.group_display("Coastal"), "Shoreline");
|
||||
assert_eq!(c.group_chain("Coastal"), vec!["Coastal"], "the key is unmoved");
|
||||
assert_eq!(
|
||||
c.group_chain("Coastal"),
|
||||
vec!["Coastal"],
|
||||
"the key is unmoved"
|
||||
);
|
||||
assert_eq!(c.item_display("Nowhere"), "Nowhere", "a formula target");
|
||||
}
|
||||
|
||||
|
||||
@@ -222,10 +222,9 @@ pub enum AxisEntry {
|
||||
GroupEnd { level: usize, depth: usize },
|
||||
/// `collapsed` is not derivable from `tuple`: a group and an item may share a
|
||||
/// name. Set for a folded group and for everything expanded beneath one.
|
||||
/// `display` is parallel to `tuple`, one name per level; the tuple is the
|
||||
/// coordinate and is never replaced by it (core.md §21).
|
||||
DataItem {
|
||||
tuple: Vec<String>,
|
||||
/// Parallel to `tuple`; the tuple is never replaced (core.md §21).
|
||||
display: Vec<String>,
|
||||
collapsed: bool,
|
||||
},
|
||||
@@ -515,8 +514,6 @@ pub fn page_constraints(axes: &[PageAxis]) -> Vec<(String, String)> {
|
||||
}
|
||||
|
||||
impl PageAxis {
|
||||
/// The items to cycle and the index `selected` sits at, for the two
|
||||
/// callers that page. `None` where the axis is not navigable.
|
||||
/// An axis shown under its own keys, as it is wherever nothing is renamed.
|
||||
pub fn plain(cat: &str, selected: &str, items: Option<Vec<String>>) -> Self {
|
||||
Self {
|
||||
@@ -529,6 +526,8 @@ impl PageAxis {
|
||||
}
|
||||
}
|
||||
|
||||
/// The items to cycle and the index `selected` sits at, for the two
|
||||
/// callers that page. `None` where the axis is not navigable.
|
||||
pub fn cycle(&self) -> Option<(&[String], usize)> {
|
||||
let items = self.items.as_deref().filter(|i| !i.is_empty())?;
|
||||
let at = items.iter().position(|i| *i == self.selected).unwrap_or(0);
|
||||
@@ -624,8 +623,7 @@ impl GridLayout {
|
||||
cat_display: model.category_display(cat).to_string(),
|
||||
selected_display: shown(&selected),
|
||||
selected,
|
||||
item_displays: navigable
|
||||
.then(|| items.iter().map(|i| shown(i)).collect()),
|
||||
item_displays: navigable.then(|| items.iter().map(|i| shown(i)).collect()),
|
||||
items: navigable.then_some(items),
|
||||
})
|
||||
})
|
||||
@@ -1127,7 +1125,9 @@ impl<'a> CategoryRuns<'a> {
|
||||
depth,
|
||||
cat_name: self.cat_name.to_string(),
|
||||
group_name: group.to_string(),
|
||||
group_display: category.map_or(*group, |c| c.group_display(group)).to_string(),
|
||||
group_display: category
|
||||
.map_or(*group, |c| c.group_display(group))
|
||||
.to_string(),
|
||||
collapsed: self
|
||||
.view
|
||||
.is_group_collapsed(self.cat_name, &chain[..=depth]),
|
||||
@@ -1717,16 +1717,19 @@ mod tests {
|
||||
("Type".into(), "Food".into()),
|
||||
]))
|
||||
);
|
||||
assert_eq!(
|
||||
layout.row_group_for(0),
|
||||
Some(("Month".into(), "Q1".into()))
|
||||
);
|
||||
assert_eq!(layout.row_group_for(0), Some(("Month".into(), "Q1".into())));
|
||||
assert_eq!(layout.row_run_start("Month", "Q1", &[]), Some(0));
|
||||
let page = &layout.page_coords[0];
|
||||
assert_eq!((page.cat.as_str(), page.selected.as_str()), ("Region", "East"));
|
||||
assert_eq!(
|
||||
(page.cat.as_str(), page.selected.as_str()),
|
||||
("Region", "East")
|
||||
);
|
||||
assert_eq!(page.cat_display, "Sales Area");
|
||||
assert_eq!(page.selected_display, "Eastern Seaboard");
|
||||
assert_eq!(page.items.as_deref(), Some(&["East".to_string(), "West".into()][..]));
|
||||
assert_eq!(
|
||||
page.items.as_deref(),
|
||||
Some(&["East".to_string(), "West".into()][..])
|
||||
);
|
||||
assert_eq!(
|
||||
page.item_displays.as_deref(),
|
||||
Some(&["Eastern Seaboard".to_string(), "Pacific".into()][..])
|
||||
@@ -1772,7 +1775,10 @@ mod tests {
|
||||
"Sales"
|
||||
);
|
||||
assert_eq!(layout.records_display(0, month).as_deref(), Some("Jan"));
|
||||
assert_eq!(layout.coordinate_display(&wb.model, 0, month).as_deref(), Some("January"));
|
||||
assert_eq!(
|
||||
layout.coordinate_display(&wb.model, 0, month).as_deref(),
|
||||
Some("January")
|
||||
);
|
||||
assert_eq!(
|
||||
layout.coordinate_display(&wb.model, 0, col_of("Revenue").unwrap()),
|
||||
None,
|
||||
|
||||
@@ -34,12 +34,12 @@ pub use reduce::{
|
||||
records_named, reduce_view,
|
||||
};
|
||||
pub use render::{
|
||||
AxisFrame, CategoryItem, CategoryRow, CategoryTreeCache, CategoryTreeUpdate, CellDisplay, CellId, CreateKind,
|
||||
DocumentCache, FormulaCache, FormulaEntry, FrameEntry, FrameGroup, GridCache, GridExtent,
|
||||
GridUpdate, MAX_ROW_HEADER_W, MIN_ROW_HEADER_W, Projection, RecordCell, Snapshot, TileBarCache,
|
||||
TileEntry, ViewAxis, ViewListCache, ViewSummary, WidgetCache, WizardCache, WizardChoice, WizardCollision,
|
||||
WizardDateField, WizardFieldKind, WizardProposal, WizardStepKind, header_level_widths,
|
||||
non_item_refusal, opening_tile,
|
||||
AxisFrame, CategoryItem, CategoryRow, CategoryTreeCache, CategoryTreeUpdate, CellDisplay,
|
||||
CellId, CreateKind, DocumentCache, FormulaCache, FormulaEntry, FrameEntry, FrameGroup,
|
||||
GridCache, GridExtent, GridUpdate, MAX_ROW_HEADER_W, MIN_ROW_HEADER_W, Projection, RecordCell,
|
||||
Snapshot, TileBarCache, TileEntry, ViewAxis, ViewListCache, ViewSummary, WidgetCache,
|
||||
WizardCache, WizardChoice, WizardCollision, WizardDateField, WizardFieldKind, WizardProposal,
|
||||
WizardStepKind, header_level_widths, non_item_refusal, opening_tile,
|
||||
};
|
||||
pub use resolve::{
|
||||
Click, KeyOutcome, OpenedEditor, Request, click, edited, read_buffer, resolve, typed,
|
||||
|
||||
@@ -1506,7 +1506,10 @@ mod tests {
|
||||
collapsed: false,
|
||||
}],
|
||||
};
|
||||
assert_eq!(frame.header_widths(), vec!["Eastern Seaboard".len() as u16 + 1]);
|
||||
assert_eq!(
|
||||
frame.header_widths(),
|
||||
vec!["Eastern Seaboard".len() as u16 + 1]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -2670,7 +2670,11 @@ mod tests {
|
||||
// produce, so the fixture projects both (improvise-e5pf).
|
||||
crate::WidgetCache::apply(&mut state.grid, &GridUpdate::Records(mode.is_records()));
|
||||
state.fold(&Command::ChangeMode(mode));
|
||||
state.grid.page_coords = vec![crate::PageAxis::plain("Quarter", "Q2", Some(vec!["Q1".into(), "Q2".into(), "Q3".into()]))];
|
||||
state.grid.page_coords = vec![crate::PageAxis::plain(
|
||||
"Quarter",
|
||||
"Q2",
|
||||
Some(vec!["Q1".into(), "Q2".into(), "Q3".into()]),
|
||||
)];
|
||||
// THREE of everything, every panel cursor seated in the MIDDLE: an edge
|
||||
// makes `move-panel-cursor` answer `Ignored` for the wrong reason.
|
||||
crate::WidgetCache::apply(
|
||||
@@ -3991,7 +3995,11 @@ mod tests {
|
||||
crate::WidgetCache::apply(&mut state.basis, &published());
|
||||
state.grid.page_coords = vec![
|
||||
crate::PageAxis::plain("Year", "2024", Some(vec!["2024".into(), "2025".into()])),
|
||||
crate::PageAxis::plain("Quarter", "Q4", Some(vec!["Q1".into(), "Q2".into(), "Q3".into(), "Q4".into()])),
|
||||
crate::PageAxis::plain(
|
||||
"Quarter",
|
||||
"Q4",
|
||||
Some(vec!["Q1".into(), "Q2".into(), "Q3".into(), "Q4".into()]),
|
||||
),
|
||||
];
|
||||
state
|
||||
}
|
||||
@@ -4040,7 +4048,10 @@ mod tests {
|
||||
#[test]
|
||||
fn a_filter_axis_is_projected_but_not_cycled() {
|
||||
let mut state = paged();
|
||||
state.grid.page_coords.push(crate::PageAxis::plain("Region", "East", None));
|
||||
state
|
||||
.grid
|
||||
.page_coords
|
||||
.push(crate::PageAxis::plain("Region", "East", None));
|
||||
let moved = selections(resolve(&state, ev(KeyCode::Char(']'))));
|
||||
assert!(
|
||||
!moved.iter().any(|(cat, _)| cat == "Region"),
|
||||
@@ -4076,11 +4087,18 @@ mod tests {
|
||||
fn picking_a_displayed_page_item_still_names_its_key() {
|
||||
let mut state = paged();
|
||||
state.grid.page_coords[1].cat_display = "Period".into();
|
||||
state.grid.page_coords[1].item_displays =
|
||||
Some(["First", "Second", "Third", "Fourth"].map(String::from).to_vec());
|
||||
state.grid.page_coords[1].item_displays = Some(
|
||||
["First", "Second", "Third", "Fourth"]
|
||||
.map(String::from)
|
||||
.to_vec(),
|
||||
);
|
||||
let commands = click(&state, Click::PageItem { axis: 1, item: 1 });
|
||||
let [Command::Model { cmd: ModelCommand::SetPageSelection { category, item, .. }, .. }] =
|
||||
commands.as_slice()
|
||||
let [
|
||||
Command::Model {
|
||||
cmd: ModelCommand::SetPageSelection { category, item, .. },
|
||||
..
|
||||
},
|
||||
] = commands.as_slice()
|
||||
else {
|
||||
panic!("expected one page selection, got {commands:?}");
|
||||
};
|
||||
@@ -4105,8 +4123,12 @@ mod tests {
|
||||
let KeyOutcome::Local(commands) = resolve(&state, ev(KeyCode::Char('H'))) else {
|
||||
panic!("hiding resolves here");
|
||||
};
|
||||
let [Command::Model { cmd: ModelCommand::HideItem { category, item, .. }, .. }] =
|
||||
commands.as_slice()
|
||||
let [
|
||||
Command::Model {
|
||||
cmd: ModelCommand::HideItem { category, item, .. },
|
||||
..
|
||||
},
|
||||
] = commands.as_slice()
|
||||
else {
|
||||
panic!("expected one hide, got {commands:?}");
|
||||
};
|
||||
@@ -4118,7 +4140,10 @@ mod tests {
|
||||
#[test]
|
||||
fn a_filter_axis_offers_no_item_to_pick() {
|
||||
let mut state = paged();
|
||||
state.grid.page_coords.push(crate::PageAxis::plain("Region", "East", None));
|
||||
state
|
||||
.grid
|
||||
.page_coords
|
||||
.push(crate::PageAxis::plain("Region", "East", None));
|
||||
assert!(
|
||||
click(&state, Click::PageItem { axis: 2, item: 0 }).is_empty(),
|
||||
"a Filter axis has no item list, so there is nothing a click could mean"
|
||||
|
||||
@@ -951,7 +951,10 @@ mod tests {
|
||||
));
|
||||
assert_eq!(model.col_levels, vec![vec!["Sales".to_string()]]);
|
||||
let bar = &model.page_coords[0];
|
||||
assert_eq!((bar.cat.as_str(), bar.selected.as_str()), ("Period", "First"));
|
||||
assert_eq!(
|
||||
(bar.cat.as_str(), bar.selected.as_str()),
|
||||
("Period", "First")
|
||||
);
|
||||
assert_eq!(bar.cycle.as_ref().unwrap().items, vec!["First", "Second"]);
|
||||
assert_eq!(model.categories[1].label, "Eastern Seaboard");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user