Skip to content

feat(table): unified filter pill UI with overflow strip#9638

Open
kirangadhave wants to merge 6 commits into
mainfrom
kg/table-filter-popover
Open

feat(table): unified filter pill UI with overflow strip#9638
kirangadhave wants to merge 6 commits into
mainfrom
kg/table-filter-popover

Conversation

@kirangadhave
Copy link
Copy Markdown
Member

@kirangadhave kirangadhave commented May 21, 2026

Summary

Replaces the column-header value-picker submenu with a unified pill-based filter strip below the table. Users add, edit, and remove filters via a single editor popover anchored to the pills row.

UI

  • New + button on the pills strip opens the editor. The column-header ··· menu routes through the same editor.
  • Persistent filter icon on column headers whose column has an active filter.
  • Filter pills format date / datetime / time values using the same helpers as column cells, so a pill matches what the cell shows.
  • "Filter by values" is shown only on text and numeric columns.
  • When pills overflow the strip width, the row clips with a soft right-edge fade and a "See all" button appears. Clicking it opens a draggable popover that lists every active filter. Overflow detection is a scrollWidth > clientWidth check on the strip itself with a ResizeObserver, re-measured on filter content change.
Screenshot 2026-05-22 at 3 46 08 PM
  • Value truncation with a full-segments tooltip and inline is_in / not_in chip rendering via a shared chip primitive reused by the editor picker.

Filter by values editor

Screenshot 2026-05-22 at 3 47 39 PM

Filter pill + Tooltip

Screenshot 2026-05-22 at 3 47 03 PM

Internals

  • Filter logic split out of filters.ts into a filters/ folder (types, operators, guards, builders, defaults, serialize, format).
    • Filters code blew up with all new additions, now the code is organized by concepts, so adding new filters or features should be easier.
  • Removes the select filter type. Text and number columns carry their own in / not_in membership shape directly. supportsMembership is promoted to a type guard (isMembershipFilterType) used at every call site.
    • select was incorrect conceptually. select is a filter operator rather than a type.
  • Operator groups (NULLISH, MEMBERSHIP, COMPARISON, TEXT_SCALAR, BOOLEAN_VALUE, EMPTY) consolidated so OPERATORS_WITHOUT_VALUE and per-type lists derive from one source.
    • Grouping operators by shape and purpose makes sense rather than just data type. We compose data type filters from the shape/purpose primitives.
    • For e.g. number column can have nullish, membership, comparision operators, but not text_scalar, empty or boolean operators
  • Local-ISO date formatters moved to utils/dates and renamed dateToLocalISO* to make local-time semantics explicit (vs. Date.toISOString).

Demo

Screen.Recording.2026-05-22.at.4.11.59.PM.mov

Test plan

  • Open mo.ui.table(df) with text, number, date, datetime, time, and boolean columns. Column ··· menu shows "Filter by values" only on text and numeric columns.
  • + button on the pill strip and the column-header ··· Filter item both open the same editor.
  • Filtered columns show the persistent filter icon next to the header.
  • Add enough filters that the strip overflows: right edge fades, "See all" appears, popover lists every filter.
  • Resize the table narrower / wider: "See all" appears / disappears as overflow changes.
  • Date / datetime / time pill text matches the column-cell formatting for the same value.
  • Pasting "X and Y", "X to Y", "X - Y", "X – Y" into a date range input all parse into the two date fields.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

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

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment May 22, 2026 10:39pm

Request Review

cubic-dev-ai[bot]

This comment was marked as resolved.

@kirangadhave kirangadhave added the enhancement New feature or request label May 21, 2026
@kirangadhave kirangadhave force-pushed the kg/table-filter-popover branch from 459bdd8 to 9a009bd Compare May 21, 2026 23:23
@kirangadhave kirangadhave marked this pull request as ready for review May 21, 2026 23:23
Copilot AI review requested due to automatic review settings May 21, 2026 23:23

This comment was marked as resolved.

@kirangadhave
Copy link
Copy Markdown
Member Author

@cubic-dev-ai

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented May 22, 2026

@cubic-dev-ai

@kirangadhave I have started the AI code review. It will take a few minutes to complete.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 17 files

Architecture diagram
sequenceDiagram
    participant Table as DataTable
    participant Ctx as FilterEditorProvider
    participant Header as ColumnHeader
    participant Pills as FilterPills
    participant Ghost as Ghost Row (hidden)
    participant AddBtn as AddFilterButton
    participant Editor as FilterPillEditor
    participant ChipPicker as CompactChipRow / Chip
    participant Overflow as Overflow Popover

    Note over Table,Overflow: Unified filter pill UI with overflow strip

    Table->>Ctx: Provide requestAddFilter callback
    Table->>Pills: Render with filters, addFilterSnapshot, onAddFilterSnapshotChange
    Table->>Pills: Pass table, calculateTopKRows

    Header->>Ctx: useFilterEditor()
    Ctx-->>Header: requestAddFilter

    alt User clicks "Filter" in column header
        Header->>+Ctx: requestAddFilter({ columnId })
        Ctx->>Table: setAddFilterSnapshot(buildEditorSnapshot(column))
        Table->>Pills: update addFilterSnapshot prop
        Pills->>AddBtn: snapshot != null
        AddBtn->>Editor: render FilterPillEditor with snapshot
    else User clicks "Filter by values" in column header
        Header->>+Ctx: requestAddFilter({ columnId, operator: "in" })
        Ctx->>Table: setAddFilterSnapshot(buildEditorSnapshot(column, { operator: "in" }))
        Table->>Pills: update addFilterSnapshot prop
        Pills->>AddBtn: snapshot != null
        AddBtn->>Editor: render FilterPillEditor with "in" operator
    end

    alt User clicks "+" button on pills strip
        AddBtn->>AddBtn: buildEditorSnapshot(first editable column)
        AddBtn->>AddBtn: set open = true
        AddBtn->>Editor: render FilterPillEditor
    else User clicks existing filter pill
        Pills->>Editor: open FilterPillEditor with pill's snapshot & editIndex
    end

    Editor->>Editor: User modifies operator/value
    Editor->>Table: onApply → setColumnFilters(updater)
    alt editIndex undefined (new filter)
        Table->>Table: prepend { id: draftColumnId, value } to filters
    else editIndex defined (edit existing)
        Table->>Table: replace filters[editIndex] = { id: draftColumnId, value }
    end
    Editor->>Pills: onClose → close popover

    Note over Pills,Ghost: Overflow detection uses hidden ghost row
    Pills->>Ghost: Always render all pills at natural width (hidden, not displayed)
    Ghost-->>Pills: Provide scrollWidth measurement
    Pills->>Pills: Compare scrollWidth > clientWidth via ResizeObserver
    alt hasOverflow
        Pills->>Pills: Apply CSS mask gradient on container
        Pills->>Pills: Show +N button at right edge
        Pills->>Overflow: open DraggablePopover with full filter list
        Overflow-->>Table: "Clear all" → setColumnFilters([])
    end

    Note over Pills,ChipPicker: Pill rendering
    Pills->>ChipPicker: Format value (scalar or multi-value)
    alt scalar value
        ChipPicker-->>Pills: Render truncated text with tooltip on hover
    else multi-value (in/not_in)
        ChipPicker->>ChipPicker: CompactChipRow with max 3 visible chips
        ChipPicker-->>Pills: Render chips with +N overflow indicator
    end

    Note over Table: Column header shows persistent filter indicator
    Header->>Header: hasFilter → show FilterIcon next to header text
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread frontend/src/components/data-table/filter-pills.tsx
Comment thread frontend/src/components/data-table/column-header.tsx Outdated
Introduces the filter pill strip with a + button to add filters and an overflow popover with a 'See all' button gated by scrollWidth detection. Pills open the filter editor directly from a column-header menu, show a persistent indicator on filtered headers, and 'Filter by values' extends to numeric, date, and time columns. Adds value truncation with a full-segments tooltip, inline is_in/not_in chip rendering, and a shared chip primitive reused by the editor picker.
Pills now use the same dateToISODate, exactDateTime, and dateToISOTime helpers as the column cells, and hide 'Filter by values' on date, datetime, and time columns since those should use the range picker. parsePastedRange also accepts 'and' as a separator alongside hyphen, en-dash, em-dash, and 'to'.
Also consolidates the filter test harness and adds column-header menu coverage.
Splits filters.ts into a filters/ folder by concern (types, operators, guards, builders, defaults, serialize, format). Removes the 'select' filter type; text and number columns now carry their own in/not_in membership shape. Consolidates operator groups so OPERATORS_WITHOUT_VALUE and per-type op lists derive from one source, and promotes supportsMembership to a type guard (isMembershipFilterType) used at all call sites. columnEditableType now throws on invalid filterType instead of silently falling back to text, and is reused in handleColumnChange. Drops the trivial getEditableType wrapper and the ColumnFilterValue cast in defaultFilterValueFor's membership branch. Renames renderSortFilterIcon to renderSortIcon and narrows filterType once in context-menu. Editor defaults for date/datetime are now '=='.
dateToISODate/Time/DateTime were generic Date-to-string helpers stranded
in filters/serialize.ts after the folder split. Move them to utils/dates
alongside the existing date formatters, rename to make the local-time
semantics explicit (dateToLocalISO*), and add docstrings clarifying when
to use them versus Date.toISOString().
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 2 comments.

Comment thread frontend/src/components/data-table/filter-pills.tsx Outdated
Comment thread frontend/src/components/data-table/data-table.tsx Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants