feat(table): add column visibility kwargs and UI controls#9687
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
2 issues found across 15 files
Architecture diagram
sequenceDiagram
participant User as Python User
participant Table as mo.ui.table (Python)
participant Val as Validation
participant Plugin as DataTablePlugin (Frontend)
participant Schema as Zod Schema
participant Hook as useColumnVisibility
participant DT as DataTableInternal
participant ColHeader as ColumnHeaderMenu
participant ColExplorer as ColumnExplorerPanel
participant BottomBar as TableBottomBar
participant Banner as All-Hidden Banner
Note over User,Banner: Column Visibility Flow (Happy Path)
User->>Table: table(data, hidden_columns=["col1"])
Table->>Val: _validate_column_visibility()
Val-->>Table: validated list
Table->>Table: normalizes hidden_columns or visible_columns to hidden list
Table-->>Plugin: payload with hiddenColumns
Plugin->>Schema: parse hiddenColumns
Schema-->>Plugin: validated string[]
Plugin->>DT: pass hiddenColumns prop
DT->>Hook: useColumnVisibility(hiddenColumns)
Hook->>Hook: init state: {"col1": false}
Hook-->>DT: columnVisibility, setColumnVisibility
DT->>DT: table options: onColumnVisibilityChange = setColumnVisibility
DT->>DT: state: columnVisibility
Note over DT,Banner: UI Controls
User->>ColHeader: click column header menu
ColHeader->>ColHeader: render HideColumn component
alt column.getCanHide()
User->>ColHeader: click "Hide column"
ColHeader->>ColHeader: column.toggleVisibility(false)
ColHeader-->>DT: visibility state update
else column is system/row header
ColHeader->>ColHeader: returns null (no hide option)
end
DT->>BottomBar: render with table ref
BottomBar->>BottomBar: getUserColumnVisibilityCounts(table)
BottomBar-->>BottomBar: {total, visible, hidden}
alt hidden > 0
BottomBar->>BottomBar: render "(N hidden)" button
User->>BottomBar: click "(N hidden)"
BottomBar->>ColExplorer: togglePanel(COLUMN_EXPLORER)
else hidden = 0
BottomBar->>BottomBar: no suffix rendered
end
Note over DT,Banner: All-Hidden Recovery
DT->>DT: getUserColumnVisibilityCounts(table)
alt total > 0 AND visible = 0
DT->>Banner: render "All columns are hidden" banner
User->>Banner: click "Unhide all"
Banner->>DT: table.resetColumnVisibility()
DT->>Hook: setColumnVisibility({})
else visible > 0
DT->>Banner: no banner rendered
end
Note over User,ColExplorer: Value Independent of Visibility
User->>Table: .value
Table-->>User: includes hidden columns (view-only concern)
Note over ColExplorer: Column Explorer Display
ColExplorer->>ColExplorer: prettifyRowColumnCount(hiddenColumns)
ColExplorer-->>ColExplorer: shows visible count + hidden indicator
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
Pull request overview
Adds column visibility support to mo.ui.table by introducing backend kwargs (hidden_columns / visible_columns) and wiring them through the frontend to TanStack Table column-visibility state, along with initial UI affordances (hide-from-header-menu, hidden-count indicator, and an all-hidden recovery banner).
Changes:
- Backend: add
hidden_columns/visible_columnskwargs, validate them, and emit a normalizedhidden-columnspayload. - Frontend: extend table plugin payload/schema, seed and manage TanStack
columnVisibility, and add UI controls (hide action + hidden-count indicator + “all hidden” banner). - Tests: add Python + frontend unit tests covering visibility seeding and UI indicators.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/_plugins/ui/_impl/test_table.py | Adds backend tests for hidden/visible kwargs normalization and validation. |
| marimo/_plugins/ui/_impl/table.py | Implements kwargs, validation, and hidden-columns payload emission. |
| frontend/src/plugins/impl/DataTablePlugin.tsx | Extends plugin Data + Zod schema, passes hiddenColumns into DataTable. |
| frontend/src/components/data-table/TableBottomBar.tsx | Shows hidden-count suffix and hooks it to opening Column Explorer. |
| frontend/src/components/data-table/pagination.tsx | Updates prettifyRowColumnCount to return structured output incl. hidden suffix. |
| frontend/src/components/data-table/hooks/use-column-visibility.ts | Adds useColumnVisibility hook and column visibility counting helper. |
| frontend/src/components/data-table/header-items.tsx | Adds HideColumn dropdown item component. |
| frontend/src/components/data-table/data-table.tsx | Wires columnVisibility into table state and renders “all hidden” banner. |
| frontend/src/components/data-table/columns.tsx | Marks which columns are hideable (excludes row headers/system/unnamed). |
| frontend/src/components/data-table/column-header.tsx | Inserts hide action into the column header dropdown menu. |
| frontend/src/components/data-table/column-explorer-panel/column-explorer.tsx | Adjusts to new prettifyRowColumnCount return type. |
| frontend/src/components/data-table/tests/useColumnVisibility.test.ts | Tests seeding and updating visibility state. |
| frontend/src/components/data-table/tests/TableBottomBar.test.tsx | Tests hidden-count rendering and click behavior. |
| frontend/src/components/data-table/tests/header-items.test.tsx | Tests HideColumn menu item behavior. |
| frontend/src/components/data-table/tests/data-table.test.tsx | Tests the “all columns hidden” banner visibility behavior. |
Companion to the backend change that started emitting `hidden-columns` in the args payload. This wires the Zod schema, the `DataTableProps` boundary, and a new `useColumnVisibility` hook so the next commit can hand TanStack's `columnVisibility` state a seed. The hook mirrors `useColumnPinning`: `useInternalStateWithSync` with a dequal comparator. That keeps user toggles sticky across renders while still re-applying a fresh seed when the upstream kwarg changes (the visibility kwargs are seeds, not constraints). No UI behavior change yet. `useReactTable` is not consuming the hook in this commit.
Consumes the `hidden-columns` payload via the new `useColumnVisibility` hook. The hook seeds TanStack's `columnVisibility` state from the prop and routes user toggles back through `onColumnVisibilityChange`. Also gates which columns may be hidden: row headers and nameless columns get `enableHiding: false`. The selection column already had it. This protects structural columns from being removed by the column-header menu added in the next commit.
Adds a "Hide column" entry to the column-header dropdown using TanStack's `column.toggleVisibility`. The item only renders for columns with `enableHiding: true`, which excludes row headers, nameless columns, and the selection column. Implemented as a React component (`<HideColumn />`) rather than the `renderX(column)` helpers used elsewhere in this file. The pattern shift is captured as a follow-up refactor for the other helpers; both shapes coexist with a TODO at the call site. No guard against hiding the last visible column. The all-hidden state is reachable from both this menu and from `hidden_columns=[...]` on the Python side; recovery happens through a single banner rather than ad-hoc per-path guards.
Footer renders "(N hidden)" suffix when columns are hidden; clicking it opens the Column Explorer panel.
When every user-hideable column is hidden, render a banner with an 'Unhide all' button above the table. Extract a shared helper for user column visibility counts.
resetColumnVisibility() without args reverts to initial state seeded from the hidden_columns kwarg, which leaves user-passed hidden columns hidden. Pass true so all columns become visible.
When DataTablePlugin clips columns, the table instance only has the rendered subset. Use that subset's total for visible/hidden math; keep the dataset-wide prop only for the no-hidden label.
55c3706 to
dffd209
Compare
| {hiddenSuffix && ( | ||
| <button | ||
| type="button" | ||
| className="text-xs underline-offset-2 hover:underline cursor-pointer" | ||
| onClick={() => togglePanel?.(PANEL_TYPES.COLUMN_EXPLORER)} | ||
| > | ||
| {hiddenSuffix} | ||
| </button> | ||
| )} |
|
🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.23.9-dev5 |
## Summary Second of two PRs implementing `mo.ui.table` column visibility (RFC). Builds on #9687 (PR 1: Python kwargs, hooks, hidden-count display, recovery banner). - Substring filter replaced with `smartMatch`: queries split on whitespace and each word must prefix a word in the column name. - Click hides/shows the column. Eye icon when visible, eye-off when hidden. - "Unhide all" link + hidden count in the panel header. Shown only when at least one user column is hidden. https://github.com/user-attachments/assets/6f08b1cf-c5ba-474b-8a49-6cb86c9d5625 Related to #9220 Closes #9419
Summary
Adds
hidden_columns/visible_columnskwargs tomo.ui.tableand wires interactive visibility controls into the UI. Visibility is a view-only concern:.value, selection, search, sort, filter, and CSV download continue to include hidden columns. This is the first PR. I will have a follow up PR which adds changes to the column explorer. Right now there is no way to unhide the hidden column, PR 2 will add that.What's in PR 1
hidden_columns/visible_columnskwargs with validation and normalized payload.Datainterface updates.useColumnVisibilityhook seeded from the payload, wired intoDataTablestate.(N hidden)indicator that opens the Column Explorer (PR 2 will add unhide buttons here).PR 2 (discoverability: smart search, eye-icon toggle, "Unhide all" in Column Explorer) will follow.
Screen.Recording.2026-05-26.at.12.18.12.AM.mov
Addresses: #9220