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>
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>