Skip to content

feat(react,ui): built-in per-column header filter menu#185

Merged
blove merged 8 commits into
mainfrom
claude/filter-ui
Jun 27, 2026
Merged

feat(react,ui): built-in per-column header filter menu#185
blove merged 8 commits into
mainfrom
claude/filter-ui

Conversation

@blove

@blove blove commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Sub-project 2 of 3 of "filter UI + operators" (after the engine in #180). Adds a built-in, per-column header filter menu to @pretable/react, styled in @pretable/ui.

What it does

Every filterable column gets a header funnel (visible on header hover/focus, and permanently accented when a filter is active). Clicking it opens a popover dialog: an operator <select> (only the operators valid for the column's filterType) + a value control that swaps by type — text input, two inputs for between, native date input(s), or a checkbox list for enum (options from filterOptions, else auto-derived via distinctColumnValues). isEmpty/isNotEmpty hide the value control.

  • Live-apply via grid.setColumnFilter (free-text debounced ~200ms; select/checkbox/date immediate). Multi-part ops (between/dateBetween) gate — a half-entered range clears rather than blanking the grid.
  • Uncontrolled by default, firing the new onFiltersChange(filters: Record<string, ColumnFilter>); fully controllable via the existing state.filters.
  • On by default for filterable columns (mirrors resize/reorder); opt out with filterable: false.

Implementation

  • New internal module packages/react/src/filter-menu/ — pure filter-operators.ts (operators-per-type, value-shape, completeness gating, draft↔ColumnFilter) + FunnelButton / useFilterPopover / FilterMenu. None are publicly exported — the only new public API is the onFiltersChange prop.
  • The funnel is an absolutely-positioned sibling overlay of the resize handle (the header is itself a <button>, so the funnel can't nest); one FilterMenu renders at the surface root (fixed-position) to escape header clipping. Clicking the funnel never triggers sort/resize/reorder.
  • @pretable/ui/grid.css: :where() + data-pretable-filter-*, reusing existing tokens (no new tokens; contract test untouched).
  • A11y: funnel aria-haspopup="dialog"/aria-expanded; popover role="dialog" with an accessible name; Escape + outside-click close; native controls throughout.

Notes

  • Final review caught one bug (switching directly between two columns' funnels reused a stale menu draft) — fixed by keying the root FilterMenu on columnId, with a regression test.
  • Known minor trade-off: hovering the header row reveals all funnels at once (flat-sibling DOM; a per-column ~ selector over-matches) — within spec, avoids per-hover re-renders.

Scope

Hero adoption, a filtering docs page, and Playwright e2e are sub-project 3. The hero still uses controlled state.filters and is unchanged here.

Validation

pnpm -r typecheck/lint/test (react 272, ui 19, website 220), pnpm format, website build, and pnpm api (refreshed for onFiltersChange; second run a no-op) all green locally.

Spec: docs/superpowers/specs/2026-06-19-filter-header-menu-design.md · Plan: docs/superpowers/plans/2026-06-19-filter-header-menu.md

🤖 Generated with Claude Code

blove and others added 8 commits June 22, 2026 18:48
Per-column funnel → popover (operator select + typed value control by
filterType), live-apply with multi-part gating, onFiltersChange, on-by-default
for filterable columns. React component + @pretable/ui vanilla CSS; RTL tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nnels

Clicking a different column's funnel while a menu is open reused the same
FilterMenu instance, so its mount-only draft stayed stale (previous column's
operator/value) and an edit would write the stale draft to the new column.
Key the root FilterMenu by columnId so it remounts (re-hydrates draft +
re-focuses) on column change. Adds a regression test for the direct switch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pretable Ready Ready Preview, Comment Jun 27, 2026 10:47pm

@blove blove enabled auto-merge (squash) June 27, 2026 22:47
@github-actions

Copy link
Copy Markdown
Contributor

Vercel preview ready

Preview: https://pretable-n9u8uuupl-cacheplane.vercel.app
Commit: 3a3fd96bfef68b6accda04c398af5df8176be2c6

Updated automatically by the deploy-preview job.

@blove blove merged commit 6f63abe into main Jun 27, 2026
14 checks passed
@blove blove deleted the claude/filter-ui branch June 27, 2026 22:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant