feat(extensions): add dashboard renderer contribution point#41703
feat(extensions): add dashboard renderer contribution point#41703rusackas wants to merge 4 commits into
Conversation
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>
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Codecov Report❌ Patch coverage is
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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
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
- Wrong type from legacy filter API · Line 71-71
-
superset-frontend/src/core/dashboards/index.ts - 1
- Orphan export from missing API · Line 42-47
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
- 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>
|
@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>
|
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 ( |
|
@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. |
|
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>
SUMMARY
This is the first step toward making the entire dashboard renderer a replaceable Extension: a new single-slot
dashboardscontribution 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 calldashboards.registerDashboardRenderer(descriptor, component)and receive a Redux-freeDashboardRendererProps— 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-styleuiConfigflags. Host services remain available via thewindow.supersetnamespaces, and theming works via context since renderers mount inside the host's theme providers.The slot semantics mirror the existing
chat/editorsprecedents:Disposablebecomes a no-op.useSyncExternalStore(extensions load asynchronously after startup).ErrorBoundary; the built-in renderer is the fallback when nothing is registered or theENABLE_EXTENSIONSflag is off.The seam:
DashboardPagenow builds the contract props from data it already fetches and renders aDashboardRendererHost. The current Redux-bound stack (DashboardContainer→DashboardBuilder, plus the active-filter selectors) moves behind the same contract asDefaultDashboardRenderer, preserving the lazyDashboardBuilderchunk. Hydration and all other host behavior are unchanged — which means the embedded page (which reusesDashboardPage) inherits the seam for free.Where this is going (follow-ups, in rough order):
onDataMaskChange/onActiveTabsChangecallbacks so custom-renderer filter state persists to permalinks.DefaultDashboardRendererinternals to consume the contract props instead of Redux, behind the now-stable interface.dashboardsevents/API namespace (à lasqlLab) for richer host interop.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
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 theDashboardPageintegration (contract props delivered, hydration still dispatched).ENABLE_EXTENSIONSon, register a renderer from the devtools console:ADDITIONAL INFORMATION
ENABLE_EXTENSIONS🤖 Generated with Claude Code