Data grid · Lab

Per-column filters

The real Rocky Creek fleet — 50 buses — with filtering moved out of the toolbar and into the column headers. Click a label to sort. Click the funnel beside it to filter that column. It's live; break it.

What we're solving

Today /fleet filters by one hand-written toolbar button (Status) and /tablets by three (Updates · Device · Assignment). Each is about 35 lines of bespoke Popover, each covers only the column someone thought to wire up, and none of them sit anywhere near the data they act on. Make, Model, Year, Route, Seats, Mileage and Defects have no filter at all.

The plumbing was already here: both grids register TanStack's faceted row models and never read them, and ui/table-header-filter.tsx has shipped a header-popover mode since day one with zero call sites. This lab spends both.

The grid

50 of 50 buses
0019At Depot
0315At Depot
0717On Route
0817Out of Service
0923At Depot
1019On Route
1225On Route
1419On Route
2020On Route
2121At Depot
2519Out of Service
2621At Depot
2815At Depot
2921At Depot
3022At Depot
3119On Route
3223At Depot
3322On Route
3422On Route
3518Out of Service
3620At Depot
3722At Depot
3820At Depot
4020At Depot
4116On Route
4216On Route
4320At Depot
4425At Depot
4722At Depot
4925At Depot
5615At Depot
6019On Route
6318At Depot
6923On Route
7225Out of Service
7324At Depot
7620At Depot
7720On Route
7915On Route
8017On Route
8519On Route
8723On Route
8819On Route
8925On Route
9016At Depot
9215At Depot
9324On Route
9720At Depot
9825On Route
9918On Route

Live filter state

No column filters active.

States worth poking at

Counts narrow as you filter

Check Blue Bird under Make, then open Model. The counts are for buses that pass every other column's filter — but Make's own list stays whole, so the box you just ticked can never vanish under your cursor.

A hidden column keeps filtering

Filter Defects, then switch it off in the Columns menu — the row count holds. Its checkbox there keeps a brand funnel, the readout below the grid says (column hidden), and every other column's menu offers Clear all. Narrow the window pastxl for the same thing by responsive tier.

Long lists search, short ones don't

Route and Bus # pass eight distinct values and grow a search box; Make and Status stay a bare list. Search something absurd to see the no-match line.

Nothing you type gets lost

Checkboxes filter on the tick and leave the menu open. Min/Maxcommit on a short debounce — so typing 40000 doesn't filter at 4, then 40, then 400, and dismissing with Escape still keeps what you typed. Committing on blur looked right and quietly dropped the entry, because the popover unmounts without the input ever blurring. Put a Min above the Max to see it say so.

Still open

  • Right-aligned columns mirror. On Seats / Mileage / Defects the funnel takes the far left edge so the label stays flush right with the numbers under it. Alignment won over icon-side consistency — say if that's the wrong trade.
  • Notes has no funnel. Free text with no natural value set. It could take a contains-search or a Has note / No note pair, but both would be invented rather than asked for.
  • Bus # is a 50-value checklist. Correct per the checklist-everywhere rule, but a contains-search may serve an identifier column better than fifty checkboxes.
  • Ranges are two number boxes, not a slider. A dual-thumb slider has no native control behind it; ui/range-editor-popover.tsx is single-value only.