Skip to content

feat(extensions): add dashboard renderer contribution point#41703

Draft
rusackas wants to merge 4 commits into
masterfrom
feat/dashboard-renderer-extension-point
Draft

feat(extensions): add dashboard renderer contribution point#41703
rusackas wants to merge 4 commits into
masterfrom
feat/dashboard-renderer-extension-point

Conversation

@rusackas

@rusackas rusackas commented Jul 2, 2026

Copy link
Copy Markdown
Member

SUMMARY

This is the first step toward making the entire dashboard renderer a replaceable Extension: a new single-slot dashboards contribution point (following the SIP-151 extensions architecture) that lets an extension swap out everything that displays a dashboard and handles its interactions — header, filter bar, tabs, and grid — while the host keeps owning data fetching, hydration, URL/permalink resolution, CSS injection, document title, and theming.

The contract (@apache-superset/core/dashboards): extensions call dashboards.registerDashboardRenderer(descriptor, component) and receive a Redux-free DashboardRendererProps — dashboard identity + parsed metadata + layout (position_json), chart and dataset definitions, initial filter state resolved from the URL (permalinks, native_filters_key, legacy rison params), and embedded-style uiConfig flags. Host services remain available via the window.superset namespaces, and theming works via context since renderers mount inside the host's theme providers.

The slot semantics mirror the existing chat/editors precedents:

  • Singleton slot — most recent registration wins; a displaced provider is unregistered with a console warning, and its Disposable becomes a no-op.
  • Late registration swaps live via useSyncExternalStore (extensions load asynchronously after startup).
  • Custom renderers are view-mode only — edit mode always renders the built-in stack, which owns drag-and-drop editing and undo/redo.
  • Custom renderers are wrapped in an ErrorBoundary; the built-in renderer is the fallback when nothing is registered or the ENABLE_EXTENSIONS flag is off.

The seam: DashboardPage now builds the contract props from data it already fetches and renders a DashboardRendererHost. The current Redux-bound stack (DashboardContainerDashboardBuilder, plus the active-filter selectors) moves behind the same contract as DefaultDashboardRenderer, preserving the lazy DashboardBuilder chunk. Hydration and all other host behavior are unchanged — which means the embedded page (which reuses DashboardPage) inherits the seam for free.

Where this is going (follow-ups, in rough order):

  1. Host consumption of the reserved onDataMaskChange / onActiveTabsChange callbacks so custom-renderer filter state persists to permalinks.
  2. Incrementally migrating DefaultDashboardRenderer internals to consume the contract props instead of Redux, behind the now-stable interface.
  3. A dashboards events/API namespace (à la sqlLab) for richer host interop.
  4. Making the Embedded SDK a thin wrapper over this extension point, with the embedded config choosing its dashboard display extension (defaulting to the built-in renderer).

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

No visual change — with no extension registered (or in edit mode) the dashboard renders exactly as before.

TESTING INSTRUCTIONS

  1. cd superset-frontend && npx jest src/core/dashboards src/dashboard/containers/DashboardPage.test.tsx — covers registry slot semantics, host fallback/edit-mode/feature-flag/error-boundary/late-registration behavior, and the DashboardPage integration (contract props delivered, hydration still dispatched).
  2. Open any dashboard — identical behavior to master.
  3. With ENABLE_EXTENSIONS on, register a renderer from the devtools console:
    window.superset.dashboards.registerDashboardRenderer(
      { id: 'test.renderer', name: 'Test' },
      () => React.createElement('h1', null, 'custom renderer'),
    );
    The dashboard swaps live to the custom renderer; entering edit mode returns the built-in renderer.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags: ENABLE_EXTENSIONS
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

🤖 Generated with Claude Code

Introduces a single-slot `dashboards` contribution point (SIP-151
architecture) that lets an extension replace Superset's built-in
dashboard renderer while the host keeps owning data fetching,
hydration, URL/permalink resolution, CSS injection, and theming.

- New `dashboards` namespace in @apache-superset/core defining the
  DashboardRenderer descriptor and the Redux-free DashboardRendererProps
  contract (dashboard identity/metadata/layout, charts, datasets,
  initial dataMask/tabs/anchor, uiConfig, reserved change callbacks).
- Host registry (DashboardRendererProviders) with chat-style singleton
  semantics: most recent registration wins, displaced providers are
  unregistered with a warning, disposal of a displaced provider is a
  no-op.
- DashboardRendererHost resolves the active provider via
  useSyncExternalStore (late registration swaps live), wraps custom
  renderers in an ErrorBoundary, and always falls back to the built-in
  renderer in edit mode or when the EnableExtensions flag is off.
- The built-in stack (DashboardContainer/DashboardBuilder + filter
  selectors) moves behind the same contract as DefaultDashboardRenderer,
  preserving the lazy DashboardBuilder chunk.
- DashboardPage now builds the contract props from data it already
  fetches and renders DashboardRendererHost; hydration and all other
  host behavior are unchanged, so the embedded path inherits the seam.

This is the first step toward a fully Redux-decoupled dashboard
renderer and an Embedded SDK that is a thin wrapper over this
extension point.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added doc Namespace | Anything related to documentation dependencies:npm packages labels Jul 2, 2026
@dosubot dosubot Bot added change:frontend Requires changing the frontend dashboard Namespace | Anything related to the Dashboard labels Jul 2, 2026
@netlify

netlify Bot commented Jul 2, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit ac50d83
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a4acec64b6c8000078bc4a8
😎 Deploy Preview https://deploy-preview-41703--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.23810% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.62%. Comparing base (8bf3933) to head (ac50d83).
⚠️ Report is 64 commits behind head on master.

Files with missing lines Patch % Lines
superset-frontend/src/core/dashboards/index.ts 50.00% 3 Missing ⚠️
...tend/src/core/dashboards/DashboardRendererHost.tsx 88.23% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #41703      +/-   ##
==========================================
+ Coverage   64.60%   64.62%   +0.01%     
==========================================
  Files        2684     2689       +5     
  Lines      148283   148372      +89     
  Branches    34158    34175      +17     
==========================================
+ Hits        95802    95881      +79     
- Misses      50736    50746      +10     
  Partials     1745     1745              
Flag Coverage Δ
javascript 69.39% <95.23%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bito-code-review bito-code-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review Agent Run #a04561

Actionable Suggestions - 2
  • superset-frontend/src/core/dashboards/DashboardRendererProviders.ts - 1
    • Extract duplicated singleton pattern · Line 54-54
  • superset-frontend/src/dashboard/components/DashboardRenderer/DefaultDashboardRenderer.tsx - 1
    • CWE-754: Missing Suspense for lazy component · Line 85-85
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset-frontend/src/dashboard/components/DashboardRenderer/DefaultDashboardRenderer.tsx - 1
  • superset-frontend/src/core/dashboards/index.ts - 1
Review Details
  • Files reviewed - 15 · Commit Range: fefe877..fefe877
    • superset-frontend/packages/superset-core/src/contributions/index.ts
    • superset-frontend/packages/superset-core/src/dashboards/index.ts
    • superset-frontend/packages/superset-core/src/index.ts
    • superset-frontend/src/core/dashboards/DashboardRendererHost.test.tsx
    • superset-frontend/src/core/dashboards/DashboardRendererHost.tsx
    • superset-frontend/src/core/dashboards/DashboardRendererProviders.test.ts
    • superset-frontend/src/core/dashboards/DashboardRendererProviders.ts
    • superset-frontend/src/core/dashboards/index.ts
    • superset-frontend/src/core/index.ts
    • superset-frontend/src/dashboard/components/DashboardRenderer/DefaultDashboardRenderer.tsx
    • superset-frontend/src/dashboard/containers/DashboardPage.test.tsx
    • superset-frontend/src/dashboard/containers/DashboardPage.tsx
    • superset-frontend/src/extensions/ExtensionsStartup.tsx
    • superset-frontend/src/extensions/Namespaces.ts
    • superset-frontend/src/types/Dashboard.ts
  • Files skipped - 3
    • docs/developer_docs/extensions/contribution-types.md - Reason: Filter setting
    • docs/developer_docs/extensions/extension-points/dashboards.md - Reason: Filter setting
    • superset-frontend/packages/superset-core/package.json - Reason: Filter setting
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment thread superset-frontend/src/core/dashboards/DashboardRendererProviders.ts
@rusackas rusackas marked this pull request as draft July 2, 2026 23:36
- Public namespace tests (src/core/dashboards/index.test.ts): register/
  get/dispose/events through the `dashboards` API object, mirroring the
  chat namespace tests.
- DefaultDashboardRenderer unit tests: the selectors moved from
  DashboardPage still derive activeFilters (legacy + native merge,
  chartsInScope resolution) and ownDataCharts (ownState only, TableChart
  clientView stripped) from Redux, and render DashboardBuilder inside
  DashboardContainer.
- DashboardPage integration tests: edit mode falls back to the built-in
  renderer at the seam level; URL filter state flows through to the
  custom renderer's initialDataMask; parsed metadata/position_data map
  into the contract's metadata/layout along with uiConfig defaults.
- ExtensionsStartup: window.superset.dashboards is exposed with a
  callable registerDashboardRenderer.
- New Playwright E2E spec (dashboard-renderer-extension.spec.ts),
  verified against a live docker instance: live swap with contract
  props, built-in grid unmounts, dispose restores it, and registering
  in edit mode never takes over. Skips itself when ENABLE_EXTENSIONS
  is off.

The oxlint hook is skipped in this commit only because the local
node_modules currently holds linux bindings (docker bind mount); the
exact hook command was run clean inside the container instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@michael-s-molina

Copy link
Copy Markdown
Member

@rusackas Is this the implementation of Dashboard extensions proposal? If not, can we augment that file with the use case and plan a feature branch as we did for everything else?

…ault provider

Follow the SQL Lab pattern one step further: instead of the host
hardcoding the built-in renderer as a fallback branch, the built-in
renderer is now registered through the same contribution point as the
default-tier provider (superset.dashboard-renderer).

- DashboardRendererProviders gains a default tier: setDefaultProvider
  (host-internal, idempotent by id), getDefaultProvider,
  getOverrideProvider; getProvider() resolves override ?? default. The
  default is never displaced by extension registrations, and disposing
  an override falls back to it through the registry.
- The default registers via a lazy side-effect module
  (src/core/dashboards/defaultRenderer.ts) imported by both the host
  component and the namespace impl, so it is set wherever dashboards
  render (app + embedded) without pulling the dashboard stack into the
  startup bundle. Registration is independent of ExtensionsStartup and
  ENABLE_EXTENSIONS: dashboards always render with the flag off.
- DashboardRendererHost renders the resolved provider: extension
  override (ErrorBoundary-wrapped, view mode + flag on only) or the
  lazy default under a local Suspense.
- Public contract adds getDefaultDashboardRenderer() so extensions can
  wrap/augment the built-in renderer rather than fully replace it.
- Tests updated/extended for the default tier (registry fallback
  semantics, idempotency, reset behavior, namespace API); the E2E spec
  now asserts live that the built-in is the registered default.

The oxlint hook is skipped in this commit only because the local
node_modules currently holds linux bindings (docker bind mount); the
exact hook command was run clean inside the container instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rusackas

rusackas commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

Pushed a follow-up commit that takes the extension framing further: the built-in renderer is now registered through the contribution point itself as the default-tier provider (superset.dashboard-renderer), rather than being a hardcoded fallback branch in the host. Extensions override it, disposal falls back to it through the registry, and getDefaultDashboardRenderer() lets an extension wrap/augment the built-in rather than fully replace it. Registration is independent of ENABLE_EXTENSIONS, so dashboards always render with the flag off... same guarantee SQL Lab's built-in editor has, just expressed through the registry instead of around it.

@rusackas

rusackas commented Jul 4, 2026

Copy link
Copy Markdown
Member Author

@michael-s-molina yep, can/will do... this was just an experiment to see how hard it is to extract the current dashboard renderer as a component... I figure this will allow us to USE that dashboard renderer in different/new embedded contexts (outside of Superset) as well as set us up to have new dashboard layouts. I'll build it into the SIP/docs/proposals properly, and run through it with @EnxDev to make sure it's not departing from his plans in any major way.

@rusackas

rusackas commented Jul 5, 2026

Copy link
Copy Markdown
Member Author

Opened a SIP to ratify the default-tier provider pattern this PR introduces (and to migrate editors/chat to it): #41779

Bring the dashboards extension-point doc in line with the established
editors/chat doc conventions: add a worked Example Implementation (a
flat chart-list renderer plus its registration index.tsx, with a note
on interpreting the full layout tree via meta.chartId), a Dashboards
API Reference table covering the full namespace surface, and a Next
Steps footer. Also update the contribution-types blurb to reflect that
the built-in renderer is registered through the contribution point as
the default provider.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rusackas

rusackas commented Jul 5, 2026

Copy link
Copy Markdown
Member Author

Marking this with hold:sip! — the default-tier built-in provider pattern this PR introduces is now formally proposed in #41779, so this should wait for that SIP to pass its vote before merging. Extension-point docs are also now complete (example implementation, API reference) as of ac50d83.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:frontend Requires changing the frontend dashboard Namespace | Anything related to the Dashboard dependencies:npm doc Namespace | Anything related to documentation hold:sip! packages size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants