Gunnar

nordisk/facett

.nornir/dataview-guide.md

# dataview — Iceberg / warehouse browsing — guide

> **Class:** `guide` · audience: a techie wiring one of the three dataview
> components into a host, or driving one from a test.
> The *what* is in [`dataview-design.md`](dataview-design.md). This file is the
> workflow.

## f0 — picking the right component (the whole workflow)

Three questions decide which of the three components you want:

1. **Do you need to scrub a single table's history (time-travel)?** →
   `facett-icebergview::IcebergView` (`f1`).
2. **Do you need several tables/graphs visible at once, as a dashboard?** →
   `facett-warehousedeck::WarehouseDeck` (`f2`).
3. **Do you need a simple "pick one table from a list, see its rows or an
   auto-chart" browser?** → `facett-table::warehouse::WarehouseTableView` (`f3`).

All three are `Facet`s: build one, `Box::new(it)` into a `FacetDeck`, and you get
the palette picker and headless testability for free — the same pattern as
[`grid-guide.md`](grid-guide.md) `f0`.

## f1 — driving `IcebergView`

Build it from a host-read scan per snapshot (order doesn't matter — it sorts
newest-first for you):

```rust
use facett_icebergview::{IcebergView, Snapshot, SnapshotMeta};

let view = IcebergView::new(
    "🧊 Tables",
    "Person",
    vec![
        Snapshot::new(SnapshotMeta::new(100, 1_000, 2), batches_v1),
        Snapshot::new(SnapshotMeta::new(200, 2_000, 3), batches_v2),
    ],
).with_page_size(500);
```

Time-travel and paging, both as direct calls and as headless `Msg`s:

```rust
view.select_snapshot(100);   // by stable Iceberg snapshot id
view.set_page(1);
view.current_id();           // Some(100)
view.snapshot_metas();       // newest-first, for a custom picker UI

// Headless, through the Elm seam:
use facett_core::harness;
use facett_icebergview::Msg;
let state = harness::snapshot(&mut view, [Msg::SelectSnapshot(200), Msg::NextPage]);
assert_eq!(state.current, Some(200));
```

When new snapshots accrue (a live feed), call `set_snapshots` again with the full
updated list — it re-sorts, re-selects the newest, and rebuilds the grid. Read
`view.state_json()` for the exact keys a headless assertion or a mega-matrix run
should check: `table`, `snapshots`, `current`, `total_rows`, `page`, `page_size`,
`page_count`, `columns`, `visible_rows`.

## f2 — building a `WarehouseDeck` wall

Start from the registry so table→view-kind mapping is one line per table:

```rust
use facett_warehousedeck::{WarehouseDeck, WarehouseRegistry, PaneData, GraphSpec, ChartSpec};

let registry = WarehouseRegistry::default_nordisk();
let mut deck = WarehouseDeck::new("🏢 Warehouse").with_cols(3);

// For each table your host scanned, look up its kind and add the matching pane:
match registry.kind_for("bench_runs") {
    Some(_) => { deck.add_for("bench runs", "bench_runs", PaneData::Grid(scanned_batches)); }
    None => {}
}
// Join-derived panes (a call graph from two tables) are added directly, no registry lookup:
deck.add_for("call graph", "call_edges", PaneData::Graph(GraphSpec::new(nodes, edges)));
```

Drive the layout / focus from a toolbar or headlessly — both routes go through the
same `Msg`s:

```rust
use facett_core::harness;
use facett_warehousedeck::{Msg, DeckLayout};

deck.set_layout(DeckLayout::Focus);          // direct call
harness::drive(&mut deck, [Msg::DrillInto("call graph".into())]);  // headless
assert_eq!(deck.selected(), Some("call graph"));
```

Reading `deck.state_json()["deck"]["trace"]["ran"]` after a headless render is the
way to assert every expected pane actually drew (each entry is
`deck.wall:<pane title>`), which is the strongest signal for "the dashboard came up
correctly" in a test.

## f3 — driving `WarehouseTableView`

```rust
use facett_table::warehouse::{WarehouseTableView, PreviewTable};

let view = WarehouseTableView::new("Warehouse")
    .with_table("bench_runs", PreviewTable::new(
        vec!["bench".into(), "ms".into()],
        vec![vec!["xml_parse".into(), "12.4".into()], vec!["json_parse".into(), "8.1".into()]],
    ))
    .with_table("repos", PreviewTable::new(
        vec!["name".into(), "lang".into()],
        vec![vec!["nornir".into(), "rust".into()]],
    ));
```

`WarehouseTableView::local()` / `::remote()` seed two demo tables with no host
attached — reach for these when you just need something on screen (a smoke test,
a storybook) rather than hand-rolling `PreviewTable`s.

Selecting a table and turning on the auto-chart, both directly and headlessly:

```rust
view.select("bench_runs");                       // direct call, by stable name
use facett_core::harness;
use facett_table::warehouse::Msg;
harness::drive(&mut view, [Msg::SetChart(true)]); // defaults value/label columns for you
```

A live feed that grows a table each tick calls `set_table(name, updated_preview)`
— it replaces an existing table's rows in place (selection preserved) or appends a
new one. `state_json()` exposes `tables`, `selected`, `chart` (`on`/`value_col`/
`label_col`), and `preview` (`loaded`, `columns`, `rows` as a count,
`numeric_columns`) for whatever's currently selected.