feat(standalone): granular Explore standalone modes and iframe-safe dashboard fullscreen - #44165
Conversation
…in standalone dashboards - Add ExploreStandaloneMode enum: 1=chart-only (backward compat), 2=hide nav but keep editor controls - Hide "Enter fullscreen" menu item when dashboard is already in standalone mode - Update ExploreViewContainer and ExploreChartPanel to use numeric standalone instead of boolean - Add backend ChartStandaloneMode.HIDE_NAV_SHOW_CONTROLS enum value - Add tests for both standalone modes - Fix PropTypes: standalone bool → number
…escapable The granular standalone work in the previous commit switched the Explore frontend to a numeric comparison, but three things it depends on still assumed the old boolean, single-mode world. Explore ignored the mode. The bootstrap payload carries `is_standalone_mode()`, a boolean, so `standalone === HideNav` was never true and `standalone=1` rendered the full editor instead of a bare chart. Mode 2 only appeared to work because the full editor is also its intended output. The mode is now read from the URL with `getUrlParam`, which already maps '1'/'true' to 1 and '2' to 2, so screenshot URLs (`ChartStandaloneMode.HIDE_NAV` sends `standalone=true`) keep working. The redux and bootstrap types go back to `boolean` to match what the backend actually sends. Mode 2 collapsed to mode 1 on any interaction. `mountExploreUrl` wrote `standalone=1` unconditionally, and Explore calls it on `history.replace` after interactions, so the editor turned into a bare chart on the first click. Callers now pass the active mode through `extraSearch` and the util only supplies HideNav when nothing was given. Fullscreen became a one-way door. The dashboard header hid the toggle whenever `standalone` was set, but that param is also what "Enter fullscreen" sets, so the menu item removed its own escape hatch. The guard is now a real iframe check via `isEmbedded()`, which is what the embed protection was reaching for -- the existing `isEmbedded` local is `!dashboardInfo.userId` and does not cover an authenticated user in an iframe. Top-level users keep both directions; iframes get no toggle at all. No Python changes: `is_standalone_mode()` stays boolean and is untouched. Tests: URL-driven Explore cases for modes 1, 2 and 'true'; an iframe case for the dashboard menu; mode-preservation tests for `mountExploreUrl`; and the subdirectory-prefix "Exit fullscreen" test is restored now that the exit path is reachable again.
✅ 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 #44165 +/- ##
==========================================
+ Coverage 79.21% 79.74% +0.52%
==========================================
Files 2879 2899 +20
Lines 166040 170334 +4294
Branches 38405 39149 +744
==========================================
+ Hits 131536 135835 +4299
+ Misses 32025 31902 -123
- Partials 2479 2597 +118
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 #97acfd
Actionable Suggestions - 1
-
superset-frontend/src/explore/components/ExploreChartPanel/index.tsx - 1
- Test regression from strict equality · Line 507-507
Additional Suggestions - 6
-
superset-frontend/src/explore/components/ExploreViewContainer/index.tsx - 1
-
Redundant standalone spread · Line 252-252This spread is dead: `...additionalParam` is spread after it, and `additionalParam` (from `history.location.search`) always contains `standalone` whenever `standalone` is truthy, since `props.standalone` is derived from the same URL via `getUrlParam`. The numeric value is always overridden by the URL string, so mode 2 is already carried by `additionalParam`. Remove the line or move it after `...additionalParam` if the numeric mode must win.
-
-
superset/utils/webdriver.py - 1
-
Dead enum member · Line 141-141`HIDE_NAV_SHOW_CONTROLS = 2` is added to `ChartStandaloneMode` but never referenced in any Python file. The only backend consumer, `superset/utils/screenshots.py:490`, uses `ChartStandaloneMode.HIDE_NAV.value`, and `ReservedUrlParameters.is_standalone_mode()` treats standalone as a boolean. The frontend `ExploreStandaloneMode.HideNavShowControls` is used, but this backend member is dead code. Remove it or consume it.
-
-
superset-frontend/src/explore/constants.ts - 1
-
Unused enum member · Line 229-229`HideNavShowControls` (=2) is never referenced anywhere in the codebase; consumers only special-case `ExploreStandaloneMode.HideNav` (=1), so mode 2 relies on implicit fall-through to the full editor. Either drop the member or branch on it explicitly in `ExploreViewContainer`/`ExploreChartPanel` so mode 2's behavior is intentional.
-
-
superset-frontend/src/explore/components/ExploreViewContainer/ExploreViewContainer.test.tsx - 2
-
Magic number in mock · Line 135-135The mock hardcodes `standalone === 1`, but production (`ExploreChartPanel/index.tsx:507`) compares against `ExploreStandaloneMode.HideNav`. If the enum value ever changes, this test silently diverges from real behavior. Import `ExploreStandaloneMode` from 'src/explore/constants' and compare `standalone === ExploreStandaloneMode.HideNav` instead of the literal `1`.
-
Wrong enum in comment · Line 299-299The comment references `ChartStandaloneMode.HIDE_NAV`, but no such enum exists in the repo — the actual enum is `ExploreStandaloneMode.HideNav` (constants.ts:226). This misleads readers about the mode's source. Update the comment to the real symbol.
-
-
superset-frontend/src/dashboard/components/Header/Header.test.tsx - 1
-
Test mock inconsistency · Line 874-880This test simulates "not in standalone mode" via `window.history.pushState('/dashboard')`, but the `useLocation` mock (line 51) still hardcodes `search: '?standalone=1'`. The label logic in `useHeaderActionsDropdownMenu` reads `getUrlParam(URL_PARAMS.standalone)` (i.e. `window.location.search`), so the test passes today — but the mock contradicts the scenario, and a refactor to `location.search` would silently break it.
-
Review Details
-
Files reviewed - 10 · Commit Range:
d2a1dcd..a9416cf- superset-frontend/src/dashboard/components/Header/Header.test.tsx
- superset-frontend/src/dashboard/components/Header/useHeaderActionsDropdownMenu.tsx
- superset-frontend/src/explore/components/ExploreChartPanel/index.tsx
- superset-frontend/src/explore/components/ExploreViewContainer/ExploreViewContainer.test.tsx
- superset-frontend/src/explore/components/ExploreViewContainer/index.tsx
- superset-frontend/src/explore/constants.ts
- superset-frontend/src/explore/exploreUtils/exploreUtils.test.tsx
- superset-frontend/src/explore/exploreUtils/index.ts
- superset-frontend/src/explore/types.ts
- superset/utils/webdriver.py
-
Files skipped - 0
-
Tools
- MyPy (Static Code Analysis) - ✔︎ Successful
- Astral Ruff (Static Code Analysis) - ✔︎ Successful
- 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 an incremental AI Review. -
/review full- 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
| ); | ||
|
|
||
| if (standalone) { | ||
| if (standalone === ExploreStandaloneMode.HideNav) { |
There was a problem hiding this comment.
standalone is now compared with strict equality (=== ExploreStandaloneMode.HideNav, i.e. === 1). The existing ExploreChartPanel.test.tsx passes standalone: true (boolean), and true === 1 is false, so the standalone branch no longer renders StandaloneDownloadControl and the test at line 232 will fail. Update those tests to pass the numeric mode.
Code Review Run #97acfd
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
Description:
SUMMARY
Adds a second Explore standalone mode and makes the dashboard fullscreen
toggle safe to use inside an iframe.
standaloneon Explore was previously all-or-nothing: any truthy valuerendered a bare chart. Embedding an editable chart in an iframe therefore
meant choosing between "no chart controls at all" and "the full Superset
nav". This adds
ExploreStandaloneMode:1— chart only (unchanged; what thumbnails and screenshots use)2— hide the nav, keep the editor controlsBecause the bootstrap payload only carries
is_standalone_mode(), aboolean, the mode is resolved from the URL via
getUrlParam, which alreadymaps
'1'/'true'to1and'2'to2. Screenshot URLs(
ChartStandaloneMode.HIDE_NAVsendsstandalone=true) keep working.mountExploreUrlnow carries the active mode throughextraSearchinsteadof writing
standalone=1unconditionally, which would otherwise downgrademode 2 to mode 1 on the first
history.replace.On dashboards, the header's fullscreen toggle is hidden when the page is
actually inside an iframe (
isEmbedded()fromdashboard/util/isEmbedded).Exiting fullscreen reloads without the
standaloneparam and restores thefull Superset nav, which breaks an embed. Note this is distinct from the
isEmbeddedlocal in the menu hook, which is!dashboardInfo.userIdandonly covers anonymous guests via the embedded SDK — not an authenticated
user whose dashboard is iframed. Top-level users keep both directions, so
fullscreen is not a one-way door.
No Python behaviour changes:
is_standalone_mode()is untouched and stillreturns a boolean. The only backend edit is one added enum value on
ChartStandaloneMode.BEFORE/AFTER SCREENSHOTS
Screenshot 1:

Explore with
standalone=2(new mode): Superset nav hidden, chart editor controls kept. On master any truthystandalonerenders a bare chart, so this layout was not previously possible.Screenshot 2:

Dashboard inside an iframe with
standalone=1: the ⋮ menu offers no fullscreen toggle. Exiting would reload withoutstandaloneand restore the full Superset nav, breaking the embed. Outside an iframe the menu is unchanged.TESTING INSTRUCTIONS
Explore:
/explore/?slice_id=<id>— full editor with nav/explore/?slice_id=<id>&standalone=1— chart only/explore/?slice_id=<id>&standalone=2— no nav, editor controls kept/explore/?slice_id=<id>&standalone=true— chart only (screenshot path)bare chart
Dashboard:
/superset/dashboard/<id>/— "Enter fullscreen" present in the ⋮ menustandalone=1and "Exit fullscreen" is availableADDITIONAL INFORMATION