Remove dashboardSceneSolo and dashboardSceneForViewers feature flags (#117083)

* Remove dashboardSceneSolo and dashboardSceneForViewers feature flags

* e2e updates

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Dominik Prokop 2026-02-03 15:05:23 +01:00 committed by GitHub
parent 2074778af5
commit 58bf5a1ba2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 133 additions and 380 deletions

View file

@ -35,8 +35,7 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `awsAsyncQueryCaching` | Enable caching for async queries for Redshift and Athena. Requires that the datasource has caching and async query support enabled | Yes |
| `dashgpt` | Enable AI powered features in dashboards | Yes |
| `kubernetesDashboards` | Use the kubernetes API in the frontend for dashboards | Yes |
| `dashboardSceneForViewers` | Enables dashboard rendering using Scenes for viewer roles | Yes |
| `dashboardSceneSolo` | Enables rendering dashboards using scenes for solo panels | Yes |
| `annotationPermissionUpdate` | Change the way annotation permissions work by scoping them to folders and dashboards. | Yes |
| `dashboardScene` | Enables dashboard rendering using scenes for all roles | Yes |
| `alertingQueryOptimization` | Optimizes eligible queries in order to reduce load on datasources | |
| `cloudWatchNewLabelParsing` | Updates CloudWatch label parsing to be more accurate | Yes |

View file

@ -280,7 +280,7 @@ You can also update these parameters in the [image rendering configuration](http
The following example shows a link to a server-side rendered PNG:
```bash
https://play.grafana.org/render/d-solo/ktMs4D6Mk?from=2024-09-03T11:55:44.442Z&to=2024-09-03T17:55:44.442Z&panelId=panel-13&__feature.dashboardSceneSolo&width=1000&height=500&tz=UTC
https://play.grafana.org/render/d-solo/ktMs4D6Mk?from=2024-09-03T11:55:44.442Z&to=2024-09-03T17:55:44.442Z&panelId=panel-13&__feature.dashboardScene&width=1000&height=500&tz=UTC
```
### Share an embed

View file

@ -19,7 +19,7 @@ test.describe(
test('Can view solo panel in scenes', async ({ page, selectors }) => {
// open Panel Tests - Graph NG
const soloPanelUrl = selectors.pages.SoloPanel.url(
'TkZXxlNG3/panel-tests-graph-ng?orgId=1&from=1699954597665&to=1699956397665&panelId=54&__feature.dashboardSceneSolo=true'
'TkZXxlNG3/panel-tests-graph-ng?orgId=1&from=1699954597665&to=1699956397665&panelId=54&__feature.dashboardScene=true'
);
await page.goto(soloPanelUrl);
@ -35,7 +35,7 @@ test.describe(
test('Can view solo repeated panel in scenes', async ({ page, selectors }) => {
// open Panel Tests - Graph NG
const soloPanelUrl = selectors.pages.SoloPanel.url(
'templating-repeating-panels/templating-repeating-panels?orgId=1&from=1699934989607&to=1699956589607&panelId=A$panel-2&__feature.dashboardSceneSolo=true'
'templating-repeating-panels/templating-repeating-panels?orgId=1&from=1699934989607&to=1699956589607&panelId=A$panel-2&__feature.dashboardScene=true'
);
await page.goto(soloPanelUrl);
@ -51,7 +51,7 @@ test.describe(
test('Can view solo in repeated row and panel in scenes', async ({ page, selectors }) => {
// open Panel Tests - Graph NG
const soloPanelUrl = selectors.pages.SoloPanel.url(
'Repeating-rows-uid/repeating-rows?orgId=1&var-server=A&var-server=B&var-server=D&var-pod=1&var-pod=2&var-pod=3&panelId=B$2$panel-2&__feature.dashboardSceneSolo=true'
'Repeating-rows-uid/repeating-rows?orgId=1&var-server=A&var-server=B&var-server=D&var-pod=1&var-pod=2&var-pod=3&panelId=B$2$panel-2&__feature.dashboardScene=true'
);
await page.goto(soloPanelUrl);

View file

@ -11,21 +11,28 @@ describe('Solo Route', () => {
cy.get('canvas').should('have.length', 6);
});
});
// Scenes solo panel tests - these require dashboardScene=true and a fresh app load
describe('Solo Route (Scenes)', () => {
beforeEach(() => {
// Override the old-arch default and enable scenes
cy.setLocalStorage('grafana.featureToggles', 'dashboardScene=true');
// Reload to pick up the new toggle before routes are initialized
cy.reload();
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'), false);
});
it('Can view solo panel in scenes', () => {
// open Panel Tests - Graph NG
e2e.pages.SoloPanel.visit(
'TkZXxlNG3/panel-tests-graph-ng?orgId=1&from=1699954597665&to=1699956397665&panelId=54&__feature.dashboardSceneSolo=true'
);
e2e.pages.SoloPanel.visit('TkZXxlNG3/panel-tests-graph-ng?orgId=1&from=1699954597665&to=1699956397665&panelId=54');
e2e.components.Panels.Panel.title('Interpolation: Step before').should('exist');
cy.contains('uplot-main-div').should('not.exist');
});
it('Can view solo repeated panel in scenes', () => {
// open Panel Tests - Graph NG
e2e.pages.SoloPanel.visit(
'templating-repeating-panels/templating-repeating-panels?orgId=1&from=1699934989607&to=1699956589607&panelId=A$panel-2&__feature.dashboardSceneSolo=true'
'templating-repeating-panels/templating-repeating-panels?orgId=1&from=1699934989607&to=1699956589607&panelId=A$panel-2'
);
e2e.components.Panels.Panel.title('server=A').should('exist');
@ -33,9 +40,8 @@ describe('Solo Route', () => {
});
it('Can view solo in repeated row and panel in scenes', () => {
// open Panel Tests - Graph NG
e2e.pages.SoloPanel.visit(
'Repeating-rows-uid/repeating-rows?orgId=1&var-server=A&var-server=B&var-server=D&var-pod=1&var-pod=2&var-pod=3&panelId=B$2$panel-2&__feature.dashboardSceneSolo=true'
'Repeating-rows-uid/repeating-rows?orgId=1&var-server=A&var-server=B&var-server=D&var-pod=1&var-pod=2&var-pod=3&panelId=B$2$panel-2'
);
e2e.components.Panels.Panel.title('server = B, pod = Rob').should('exist');

View file

@ -2174,11 +2174,6 @@
"count": 1
}
},
"public/app/features/dashboard/containers/DashboardPageProxy.tsx": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1
}
},
"public/app/features/dashboard/containers/PublicDashboardPage.tsx": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1

View file

@ -364,15 +364,10 @@ export interface FeatureToggles {
*/
alertmanagerRemotePrimary?: boolean;
/**
* Enables dashboard rendering using Scenes for viewer roles
* Change the way annotation permissions work by scoping them to folders and dashboards.
* @default true
*/
dashboardSceneForViewers?: boolean;
/**
* Enables rendering dashboards using scenes for solo panels
* @default true
*/
dashboardSceneSolo?: boolean;
annotationPermissionUpdate?: boolean;
/**
* Enables dashboard rendering using scenes for all roles
* @default true

View file

@ -324,7 +324,7 @@ function overrideFeatureTogglesFromUrl(config: GrafanaBootConfig) {
// Although most flags can not be changed from the URL in production,
// some of them are safe (and useful!) to change dynamically from the browser URL
const safeRuntimeFeatureFlags = new Set(['queryServiceFromUI', 'dashboardSceneSolo']);
const safeRuntimeFeatureFlags = new Set(['queryServiceFromUI']);
const params = new URLSearchParams(window.location.search);
params.forEach((value, key) => {

View file

@ -553,20 +553,12 @@ var (
Expression: "false",
},
{
Name: "dashboardSceneForViewers",
Description: "Enables dashboard rendering using Scenes for viewer roles",
Stage: FeatureStageGeneralAvailability,
FrontendOnly: true,
Owner: grafanaDashboardsSquad,
Expression: "true", // enabled by default
},
{
Name: "dashboardSceneSolo",
Description: "Enables rendering dashboards using scenes for solo panels",
Stage: FeatureStageGeneralAvailability,
FrontendOnly: true,
Owner: grafanaDashboardsSquad,
Expression: "true", // enabled by default
Name: "annotationPermissionUpdate",
Description: "Change the way annotation permissions work by scoping them to folders and dashboards.",
Stage: FeatureStageGeneralAvailability,
RequiresDevMode: false,
Expression: "true", // enabled by default
Owner: identityAccessTeam,
},
{
Name: "dashboardScene",

View file

@ -23,7 +23,7 @@ Created,Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
2023-04-12,disableSSEDataplane,experimental,@grafana/grafana-datasources-core-services,false,false,false
2023-04-03,renderAuthJWT,preview,@grafana/grafana-operator-experience-squad,false,false,false
2023-06-06,refactorVariablesTimeRange,preview,@grafana/dashboards-squad,false,false,false
2023-05-04,faroDatasourceSelector,preview,@grafana/app-o11y,false,false,true
2023-05-05,faroDatasourceSelector,preview,@grafana/app-o11y,false,false,true
2023-04-24,enableDatagridEditing,preview,@grafana/dataviz-squad,false,false,false
2023-07-12,logsExploreTableVisualisation,GA,@grafana/observability-logs,false,false,true
2023-07-06,awsDatasourcesTempCredentials,GA,@grafana/aws-datasources,false,false,false
@ -43,11 +43,11 @@ Created,Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
2023-09-19,lokiRunQueriesInParallel,privatePreview,@grafana/oss-big-tent,false,false,false
2023-09-28,externalServiceAccounts,preview,@grafana/identity-access-team,false,false,false
2023-12-05,kubernetesSnapshots,experimental,@grafana/grafana-app-platform-squad,false,true,false
2025-06-25,kubernetesLibraryPanels,experimental,@grafana/grafana-app-platform-squad,false,true,false
2025-06-26,kubernetesLibraryPanels,experimental,@grafana/grafana-app-platform-squad,false,true,false
2024-06-05,kubernetesDashboards,GA,@grafana/dashboards-squad,false,false,false
2025-07-31,kubernetesShortURLs,experimental,@grafana/grafana-app-platform-squad,false,true,false
2025-08-01,kubernetesShortURLs,experimental,@grafana/grafana-app-platform-squad,false,true,false
2025-08-29,useKubernetesShortURLsAPI,experimental,@grafana/sharing-squad,false,false,true
2025-07-31,kubernetesAlertingRules,experimental,@grafana/alerting-squad,false,true,false
2025-08-01,kubernetesAlertingRules,experimental,@grafana/alerting-squad,false,true,false
2025-08-29,kubernetesCorrelations,experimental,@grafana/datapro,false,true,false
2025-12-09,kubernetesUnifiedStorageQuotas,experimental,@grafana/search-and-storage,false,true,false
2025-10-16,kubernetesLogsDrilldown,experimental,@grafana/observability-logs,false,true,false
@ -68,8 +68,7 @@ Created,Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
2025-11-13,alertingUIUseBackendFilters,experimental,@grafana/alerting-squad,false,false,false
2025-11-24,alertingUIUseFullyCompatBackendFilters,experimental,@grafana/alerting-squad,false,false,false
2023-10-30,alertmanagerRemotePrimary,experimental,@grafana/alerting-squad,false,false,false
2023-11-02,dashboardSceneForViewers,GA,@grafana/dashboards-squad,false,false,true
2024-02-11,dashboardSceneSolo,GA,@grafana/dashboards-squad,false,false,true
2023-10-31,annotationPermissionUpdate,GA,@grafana/identity-access-team,false,false,false
2023-11-13,dashboardScene,GA,@grafana/dashboards-squad,false,false,true
2024-10-23,dashboardNewLayouts,preview,@grafana/dashboards-squad,false,false,false
2025-08-29,dashboardUndoRedo,experimental,@grafana/dashboards-squad,false,false,true
@ -88,10 +87,10 @@ Created,Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
2024-01-18,jitterAlertRulesWithinGroups,preview,@grafana/alerting-squad,false,true,false
2025-12-29,auditLoggingAppPlatform,experimental,@grafana/grafana-operator-experience-squad,false,true,false
2025-03-19,secretsManagementAppPlatform,experimental,@grafana/grafana-operator-experience-squad,false,false,false
2025-07-31,secretsManagementAppPlatformUI,experimental,@grafana/grafana-operator-experience-squad,false,false,false
2025-08-01,secretsManagementAppPlatformUI,experimental,@grafana/grafana-operator-experience-squad,false,false,false
2024-01-23,alertingSaveStatePeriodic,privatePreview,@grafana/alerting-squad,false,false,false
2025-01-27,alertingSaveStateCompressed,preview,@grafana/alerting-squad,false,false,false
2024-11-26,scopeApi,experimental,@grafana/grafana-app-platform-squad,false,false,false
2024-11-27,scopeApi,experimental,@grafana/grafana-app-platform-squad,false,false,false
2025-07-31,useScopeSingleNodeEndpoint,experimental,@grafana/grafana-operator-experience-squad,false,false,true
2025-08-29,useMultipleScopeNodesEndpoint,experimental,@grafana/grafana-operator-experience-squad,false,false,true
2024-11-11,logQLScope,privatePreview,@grafana/oss-big-tent,false,false,false
@ -132,13 +131,13 @@ Created,Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
2024-09-11,alertingFilterV2,experimental,@grafana/alerting-squad,false,false,false
2024-08-09,dataplaneAggregator,experimental,@grafana/grafana-app-platform-squad,false,true,false
2024-08-30,newFiltersUI,GA,@grafana/dashboards-squad,false,false,false
2025-07-31,vizActionsAuth,preview,@grafana/dataviz-squad,false,false,true
2025-08-01,vizActionsAuth,preview,@grafana/dataviz-squad,false,false,true
2024-09-27,alertingPrometheusRulesPrimary,experimental,@grafana/alerting-squad,false,false,true
2024-08-29,exploreLogsShardSplitting,experimental,@grafana/observability-logs,false,false,true
2024-08-29,exploreLogsAggregatedMetrics,experimental,@grafana/observability-logs,false,false,true
2024-10-14,appPlatformGrpcClientAuth,experimental,@grafana/identity-access-team,false,false,false
2024-09-09,groupAttributeSync,privatePreview,@grafana/identity-access-team,false,false,false
2024-09-25,alertingQueryAndExpressionsStepMode,GA,@grafana/alerting-squad,false,false,true
2024-09-26,alertingQueryAndExpressionsStepMode,GA,@grafana/alerting-squad,false,false,true
2024-09-17,improvedExternalSessionHandling,GA,@grafana/identity-access-team,false,false,false
2024-09-23,useSessionStorageForRedirection,GA,@grafana/identity-access-team,false,false,false
2024-09-26,rolePickerDrawer,experimental,@grafana/identity-access-team,false,false,false
@ -146,10 +145,10 @@ Created,Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
2024-10-04,pluginsSriChecks,GA,@grafana/plugins-platform-backend,false,false,false
2024-10-17,unifiedStorageBigObjectsSupport,experimental,@grafana/search-and-storage,false,false,false
2024-10-22,timeRangeProvider,experimental,@grafana/grafana-frontend-platform,false,false,false
2025-11-04,timeRangePan,GA,@grafana/dataviz-squad,false,false,true
2025-11-05,timeRangePan,GA,@grafana/dataviz-squad,false,false,true
2025-11-20,newTimeRangeZoomShortcuts,GA,@grafana/dataviz-squad,false,false,true
2024-10-24,azureMonitorDisableLogLimit,GA,@grafana/partner-datasources,false,false,false
2024-12-19,playlistsReconciler,experimental,@grafana/grafana-app-platform-squad,false,true,false
2024-12-20,playlistsReconciler,experimental,@grafana/grafana-app-platform-squad,false,true,false
2024-11-14,passwordlessMagicLinkAuthentication,experimental,@grafana/identity-access-team,false,false,false
2024-12-18,prometheusSpecialCharsInLabelValues,experimental,@grafana/oss-big-tent,false,false,true
2024-11-05,enableExtensionsAdminPage,experimental,@grafana/plugins-platform-backend,false,true,false
@ -161,7 +160,7 @@ Created,Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
2025-07-23,alertingAIFeedback,experimental,@grafana/alerting-squad,false,false,false
2025-07-16,alertingAIImproveAlertRules,experimental,@grafana/alerting-squad,false,false,false
2025-07-16,alertingAIGenTemplates,experimental,@grafana/alerting-squad,false,false,false
2025-07-31,alertingEnrichmentPerRule,experimental,@grafana/alerting-squad,false,false,false
2025-08-01,alertingEnrichmentPerRule,experimental,@grafana/alerting-squad,false,false,false
2025-08-29,alertingEnrichmentAssistantInvestigations,experimental,@grafana/alerting-squad,false,false,false
2025-07-16,alertingAIAnalyzeCentralStateHistory,experimental,@grafana/alerting-squad,false,false,false
2024-11-22,alertingNotificationsStepMode,GA,@grafana/alerting-squad,false,false,true
@ -199,7 +198,7 @@ Created,Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
2025-04-09,unifiedNavbars,GA,@grafana/plugins-platform-backend,false,false,true
2025-04-07,logsPanelControls,preview,@grafana/observability-logs,false,false,true
2025-04-09,metricsFromProfiles,experimental,@grafana/observability-traces-and-profiling,false,false,true
2025-07-31,grafanaAssistantInProfilesDrilldown,GA,@grafana/observability-traces-and-profiling,false,false,true
2025-08-01,grafanaAssistantInProfilesDrilldown,GA,@grafana/observability-traces-and-profiling,false,false,true
2025-07-15,tempoAlerting,experimental,@grafana/observability-traces-and-profiling,false,false,false
2025-04-16,pluginsAutoUpdate,experimental,@grafana/plugins-platform-backend,false,false,false
2025-04-22,alertingListViewV2PreviewToggle,privatePreview,@grafana/alerting-squad,false,false,true
@ -207,12 +206,12 @@ Created,Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
2025-04-24,alertingBulkActionsInUI,GA,@grafana/alerting-squad,false,false,true
2025-06-18,kubernetesAuthzApis,deprecated,@grafana/identity-access-team,false,false,false
2025-08-29,kubernetesAuthZHandlerRedirect,experimental,@grafana/identity-access-team,false,false,false
2025-07-31,kubernetesAuthzResourcePermissionApis,experimental,@grafana/identity-access-team,false,false,false
2025-08-01,kubernetesAuthzResourcePermissionApis,experimental,@grafana/identity-access-team,false,false,false
2025-10-13,kubernetesAuthzZanzanaSync,experimental,@grafana/identity-access-team,false,false,false
2026-01-12,kubernetesAuthzCoreRolesApi,experimental,@grafana/identity-access-team,false,false,false
2026-01-15,kubernetesAuthzGlobalRolesApi,experimental,@grafana/identity-access-team,false,false,false
2026-01-12,kubernetesAuthzRolesApi,experimental,@grafana/identity-access-team,false,false,false
2026-01-14,kubernetesAuthzTeamLBACRuleApi,experimental,@grafana/identity-access-team,false,false,false
2026-01-15,kubernetesAuthzTeamLBACRuleApi,experimental,@grafana/identity-access-team,false,false,false
2026-01-12,kubernetesAuthzRoleBindingsApi,experimental,@grafana/identity-access-team,false,false,false
2025-07-25,kubernetesAuthnMutation,experimental,@grafana/identity-access-team,false,false,false
2025-11-25,kubernetesExternalGroupMapping,experimental,@grafana/identity-access-team,false,false,false
@ -220,16 +219,16 @@ Created,Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
2025-12-10,recentlyViewedDashboards,experimental,@grafana/grafana-search-navigate-organise,false,false,true
2026-01-13,experimentRecentlyViewedDashboards,experimental,@grafana/grafana-search-navigate-organise,false,false,true
2025-06-06,alertEnrichment,experimental,@grafana/alerting-squad,false,false,false
2025-07-31,alertEnrichmentMultiStep,experimental,@grafana/alerting-squad,false,false,false
2025-07-31,alertEnrichmentConditional,experimental,@grafana/alerting-squad,false,false,false
2025-08-01,alertEnrichmentMultiStep,experimental,@grafana/alerting-squad,false,false,false
2025-08-01,alertEnrichmentConditional,experimental,@grafana/alerting-squad,false,false,false
2025-06-10,alertingImportAlertmanagerAPI,experimental,@grafana/alerting-squad,false,false,false
2025-07-31,alertingImportAlertmanagerUI,experimental,@grafana/alerting-squad,false,false,false
2025-08-01,alertingImportAlertmanagerUI,experimental,@grafana/alerting-squad,false,false,false
2026-01-26,alertingDisableDMAinUI,experimental,@grafana/alerting-squad,false,false,false
2025-07-15,sharingDashboardImage,GA,@grafana/sharing-squad,false,false,true
2025-06-17,preferLibraryPanelTitle,privatePreview,@grafana/dashboards-squad,false,false,false
2025-06-24,tabularNumbers,GA,@grafana/grafana-frontend-platform,false,false,false
2025-06-25,newInfluxDSConfigPageDesign,privatePreview,@grafana/partner-datasources,false,false,false
2025-06-29,enableAppChromeExtensions,experimental,@grafana/plugins-platform-backend,false,false,true
2025-06-30,enableAppChromeExtensions,experimental,@grafana/plugins-platform-backend,false,false,true
2025-10-13,enableDashboardEmptyExtensions,experimental,@grafana/dashboards-squad,false,false,true
2025-07-03,foldersAppPlatformAPI,experimental,@grafana/grafana-search-navigate-organise,false,false,true
2025-07-16,otelLogsFormatting,experimental,@grafana/observability-logs,false,false,true
@ -237,23 +236,23 @@ Created,Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
2025-07-18,unifiedStorageSearchDualReaderEnabled,experimental,@grafana/search-and-storage,false,false,false
2025-07-31,dashboardLevelTimeMacros,experimental,@grafana/dashboards-squad,false,false,true
2025-07-25,alertmanagerRemoteSecondaryWithRemoteState,experimental,@grafana/alerting-squad,false,false,false
2025-07-31,restrictedPluginApis,experimental,@grafana/plugins-platform-backend,false,false,true
2025-07-31,favoriteDatasources,experimental,@grafana/plugins-platform-backend,false,false,true
2025-07-31,newLogContext,experimental,@grafana/observability-logs,false,false,true
2025-07-31,newClickhouseConfigPageDesign,privatePreview,@grafana/partner-datasources,false,false,false
2025-07-31,teamFolders,experimental,@grafana/grafana-search-navigate-organise,false,false,false
2025-08-01,restrictedPluginApis,experimental,@grafana/plugins-platform-backend,false,false,true
2025-08-01,favoriteDatasources,experimental,@grafana/plugins-platform-backend,false,false,true
2025-08-01,newLogContext,experimental,@grafana/observability-logs,false,false,true
2025-08-01,newClickhouseConfigPageDesign,privatePreview,@grafana/partner-datasources,false,false,false
2025-08-01,teamFolders,experimental,@grafana/grafana-search-navigate-organise,false,false,false
2025-10-22,interactiveLearning,preview,@grafana/pathfinder,false,false,false
2025-07-31,alertingTriage,experimental,@grafana/alerting-squad,false,false,false
2025-07-31,graphiteBackendMode,privatePreview,@grafana/partner-datasources,false,false,false
2025-07-31,azureResourcePickerUpdates,GA,@grafana/partner-datasources,false,false,true
2025-07-31,prometheusTypeMigration,experimental,@grafana/partner-datasources,false,true,false
2025-07-31,pluginContainers,privatePreview,@grafana/plugins-platform-backend,false,true,false
2025-08-01,alertingTriage,experimental,@grafana/alerting-squad,false,false,false
2025-08-01,graphiteBackendMode,privatePreview,@grafana/partner-datasources,false,false,false
2025-08-01,azureResourcePickerUpdates,GA,@grafana/partner-datasources,false,false,true
2025-08-01,prometheusTypeMigration,experimental,@grafana/partner-datasources,false,true,false
2025-08-01,pluginContainers,privatePreview,@grafana/plugins-platform-backend,false,true,false
2025-08-29,cdnPluginsLoadFirst,experimental,@grafana/plugins-platform-backend,false,false,false
2025-08-29,cdnPluginsUrls,experimental,@grafana/plugins-platform-backend,false,false,false
2025-10-24,pluginInstallAPISync,experimental,@grafana/plugins-platform-backend,false,false,false
2025-10-20,newGauge,preview,@grafana/dataviz-squad,false,false,true
2025-11-12,newVizSuggestions,preview,@grafana/dataviz-squad,false,false,true
2025-12-01,externalVizSuggestions,experimental,@grafana/dataviz-squad,false,false,true
2025-12-02,externalVizSuggestions,experimental,@grafana/dataviz-squad,false,false,true
2025-12-18,heatmapRowsAxisOptions,experimental,@grafana/dataviz-squad,false,false,true
2025-10-17,preventPanelChromeOverflow,preview,@grafana/grafana-frontend-platform,false,false,true
2025-10-31,jaegerEnableGrpcEndpoint,experimental,@grafana/oss-big-tent,false,false,false
@ -271,7 +270,7 @@ Created,Name,Stage,Owner,requiresDevMode,RequiresRestart,FrontendOnly
2025-11-21,lokiQueryLimitsContext,experimental,@grafana/observability-logs,false,false,true
2025-11-19,rudderstackUpgrade,experimental,@grafana/grafana-frontend-platform,false,false,true
2025-11-27,kubernetesAlertingHistorian,experimental,@grafana/alerting-squad,false,true,false
2025-12-04,useMTPlugins,experimental,@grafana/plugins-platform-backend,false,false,true
2025-12-05,useMTPlugins,experimental,@grafana/plugins-platform-backend,false,false,true
2025-12-09,multiPropsVariables,experimental,@grafana/dashboards-squad,false,false,true
2026-01-05,smoothingTransformation,experimental,@grafana/datapro,false,false,true
2026-01-06,secretsManagementAppPlatformAwsKeeper,experimental,@grafana/grafana-operator-experience-squad,false,false,false

1 Created Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
23 2023-04-12 disableSSEDataplane experimental @grafana/grafana-datasources-core-services false false false
24 2023-04-03 renderAuthJWT preview @grafana/grafana-operator-experience-squad false false false
25 2023-06-06 refactorVariablesTimeRange preview @grafana/dashboards-squad false false false
26 2023-05-04 2023-05-05 faroDatasourceSelector preview @grafana/app-o11y false false true
27 2023-04-24 enableDatagridEditing preview @grafana/dataviz-squad false false false
28 2023-07-12 logsExploreTableVisualisation GA @grafana/observability-logs false false true
29 2023-07-06 awsDatasourcesTempCredentials GA @grafana/aws-datasources false false false
43 2023-09-19 lokiRunQueriesInParallel privatePreview @grafana/oss-big-tent false false false
44 2023-09-28 externalServiceAccounts preview @grafana/identity-access-team false false false
45 2023-12-05 kubernetesSnapshots experimental @grafana/grafana-app-platform-squad false true false
46 2025-06-25 2025-06-26 kubernetesLibraryPanels experimental @grafana/grafana-app-platform-squad false true false
47 2024-06-05 kubernetesDashboards GA @grafana/dashboards-squad false false false
48 2025-07-31 2025-08-01 kubernetesShortURLs experimental @grafana/grafana-app-platform-squad false true false
49 2025-08-29 useKubernetesShortURLsAPI experimental @grafana/sharing-squad false false true
50 2025-07-31 2025-08-01 kubernetesAlertingRules experimental @grafana/alerting-squad false true false
51 2025-08-29 kubernetesCorrelations experimental @grafana/datapro false true false
52 2025-12-09 kubernetesUnifiedStorageQuotas experimental @grafana/search-and-storage false true false
53 2025-10-16 kubernetesLogsDrilldown experimental @grafana/observability-logs false true false
68 2025-11-13 alertingUIUseBackendFilters experimental @grafana/alerting-squad false false false
69 2025-11-24 alertingUIUseFullyCompatBackendFilters experimental @grafana/alerting-squad false false false
70 2023-10-30 alertmanagerRemotePrimary experimental @grafana/alerting-squad false false false
71 2023-11-02 2023-10-31 dashboardSceneForViewers annotationPermissionUpdate GA @grafana/dashboards-squad @grafana/identity-access-team false false true false
2024-02-11 dashboardSceneSolo GA @grafana/dashboards-squad false false true
72 2023-11-13 dashboardScene GA @grafana/dashboards-squad false false true
73 2024-10-23 dashboardNewLayouts preview @grafana/dashboards-squad false false false
74 2025-08-29 dashboardUndoRedo experimental @grafana/dashboards-squad false false true
87 2024-01-18 jitterAlertRulesWithinGroups preview @grafana/alerting-squad false true false
88 2025-12-29 auditLoggingAppPlatform experimental @grafana/grafana-operator-experience-squad false true false
89 2025-03-19 secretsManagementAppPlatform experimental @grafana/grafana-operator-experience-squad false false false
90 2025-07-31 2025-08-01 secretsManagementAppPlatformUI experimental @grafana/grafana-operator-experience-squad false false false
91 2024-01-23 alertingSaveStatePeriodic privatePreview @grafana/alerting-squad false false false
92 2025-01-27 alertingSaveStateCompressed preview @grafana/alerting-squad false false false
93 2024-11-26 2024-11-27 scopeApi experimental @grafana/grafana-app-platform-squad false false false
94 2025-07-31 useScopeSingleNodeEndpoint experimental @grafana/grafana-operator-experience-squad false false true
95 2025-08-29 useMultipleScopeNodesEndpoint experimental @grafana/grafana-operator-experience-squad false false true
96 2024-11-11 logQLScope privatePreview @grafana/oss-big-tent false false false
131 2024-09-11 alertingFilterV2 experimental @grafana/alerting-squad false false false
132 2024-08-09 dataplaneAggregator experimental @grafana/grafana-app-platform-squad false true false
133 2024-08-30 newFiltersUI GA @grafana/dashboards-squad false false false
134 2025-07-31 2025-08-01 vizActionsAuth preview @grafana/dataviz-squad false false true
135 2024-09-27 alertingPrometheusRulesPrimary experimental @grafana/alerting-squad false false true
136 2024-08-29 exploreLogsShardSplitting experimental @grafana/observability-logs false false true
137 2024-08-29 exploreLogsAggregatedMetrics experimental @grafana/observability-logs false false true
138 2024-10-14 appPlatformGrpcClientAuth experimental @grafana/identity-access-team false false false
139 2024-09-09 groupAttributeSync privatePreview @grafana/identity-access-team false false false
140 2024-09-25 2024-09-26 alertingQueryAndExpressionsStepMode GA @grafana/alerting-squad false false true
141 2024-09-17 improvedExternalSessionHandling GA @grafana/identity-access-team false false false
142 2024-09-23 useSessionStorageForRedirection GA @grafana/identity-access-team false false false
143 2024-09-26 rolePickerDrawer experimental @grafana/identity-access-team false false false
145 2024-10-04 pluginsSriChecks GA @grafana/plugins-platform-backend false false false
146 2024-10-17 unifiedStorageBigObjectsSupport experimental @grafana/search-and-storage false false false
147 2024-10-22 timeRangeProvider experimental @grafana/grafana-frontend-platform false false false
148 2025-11-04 2025-11-05 timeRangePan GA @grafana/dataviz-squad false false true
149 2025-11-20 newTimeRangeZoomShortcuts GA @grafana/dataviz-squad false false true
150 2024-10-24 azureMonitorDisableLogLimit GA @grafana/partner-datasources false false false
151 2024-12-19 2024-12-20 playlistsReconciler experimental @grafana/grafana-app-platform-squad false true false
152 2024-11-14 passwordlessMagicLinkAuthentication experimental @grafana/identity-access-team false false false
153 2024-12-18 prometheusSpecialCharsInLabelValues experimental @grafana/oss-big-tent false false true
154 2024-11-05 enableExtensionsAdminPage experimental @grafana/plugins-platform-backend false true false
160 2025-07-23 alertingAIFeedback experimental @grafana/alerting-squad false false false
161 2025-07-16 alertingAIImproveAlertRules experimental @grafana/alerting-squad false false false
162 2025-07-16 alertingAIGenTemplates experimental @grafana/alerting-squad false false false
163 2025-07-31 2025-08-01 alertingEnrichmentPerRule experimental @grafana/alerting-squad false false false
164 2025-08-29 alertingEnrichmentAssistantInvestigations experimental @grafana/alerting-squad false false false
165 2025-07-16 alertingAIAnalyzeCentralStateHistory experimental @grafana/alerting-squad false false false
166 2024-11-22 alertingNotificationsStepMode GA @grafana/alerting-squad false false true
198 2025-04-09 unifiedNavbars GA @grafana/plugins-platform-backend false false true
199 2025-04-07 logsPanelControls preview @grafana/observability-logs false false true
200 2025-04-09 metricsFromProfiles experimental @grafana/observability-traces-and-profiling false false true
201 2025-07-31 2025-08-01 grafanaAssistantInProfilesDrilldown GA @grafana/observability-traces-and-profiling false false true
202 2025-07-15 tempoAlerting experimental @grafana/observability-traces-and-profiling false false false
203 2025-04-16 pluginsAutoUpdate experimental @grafana/plugins-platform-backend false false false
204 2025-04-22 alertingListViewV2PreviewToggle privatePreview @grafana/alerting-squad false false true
206 2025-04-24 alertingBulkActionsInUI GA @grafana/alerting-squad false false true
207 2025-06-18 kubernetesAuthzApis deprecated @grafana/identity-access-team false false false
208 2025-08-29 kubernetesAuthZHandlerRedirect experimental @grafana/identity-access-team false false false
209 2025-07-31 2025-08-01 kubernetesAuthzResourcePermissionApis experimental @grafana/identity-access-team false false false
210 2025-10-13 kubernetesAuthzZanzanaSync experimental @grafana/identity-access-team false false false
211 2026-01-12 kubernetesAuthzCoreRolesApi experimental @grafana/identity-access-team false false false
212 2026-01-15 kubernetesAuthzGlobalRolesApi experimental @grafana/identity-access-team false false false
213 2026-01-12 kubernetesAuthzRolesApi experimental @grafana/identity-access-team false false false
214 2026-01-14 2026-01-15 kubernetesAuthzTeamLBACRuleApi experimental @grafana/identity-access-team false false false
215 2026-01-12 kubernetesAuthzRoleBindingsApi experimental @grafana/identity-access-team false false false
216 2025-07-25 kubernetesAuthnMutation experimental @grafana/identity-access-team false false false
217 2025-11-25 kubernetesExternalGroupMapping experimental @grafana/identity-access-team false false false
219 2025-12-10 recentlyViewedDashboards experimental @grafana/grafana-search-navigate-organise false false true
220 2026-01-13 experimentRecentlyViewedDashboards experimental @grafana/grafana-search-navigate-organise false false true
221 2025-06-06 alertEnrichment experimental @grafana/alerting-squad false false false
222 2025-07-31 2025-08-01 alertEnrichmentMultiStep experimental @grafana/alerting-squad false false false
223 2025-07-31 2025-08-01 alertEnrichmentConditional experimental @grafana/alerting-squad false false false
224 2025-06-10 alertingImportAlertmanagerAPI experimental @grafana/alerting-squad false false false
225 2025-07-31 2025-08-01 alertingImportAlertmanagerUI experimental @grafana/alerting-squad false false false
226 2026-01-26 alertingDisableDMAinUI experimental @grafana/alerting-squad false false false
227 2025-07-15 sharingDashboardImage GA @grafana/sharing-squad false false true
228 2025-06-17 preferLibraryPanelTitle privatePreview @grafana/dashboards-squad false false false
229 2025-06-24 tabularNumbers GA @grafana/grafana-frontend-platform false false false
230 2025-06-25 newInfluxDSConfigPageDesign privatePreview @grafana/partner-datasources false false false
231 2025-06-29 2025-06-30 enableAppChromeExtensions experimental @grafana/plugins-platform-backend false false true
232 2025-10-13 enableDashboardEmptyExtensions experimental @grafana/dashboards-squad false false true
233 2025-07-03 foldersAppPlatformAPI experimental @grafana/grafana-search-navigate-organise false false true
234 2025-07-16 otelLogsFormatting experimental @grafana/observability-logs false false true
236 2025-07-18 unifiedStorageSearchDualReaderEnabled experimental @grafana/search-and-storage false false false
237 2025-07-31 dashboardLevelTimeMacros experimental @grafana/dashboards-squad false false true
238 2025-07-25 alertmanagerRemoteSecondaryWithRemoteState experimental @grafana/alerting-squad false false false
239 2025-07-31 2025-08-01 restrictedPluginApis experimental @grafana/plugins-platform-backend false false true
240 2025-07-31 2025-08-01 favoriteDatasources experimental @grafana/plugins-platform-backend false false true
241 2025-07-31 2025-08-01 newLogContext experimental @grafana/observability-logs false false true
242 2025-07-31 2025-08-01 newClickhouseConfigPageDesign privatePreview @grafana/partner-datasources false false false
243 2025-07-31 2025-08-01 teamFolders experimental @grafana/grafana-search-navigate-organise false false false
244 2025-10-22 interactiveLearning preview @grafana/pathfinder false false false
245 2025-07-31 2025-08-01 alertingTriage experimental @grafana/alerting-squad false false false
246 2025-07-31 2025-08-01 graphiteBackendMode privatePreview @grafana/partner-datasources false false false
247 2025-07-31 2025-08-01 azureResourcePickerUpdates GA @grafana/partner-datasources false false true
248 2025-07-31 2025-08-01 prometheusTypeMigration experimental @grafana/partner-datasources false true false
249 2025-07-31 2025-08-01 pluginContainers privatePreview @grafana/plugins-platform-backend false true false
250 2025-08-29 cdnPluginsLoadFirst experimental @grafana/plugins-platform-backend false false false
251 2025-08-29 cdnPluginsUrls experimental @grafana/plugins-platform-backend false false false
252 2025-10-24 pluginInstallAPISync experimental @grafana/plugins-platform-backend false false false
253 2025-10-20 newGauge preview @grafana/dataviz-squad false false true
254 2025-11-12 newVizSuggestions preview @grafana/dataviz-squad false false true
255 2025-12-01 2025-12-02 externalVizSuggestions experimental @grafana/dataviz-squad false false true
256 2025-12-18 heatmapRowsAxisOptions experimental @grafana/dataviz-squad false false true
257 2025-10-17 preventPanelChromeOverflow preview @grafana/grafana-frontend-platform false false true
258 2025-10-31 jaegerEnableGrpcEndpoint experimental @grafana/oss-big-tent false false false
270 2025-11-21 lokiQueryLimitsContext experimental @grafana/observability-logs false false true
271 2025-11-19 rudderstackUpgrade experimental @grafana/grafana-frontend-platform false false true
272 2025-11-27 kubernetesAlertingHistorian experimental @grafana/alerting-squad false true false
273 2025-12-04 2025-12-05 useMTPlugins experimental @grafana/plugins-platform-backend false false true
274 2025-12-09 multiPropsVariables experimental @grafana/dashboards-squad false false true
275 2026-01-05 smoothingTransformation experimental @grafana/datapro false false true
276 2026-01-06 secretsManagementAppPlatformAwsKeeper experimental @grafana/grafana-operator-experience-squad false false false

View file

@ -231,6 +231,10 @@ const (
// Enable Grafana to have a remote Alertmanager instance as the primary Alertmanager.
FlagAlertmanagerRemotePrimary = "alertmanagerRemotePrimary"
// FlagAnnotationPermissionUpdate
// Change the way annotation permissions work by scoping them to folders and dashboards.
FlagAnnotationPermissionUpdate = "annotationPermissionUpdate"
// FlagDashboardNewLayouts
// Enables new dashboard layouts
FlagDashboardNewLayouts = "dashboardNewLayouts"

View file

@ -1242,7 +1242,8 @@
"metadata": {
"name": "dashboardSceneForViewers",
"resourceVersion": "1764664939750",
"creationTimestamp": "2023-11-02T19:02:25Z"
"creationTimestamp": "2023-11-02T19:02:25Z",
"deletionTimestamp": "2026-01-29T16:02:04Z"
},
"spec": {
"description": "Enables dashboard rendering using Scenes for viewer roles",
@ -1256,7 +1257,8 @@
"metadata": {
"name": "dashboardSceneSolo",
"resourceVersion": "1764664939750",
"creationTimestamp": "2024-02-11T08:08:47Z"
"creationTimestamp": "2024-02-11T08:08:47Z",
"deletionTimestamp": "2026-01-29T16:02:04Z"
},
"spec": {
"description": "Enables rendering dashboards using scenes for solo panels",

View file

@ -38,7 +38,7 @@ describe('ShareLinkTab', () => {
config.appUrl = 'http://dashboards.grafana.com/grafana/';
config.rendererAvailable = true;
contextSrv.user.orgId = 1;
config.featureToggles.dashboardSceneForViewers = true;
config.featureToggles.dashboardScene = true;
locationService.push('/d/dash-1?from=now-6h&to=now');
});
@ -89,7 +89,7 @@ describe('ShareLinkTab', () => {
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
).toHaveAttribute(
'href',
'http://dashboards.grafana.com/grafana/render/d-solo/dash-1?from=2019-02-11T13:00:00.000Z&to=2019-02-11T19:00:00.000Z&panelId=A$panel-12&__feature.dashboardSceneSolo=true&hideLogo=true&width=1000&height=500&tz=Pacific%2FEaster'
'http://dashboards.grafana.com/grafana/render/d-solo/dash-1?from=2019-02-11T13:00:00.000Z&to=2019-02-11T19:00:00.000Z&panelId=A$panel-12&__feature.dashboardScene=true&hideLogo=true&width=1000&height=500&tz=Pacific%2FEaster'
);
});
});

View file

@ -78,7 +78,7 @@ export class ShareLinkTab extends SceneObjectBase<ShareLinkTabState> implements
delete imageQueryParams.viewPanel;
imageQueryParams.panelId = panel.getPathId();
// force solo route to use scenes
imageQueryParams['__feature.dashboardSceneSolo'] = true;
imageQueryParams['__feature.dashboardScene'] = true;
}
// hide Grafana logo in the rendered image

View file

@ -65,7 +65,7 @@ const getIframeBuilder =
params.set('panelId', editOrViewPanel);
params.delete('editPanel');
params.delete('viewPanel');
params.set('__feature.dashboardSceneSolo', 'true');
params.set('__feature.dashboardScene', 'true');
const soloUrl = getDashboardUrl({
absolute: true,

View file

@ -3,8 +3,6 @@ import { Dashboard, Panel, RowPanel } from '@grafana/schema';
import {
isDashboardSceneEnabled,
isDashboardSceneForViewersEnabled,
isDashboardSceneSoloEnabled,
isPublicDashboardsSceneEnabled,
isValidLibraryPanelRef,
hasLibraryPanelsInV1Dashboard,
@ -413,40 +411,4 @@ describe('utils', () => {
}
);
});
describe('isDashboardSceneForViewersEnabled', () => {
it.each([
{ dashboardSceneForViewers: true, dashboardNewLayouts: false, expected: true },
{ dashboardSceneForViewers: false, dashboardNewLayouts: true, expected: true },
{ dashboardSceneForViewers: true, dashboardNewLayouts: true, expected: true },
{ dashboardSceneForViewers: false, dashboardNewLayouts: false, expected: false },
])(
'should return $expected when dashboardSceneForViewers=$dashboardSceneForViewers and dashboardNewLayouts=$dashboardNewLayouts',
({ dashboardSceneForViewers, dashboardNewLayouts, expected }) => {
config.featureToggles = {
dashboardSceneForViewers,
dashboardNewLayouts,
};
expect(isDashboardSceneForViewersEnabled()).toBe(expected);
}
);
});
describe('isDashboardSceneSoloEnabled', () => {
it.each([
{ dashboardSceneSolo: true, dashboardNewLayouts: false, expected: true },
{ dashboardSceneSolo: false, dashboardNewLayouts: true, expected: true },
{ dashboardSceneSolo: true, dashboardNewLayouts: true, expected: true },
{ dashboardSceneSolo: false, dashboardNewLayouts: false, expected: false },
])(
'should return $expected when dashboardSceneSolo=$dashboardSceneSolo and dashboardNewLayouts=$dashboardNewLayouts',
({ dashboardSceneSolo, dashboardNewLayouts, expected }) => {
config.featureToggles = {
dashboardSceneSolo,
dashboardNewLayouts,
};
expect(isDashboardSceneSoloEnabled()).toBe(expected);
}
);
});
});

View file

@ -502,11 +502,3 @@ export function isDashboardSceneEnabled(): boolean {
export function isPublicDashboardsSceneEnabled(): boolean {
return !!(config.featureToggles.publicDashboardsScene || config.featureToggles.dashboardNewLayouts);
}
export function isDashboardSceneForViewersEnabled(): boolean {
return !!(config.featureToggles.dashboardSceneForViewers || config.featureToggles.dashboardNewLayouts);
}
export function isDashboardSceneSoloEnabled(): boolean {
return !!(config.featureToggles.dashboardSceneSolo || config.featureToggles.dashboardNewLayouts);
}

View file

@ -3,65 +3,11 @@ import { useParams } from 'react-router-dom-v5-compat';
import { Props } from 'react-virtualized-auto-sizer';
import { render } from 'test/test-utils';
import { selectors } from '@grafana/e2e-selectors';
import { config, locationService } from '@grafana/runtime';
import {
HOME_DASHBOARD_CACHE_KEY,
getDashboardScenePageStateManager,
} from 'app/features/dashboard-scene/pages/DashboardScenePageStateManager';
import {
setupLoadDashboardMockReject,
setupLoadDashboardRuntimeErrorMock,
} from 'app/features/dashboard-scene/utils/test-utils';
import { DashboardDTO, DashboardRoutes } from 'app/types/dashboard';
import { DashboardLoaderSrv, setDashboardLoaderSrv } from '../services/DashboardLoaderSrv';
import { DashboardRoutes } from 'app/types/dashboard';
import DashboardPageProxy, { DashboardPageProxyProps } from './DashboardPageProxy';
const dashMock: DashboardDTO = {
dashboard: {
id: 1,
annotations: {
list: [],
},
uid: 'uid',
title: 'title',
panels: [],
version: 1,
schemaVersion: 1,
},
meta: {
canEdit: false,
created: 'Friday, 4 July 2025 07:56:41 GMT+05:30',
},
};
const homeMock = {
...dashMock,
dashboard: {
...dashMock.dashboard,
uid: '',
title: 'Home',
},
};
const homeMockEditable = {
...homeMock,
meta: {
...homeMock.meta,
canEdit: true,
},
};
const dashMockEditable = {
...dashMock,
meta: {
...dashMock.meta,
canEdit: true,
},
};
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
getDataSourceSrv: jest.fn().mockReturnValue({
@ -88,12 +34,6 @@ jest.mock('react-virtualized-auto-sizer', () => {
});
});
setDashboardLoaderSrv({
loadDashboard: jest.fn().mockResolvedValue(dashMock),
// disabling type checks since this is a test util
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
} as unknown as DashboardLoaderSrv);
jest.mock('react-router-dom-v5-compat', () => ({
...jest.requireActual('react-router-dom-v5-compat'),
useParams: jest.fn().mockReturnValue({}),
@ -112,14 +52,53 @@ function setup(props: Partial<DashboardPageProxyProps> & { uid?: string }) {
}
describe('DashboardPageProxy', () => {
describe('when dashboardSceneForViewers feature toggle disabled', () => {
describe('when dashboardScene feature toggle is enabled (default)', () => {
beforeEach(() => {
jest.clearAllMocks();
config.featureToggles.dashboardSceneForViewers = false;
config.featureToggles.dashboardScene = true;
});
it('home dashboard', async () => {
getDashboardScenePageStateManager().setDashboardCache(HOME_DASHBOARD_CACHE_KEY, dashMock);
it('should render DashboardScenePage for home route', async () => {
setup({
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
});
await waitFor(() => {
expect(screen.queryAllByTestId('dashboard-scene-page')).toHaveLength(1);
});
});
it('should render DashboardScenePage for normal route with uid', async () => {
setup({
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
uid: 'abc-def',
});
await waitFor(() => {
expect(screen.queryAllByTestId('dashboard-scene-page')).toHaveLength(1);
});
});
it('should render legacy DashboardPage when forceOld query param is set', async () => {
setup({
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
uid: 'abc-def',
queryParams: { scenes: false },
});
await waitFor(() => {
expect(screen.queryAllByTestId('dashboard-scene-page')).toHaveLength(0);
});
});
});
describe('when dashboardScene feature toggle is disabled', () => {
beforeEach(() => {
jest.clearAllMocks();
config.featureToggles.dashboardScene = false;
});
it('should render legacy DashboardPage for home route', async () => {
setup({
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
});
@ -129,9 +108,7 @@ describe('DashboardPageProxy', () => {
});
});
it('uid dashboard', async () => {
getDashboardScenePageStateManager().setDashboardCache('abc-def', dashMock);
it('should render legacy DashboardPage for normal route with uid', async () => {
setup({
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
uid: 'abc-def',
@ -141,132 +118,17 @@ describe('DashboardPageProxy', () => {
expect(screen.queryAllByTestId('dashboard-scene-page')).toHaveLength(0);
});
});
});
describe('when dashboardSceneForViewers feature toggle enabled', () => {
beforeEach(() => {
jest.clearAllMocks();
config.featureToggles.dashboardSceneForViewers = true;
});
describe('when user can edit a dashboard ', () => {
it('should not render DashboardScenePage if route is Home', async () => {
getDashboardScenePageStateManager().setDashboardCache(HOME_DASHBOARD_CACHE_KEY, homeMockEditable);
setup({
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
uid: '',
});
await waitFor(() => {
expect(screen.queryAllByTestId('dashboard-scene-page')).toHaveLength(0);
});
it('should render DashboardScenePage when forceScenes query param is set', async () => {
setup({
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
uid: 'abc-def',
queryParams: { scenes: true },
});
it('should not render DashboardScenePage if route is Normal and has uid', async () => {
getDashboardScenePageStateManager().setDashboardCache('abc-def', dashMockEditable);
setup({
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
uid: 'abc-def',
});
await waitFor(() => {
expect(screen.queryAllByTestId('dashboard-scene-page')).toHaveLength(0);
});
await waitFor(() => {
expect(screen.queryAllByTestId('dashboard-scene-page')).toHaveLength(1);
});
});
describe('when user can only view a dashboard ', () => {
it('should render DashboardScenePage if route is Home', async () => {
getDashboardScenePageStateManager().setDashboardCache(HOME_DASHBOARD_CACHE_KEY, homeMock);
setup({
route: { routeName: DashboardRoutes.Home, component: () => null, path: '/' },
uid: '',
});
await waitFor(() => {
expect(screen.queryAllByTestId('dashboard-scene-page')).toHaveLength(1);
});
});
it('should render DashboardScenePage if route is Normal and has uid', async () => {
getDashboardScenePageStateManager().setDashboardCache('uid', dashMock);
setup({
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
uid: 'uid',
});
await waitFor(() => {
expect(screen.queryAllByTestId('dashboard-scene-page')).toHaveLength(1);
});
});
it('should render not DashboardScenePage if dashboard UID does not match route UID', async () => {
getDashboardScenePageStateManager().setDashboardCache('uid', dashMock);
setup({
route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' },
uid: 'wrongUID',
});
await waitFor(() => {
expect(screen.queryAllByTestId('dashboard-scene-page')).toHaveLength(0);
});
});
});
});
describe('errors rendering', () => {
it('should render dashboard not found notice when dashboard... not found', async () => {
setupLoadDashboardMockReject({
status: 404,
statusText: 'Not Found',
data: {
message: 'Dashboard not found',
},
config: {
method: 'GET',
url: 'api/dashboards/uid/adfjq9edwm0hsdsa',
retry: 0,
headers: {
'X-Grafana-Org-Id': 1,
},
hideFromInspector: true,
},
isHandled: true,
});
setup({ route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' }, uid: 'abc' });
expect(await screen.findByTestId(selectors.components.EntityNotFound.container)).toBeInTheDocument();
});
it('should render error alert for backend errors', async () => {
setupLoadDashboardMockReject({
status: 500,
statusText: 'internal server error',
data: {
message: 'Internal server error',
},
config: {
method: 'GET',
url: 'api/dashboards/uid/adfjq9edwm0hsdsa',
retry: 0,
headers: {
'X-Grafana-Org-Id': 1,
},
hideFromInspector: true,
},
isHandled: true,
});
setup({ route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' }, uid: 'abc' });
expect(await screen.findByTestId('dashboard-page-error')).toBeInTheDocument();
expect(await screen.findByTestId('dashboard-page-error')).toHaveTextContent('Internal server error');
});
it('should render error alert for runtime errors', async () => {
setupLoadDashboardRuntimeErrorMock();
setup({ route: { routeName: DashboardRoutes.Normal, component: () => null, path: '/' }, uid: 'abc' });
expect(await screen.findByTestId('dashboard-page-error')).toBeInTheDocument();
expect(await screen.findByTestId('dashboard-page-error')).toHaveTextContent('Runtime error');
});
});
});

View file

@ -1,16 +1,11 @@
import { useLocation, useParams } from 'react-router-dom-v5-compat';
import { useAsync } from 'react-use';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
import DashboardScenePage from 'app/features/dashboard-scene/pages/DashboardScenePage';
import { getDashboardScenePageStateManager } from 'app/features/dashboard-scene/pages/DashboardScenePageStateManager';
import { DashboardRoutes } from 'app/types/dashboard';
import { isDashboardSceneEnabled, isDashboardSceneForViewersEnabled } from '../../dashboard-scene/utils/utils';
import { isDashboardV2Resource } from '../api/utils';
import { isDashboardSceneEnabled } from '../../dashboard-scene/utils/utils';
import DashboardPage, { DashboardPageParams } from './DashboardPage';
import { DashboardPageError } from './DashboardPageError';
import { DashboardPageRouteParams, DashboardPageRouteSearchParams } from './types';
export type DashboardPageProxyProps = Omit<
@ -19,71 +14,21 @@ export type DashboardPageProxyProps = Omit<
>;
// This proxy component is used for Dashboard -> Scenes migration.
// It will render DashboardScenePage if the user is only allowed to view the dashboard.
// When dashboardScene is enabled (default), it renders DashboardScenePage for all users.
// Otherwise - use the legacy DashboardPage ¯\_ (ツ)_/¯
function DashboardPageProxy(props: DashboardPageProxyProps) {
const forceScenes = props.queryParams.scenes === true;
const forceOld = props.queryParams.scenes === false;
const params = useParams<DashboardPageParams>();
const location = useLocation();
const stateManager = getDashboardScenePageStateManager();
if (forceScenes || (isDashboardSceneEnabled() && !forceOld)) {
const useScenes = forceScenes || (isDashboardSceneEnabled() && !forceOld);
if (useScenes) {
return <DashboardScenePage {...props} />;
}
const isScenesSupportedRoute = Boolean(
props.route.routeName === DashboardRoutes.Home ||
props.route.routeName === DashboardRoutes.Template ||
(props.route.routeName === DashboardRoutes.Normal && params.uid)
);
// We pre-fetch dashboard to render dashboard page component depending on dashboard permissions.
// To avoid querying single dashboard multiple times, stateManager.fetchDashboard uses a simple, short-lived cache.
// eslint-disable-next-line react-hooks/rules-of-hooks
const dashboard = useAsync(async () => {
if (params.type === 'snapshot') {
return null;
}
return stateManager.fetchDashboard({
route: props.route.routeName as DashboardRoutes,
uid: params.uid ?? '',
type: params.type,
slug: params.slug,
});
}, [params.uid, props.route.routeName]);
if (dashboard.error) {
return <DashboardPageError error={dashboard.error} />;
}
if (dashboard.loading) {
return null;
}
const uid =
dashboard.value && isDashboardV2Resource(dashboard.value)
? dashboard.value.metadata.name
: dashboard.value?.meta.uid;
const canEdit =
dashboard.value && isDashboardV2Resource(dashboard.value)
? dashboard.value?.access.canEdit
: dashboard.value?.meta?.canEdit || dashboard.value?.meta?.canMakeEditable;
const isNew = !uid;
if (uid !== params.uid && !isNew) {
return null;
}
if (!isDashboardSceneForViewersEnabled()) {
return <DashboardPage {...props} params={params} location={location} />;
}
if (!canEdit && isScenesSupportedRoute && !forceOld) {
return <DashboardScenePage {...props} />;
} else {
return <DashboardPage {...props} params={params} location={location} />;
}
return <DashboardPage {...props} params={params} location={location} />;
}
export default DashboardPageProxy;

View file

@ -23,7 +23,7 @@ import { DashboardRoutes } from 'app/types/dashboard';
import { SafeDynamicImport } from '../core/components/DynamicImports/SafeDynamicImport';
import { RouteDescriptor } from '../core/navigation/types';
import { getPublicDashboardRoutes } from '../features/dashboard/routes';
import { isDashboardSceneSoloEnabled } from '../features/dashboard-scene/utils/utils';
import { isDashboardSceneEnabled } from '../features/dashboard-scene/utils/utils';
import { getProvisioningRoutes } from '../features/provisioning/utils/routes';
const isDevEnv = config.buildInfo.env === 'development';
@ -109,7 +109,7 @@ export function getAppRoutes(): RouteDescriptor[] {
routeName: DashboardRoutes.Normal,
chromeless: true,
component: SafeDynamicImport(() =>
isDashboardSceneSoloEnabled()
isDashboardSceneEnabled()
? import(/* webpackChunkName: "SoloPanelPage" */ '../features/dashboard-scene/solo/SoloPanelPage')
: import(/* webpackChunkName: "SoloPanelPageOld" */ '../features/dashboard/containers/SoloPanelPage')
),
@ -120,7 +120,7 @@ export function getAppRoutes(): RouteDescriptor[] {
routeName: DashboardRoutes.Normal,
chromeless: true,
component: SafeDynamicImport(() =>
isDashboardSceneSoloEnabled()
isDashboardSceneEnabled()
? import(/* webpackChunkName: "SoloPanelPage" */ '../features/dashboard-scene/solo/SoloPanelPage')
: import(/* webpackChunkName: "SoloPanelPageOld" */ '../features/dashboard/containers/SoloPanelPage')
),