Skip to content

feat(ui): a host can activate a saved view and read the view list through the DataTable view API - #140

Closed
maxidragon wants to merge 2 commits into
developfrom
datatable-view-api
Closed

maxidragon wants to merge 2 commits into
developfrom
datatable-view-api

Conversation

@maxidragon

@maxidragon maxidragon commented Oct 7, 2026 •

Copy link
Copy Markdown

Problem

A host page may want to render its own switcher for saved views, such as tabs above the table, instead of DataTable's built-in "Views" dropdown. DataTable already exposes an imperative handle for host-built controls (viewApiRef → getCurrentSettings, getDirtyState, saveCurrentView, openViewsSidebar), but two pieces were missing:

  1. No way to activate a view from outside. Activation (handlePerspectiveActivate → applyPerspectiveSettings) was internal.
  2. No way to know which views exist. The perspectives query result never reached the host.

Without both, a host switcher can't be built. With both, it takes a few lines.

Solution

Three additive members. Nothing existing changes:

Addition Behavior
DataTableViewApi.activatePerspective(id: string | null) Applies a personal or role view by id through the same path the built-in switcher takes. null clears to "No view" like the dropdown's "All views" entry (empty settings, no active id). An unknown id is a no-op and doesn't throw.
DataTableViewApi.getPerspectives() Returns { perspectives, rolePerspectives, activePerspectiveId } as the built-in switcher sees them. Empty arrays while the list loads.
DataTablePerspectiveConfig.onPerspectivesChange(state) Push counterpart of getPerspectives(). Fires with the same shape whenever the list or the active view changes, so a host switcher re-renders without polling.

The state shape is exported as DataTablePerspectivesState.

All three use the same perspectives.use gate as the built-in switcher. A user without the feature gets the empty state and a no-op activatePerspective. This holds even when the list was pre-seeded through initialState.response.

Compatibility

  • Nothing existing is removed or changed. The config type gains an optional onPerspectivesChange. The handle gains two members, both required because DataTableViewApi is what DataTable hands out and hosts only read it. The one kind of code this affects is a test that builds a stand-in handle by hand as a DataTableViewApi object literal: it has to add activatePerspective and getPerspectives.
  • The built-in dropdown and sidebar are untouched. A host that wants to hide them handles that itself.
  • apps/docs/docs/framework/admin-ui/perspectives.mdx gains a "Rendering your own view switcher" section with a Tabs example that renders personal and role views.

Verification

  • New packages/ui/src/backend/__tests__/DataTable.perspectiveViewApi.test.tsx has 6 cases. The perspectives API is mocked through seeded query data, as in the existing view-API suites. The cases cover:
    • activating a personal view by id applies its filters, sorting and columnVisibility, asserted through onFiltersApply, onSortingChange and getCurrentSettings()
    • activating a role view by id
    • activatePerspective(null) clears to no view
    • an unknown id changes nothing
    • getPerspectives() reflects the response, and onPerspectivesChange fires on load and again after activation
    • a user without perspectives.use gets the empty state, and activation is a no-op
  • Mutation check: dropping the permission gate fails the "no permission" case, and dropping the push call fails the two onPerspectivesChange cases.
  • npx jest src/backend/__tests__/DataTable in packages/ui: 19 suites, 107 tests, all passing.
  • yarn typecheck (packages/ui) is clean. ESLint shows no new findings on the changed files.

Labels

needs-qa because the change is under packages/ui/src/, although nothing visible changes unless a host uses the new members. risk-low because the change is purely additive and opt-in.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

A host page that wants its own saved-view switcher (tabs above the table,
say) instead of DataTable's built-in "Views" dropdown could not build one:
the imperative view API had no way to activate a view, and the list of
views stayed inside the component's query.

Three additive members close the gap:

- `DataTableViewApi.activatePerspective(id)` applies a personal or role
  view through the same path the built-in switcher takes; `null` clears to
  "No view", an unknown id is a no-op.
- `DataTableViewApi.getPerspectives()` returns the personal views, role
  views and active view id (empty arrays while loading).
- `DataTablePerspectiveConfig.onPerspectivesChange` pushes the same shape
  whenever the list or the active view changes, so the host switcher
  re-renders without polling.

All three sit behind the `perspectives.use` check the built-in switcher
uses: a user without it gets the empty state, even when the list was
pre-seeded through `initialState.response`. The built-in dropdown and
sidebar are unchanged.
@maxidragon maxidragon added review Ready for code review priority-medium Normal product priority needs-qa Requires manual QA before merge risk-low feature New feature labels Oct 7, 2026
@maxidragon
maxidragon requested a balanced review from Copilot October 7, 2026 11:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Required additions break the exported handle’s compatibility, and the documentation example omits role views.

Review effort: Balanced
Findings: 1 High severity · 1 Low severity

Open (2)
What changed in this PR

Adds host-controlled saved-view switching to DataTable.

Changes:

  • Exposes perspective listing, activation, and change notifications.
  • Adds coverage for activation and permission gating.
  • Documents a custom tabs-based switcher.
File Description
packages/​ui/​src/​backend/​DataTable.tsx Extends the perspective API.
packages/​ui/​src/​backend/​__tests__/​DataTable.perspectiveViewApi.test.tsx Tests the new API behavior.
apps/​docs/​docs/​framework/​admin-ui/​perspectives.mdx Documents custom view switchers.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/ui/src/backend/DataTable.tsx Resolved
The example rendered only personal views, so copying it hid every role
view even though activatePerspective switches both.
@blacksmith-sh

This comment has been minimized.

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

DS lint — 287 findings (+40 vs develop)

Rule develop this PR Δ
om-ds/no-raw-table 120 163 +43
om-ds/require-loading-state 50 50 0
om-ds/require-page-wrapper 24 24 0
om-ds/no-hardcoded-status-colors 18 18 0
om-ds/no-legacy-alert-variant 13 13 0
om-ds/require-empty-state 15 12 -3
om-ds/require-status-badge 7 7 0
DS lint opt-outs 1 0 -1

New findings

  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:571 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:572 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:574 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:575 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:600 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:603 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:606 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:622 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:623 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:690 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:691 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:694 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/page.tsx:697 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1135 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1136 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1137 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1138 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1141 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1144 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1147 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1152 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1154 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1155 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1156 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1159 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx:1169 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx:474 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx:475 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx:476 — om-ds/no-raw-table
  • packages/core/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx:477 — om-ds/no-raw-table
  • …and 13 more

@blacksmith-sh

blacksmith-sh Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Found 4 test failures on Blacksmith runners:

Failures

Test View Logs
packages/core/src/modules/messages/integration/
TC-MSG-013.spec.ts:59:3 › TC-MSG-013: Inbox Bulk Mark Read And Mark Unread › should bul
k mark inbox messages read and unread and keep selection on total failure
View Logs
packages/core/src/modules/planner/integration/
TC-PLAN-005-ruleset-prefill.spec.ts:14:3 › TC-PLAN-005: Availability editor hydrates sa
ved rule set › shows the saved rule set when it is outside the first loaded options pag
e
View Logs
packages/core/src/modules/sales/integration/
TC-SALES-ADDR-CONTACT-003.spec.ts:15:1 › TC-SALES-ADDR-CONTACT-003: locked orders disab
le address contact fields
View Logs
packages/core/src/modules/staff/integration/
TC-TT-017.spec.ts:42:3 › TC-TT-017: No access to a project › shows the guard state with
out leaking the customer or project, and the request-access action works
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@maxidragon

Copy link
Copy Markdown
Author

CI failure is unrelated. I fixed it in #143, but looks like CI on open-mercato/develop is also failing due to another reason :/

@maxidragon maxidragon closed this Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature needs-qa Requires manual QA before merge priority-medium Normal product priority review Ready for code review risk-low

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants