diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index dea03f07484..2653da9f879 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -79,7 +79,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `grafanaAssistantInProfilesDrilldown` | Enables integration with Grafana Assistant in Profiles Drilldown | Yes | | `sharingDashboardImage` | Enables image sharing functionality for dashboards | Yes | | `tabularNumbers` | Use fixed-width numbers globally in the UI | | -| `dashboardDsAdHocFiltering` | Enables adhoc filtering support for the dashboard datasource | Yes | | `adhocFiltersInTooltips` | Enable adhoc filter buttons in visualization tooltips | Yes | | `tempoSearchBackendMigration` | Run search queries through the tempo backend | | diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 89e1f8e9aed..d77c6821339 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -1119,11 +1119,6 @@ export interface FeatureToggles { */ unifiedStorageSearchDualReaderEnabled?: boolean; /** - * Enables adhoc filtering support for the dashboard datasource - * @default true - */ - dashboardDsAdHocFiltering?: boolean; - /** * Supports __from and __to macros that always use the dashboard level time range */ dashboardLevelTimeMacros?: boolean; diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 6bc730b8d50..0b4f483e502 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -1942,14 +1942,6 @@ var ( HideFromAdminPage: true, HideFromDocs: true, }, - { - Name: "dashboardDsAdHocFiltering", - Description: "Enables adhoc filtering support for the dashboard datasource", - Stage: FeatureStageGeneralAvailability, - Owner: grafanaDataProSquad, - FrontendOnly: true, - Expression: "true", - }, { Name: "dashboardLevelTimeMacros", Description: "Supports __from and __to macros that always use the dashboard level time range", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 8695ec64f57..3822dfc8c8d 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -250,7 +250,6 @@ foldersAppPlatformAPI,experimental,@grafana/grafana-search-navigate-organise,fal otelLogsFormatting,experimental,@grafana/observability-logs,false,false,true alertingNotificationHistory,experimental,@grafana/alerting-squad,false,false,false unifiedStorageSearchDualReaderEnabled,experimental,@grafana/search-and-storage,false,false,false -dashboardDsAdHocFiltering,GA,@grafana/datapro,false,false,true dashboardLevelTimeMacros,experimental,@grafana/dashboards-squad,false,false,true alertmanagerRemoteSecondaryWithRemoteState,experimental,@grafana/alerting-squad,false,false,false restrictedPluginApis,experimental,@grafana/plugins-platform-backend,false,false,true diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index fe407b9066f..a1a1be66f54 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -1010,10 +1010,6 @@ const ( // Enable dual reader for unified storage search FlagUnifiedStorageSearchDualReaderEnabled = "unifiedStorageSearchDualReaderEnabled" - // FlagDashboardDsAdHocFiltering - // Enables adhoc filtering support for the dashboard datasource - FlagDashboardDsAdHocFiltering = "dashboardDsAdHocFiltering" - // FlagDashboardLevelTimeMacros // Supports __from and __to macros that always use the dashboard level time range FlagDashboardLevelTimeMacros = "dashboardLevelTimeMacros" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index dbf4d982629..05c83353ac6 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -1061,6 +1061,7 @@ "name": "dashboardDsAdHocFiltering", "resourceVersion": "1756814786992", "creationTimestamp": "2025-07-23T08:12:25Z", + "deletionTimestamp": "2025-09-27T19:59:33Z", "annotations": { "grafana.app/updatedTimestamp": "2025-09-02 12:06:26.992384 +0000 UTC" } diff --git a/public/app/features/dashboard-scene/settings/variables/components/AdHocVariableForm.tsx b/public/app/features/dashboard-scene/settings/variables/components/AdHocVariableForm.tsx index 29183c79531..5434068f21e 100644 --- a/public/app/features/dashboard-scene/settings/variables/components/AdHocVariableForm.tsx +++ b/public/app/features/dashboard-scene/settings/variables/components/AdHocVariableForm.tsx @@ -4,7 +4,6 @@ import { DataSourceInstanceSettings, MetricFindValue, readCSV } from '@grafana/d import { selectors } from '@grafana/e2e-selectors'; import { Trans, t } from '@grafana/i18n'; import { EditorField } from '@grafana/plugin-ui'; -import { config } from '@grafana/runtime'; import { DataSourceRef } from '@grafana/schema'; import { Alert, CodeEditor, Field, Switch, Box } from '@grafana/ui'; import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; @@ -67,7 +66,7 @@ export function AdHocVariableForm({ onChange={onDataSourceChange} width={30} variables={true} - dashboard={config.featureToggles.dashboardDsAdHocFiltering} + dashboard={true} noDefault /> diff --git a/public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx b/public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx index 2cbfa311d7b..876e996dbe8 100644 --- a/public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx +++ b/public/app/plugins/datasource/dashboard/DashboardQueryEditor.test.tsx @@ -1,8 +1,7 @@ -import { act, render, screen, waitFor } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { getDefaultTimeRange, LoadingState } from '@grafana/data'; -import config from 'app/core/config'; import { mockDataSource } from 'app/features/alerting/unified/mocks'; import { setupDataSources } from 'app/features/alerting/unified/testSetup/datasources'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; @@ -30,9 +29,6 @@ jest.mock('app/core/config', () => ({ }, }, }, - featureToggles: { - dashboardDsAdHocFiltering: false, // Default to false, can be overridden in tests - }, })); setupDataSources(mockDataSource({ isDefault: true })); @@ -179,11 +175,7 @@ describe('DashboardQueryEditor', () => { jest.spyOn(getDashboardSrv(), 'getCurrent').mockImplementation(() => mockDashboard); }); - it('shows the AdHoc Filters toggle when feature toggle is enabled', async () => { - await act(async () => { - config.featureToggles.dashboardDsAdHocFiltering = true; - }); - + it('shows the AdHoc Filters toggle', async () => { const query: DashboardQuery = { refId: 'A', panelId: 1, adHocFiltersEnabled: false }; await act(async () => { @@ -201,30 +193,5 @@ describe('DashboardQueryEditor', () => { const adhocFiltersToggle = await screen.findByText('AdHoc Filters'); expect(adhocFiltersToggle).toBeInTheDocument(); }); - - it('does not show the AdHoc Filters toggle when feature toggle is disabled', async () => { - await act(async () => { - config.featureToggles.dashboardDsAdHocFiltering = false; - }); - - const query: DashboardQuery = { refId: 'A', panelId: 1, adHocFiltersEnabled: false }; - - await act(async () => { - render( - - ); - }); - - // Wait for any async operations to complete - await waitFor(() => { - expect(screen.queryByText('AdHoc Filters')).not.toBeInTheDocument(); - }); - }); }); }); diff --git a/public/app/plugins/datasource/dashboard/DashboardQueryEditor.tsx b/public/app/plugins/datasource/dashboard/DashboardQueryEditor.tsx index e137069219d..d8e9b952281 100644 --- a/public/app/plugins/datasource/dashboard/DashboardQueryEditor.tsx +++ b/public/app/plugins/datasource/dashboard/DashboardQueryEditor.tsx @@ -205,15 +205,13 @@ export function DashboardQueryEditor({ data, query, onChange, onRunQuery }: Prop )} - {config.featureToggles.dashboardDsAdHocFiltering && ( - - - - )} + + + {loadingResults ? ( diff --git a/public/app/plugins/datasource/dashboard/datasource.test.ts b/public/app/plugins/datasource/dashboard/datasource.test.ts index 55d7a12968d..2dcb17c63db 100644 --- a/public/app/plugins/datasource/dashboard/datasource.test.ts +++ b/public/app/plugins/datasource/dashboard/datasource.test.ts @@ -12,7 +12,7 @@ import { AdHocVariableFilter, } from '@grafana/data'; import { getPanelPlugin } from '@grafana/data/test'; -import { setPluginImportUtils, config } from '@grafana/runtime'; +import { setPluginImportUtils } from '@grafana/runtime'; import { SafeSerializableSceneObject, SceneDataNode, @@ -178,16 +178,6 @@ describe('DashboardDatasource', () => { // Test AdHoc filtering via the Public API first, to ensure Integration describe('Integration (Public API)', () => { - const originalToggleValue = config.featureToggles.dashboardDsAdHocFiltering; - - beforeEach(() => { - config.featureToggles.dashboardDsAdHocFiltering = true; - }); - - afterEach(() => { - config.featureToggles.dashboardDsAdHocFiltering = originalToggleValue; - }); - it('should apply basic filtering end-to-end through public query method', async () => { const testFrame = createTestFrame([ { name: 'name', type: FieldType.string, values: ['John', 'Jane', 'Bob'] }, @@ -224,46 +214,6 @@ describe('DashboardDatasource', () => { expect(result?.data[0].length).toBe(1); }); - it('should respect feature toggle and not filter when disabled', async () => { - // Temporarily disable the feature toggle for this test - config.featureToggles.dashboardDsAdHocFiltering = false; - - const testFrame = createTestFrame([ - { name: 'name', type: FieldType.string, values: ['John', 'Jane', 'Bob'] }, - { name: 'age', type: FieldType.number, values: [25, 30, 35] }, - ]); - - const scene = new SceneFlexLayout({ - children: [ - new SceneFlexItem({ - body: new VizPanel({ - key: getVizPanelKeyForPanelId(1), - $data: new SceneDataNode({ - data: { - series: [testFrame], - state: LoadingState.Done, - timeRange: getDefaultTimeRange(), - }, - }), - }), - }), - ], - }); - - const ds = new DashboardDatasource({} as DataSourceInstanceSettings); - const filters: AdHocVariableFilter[] = [{ key: 'name', operator: '=', value: 'John' }]; - - const observable = ds.query(createQueryRequest(filters, scene)); - - let result: DataQueryResponse | undefined; - observable.subscribe({ next: (data) => (result = data) }); - - // Should return unfiltered data since feature toggle is disabled - expect(result?.data[0].fields[0].values).toEqual(['John', 'Jane', 'Bob']); - expect(result?.data[0].fields[1].values).toEqual([25, 30, 35]); - expect(result?.data[0].length).toBe(3); - }); - it('should respect per-panel adHocFiltersEnabled setting and not filter when disabled', async () => { const testFrame = createTestFrame([ { name: 'name', type: FieldType.string, values: ['John', 'Jane', 'Bob'] }, @@ -723,27 +673,8 @@ describe('DashboardDatasource', () => { }); describe('getDrilldownsApplicability', () => { - const originalToggleValue = config.featureToggles.dashboardDsAdHocFiltering; const ds = new DashboardDatasource({} as DataSourceInstanceSettings); - beforeEach(() => { - config.featureToggles.dashboardDsAdHocFiltering = true; - }); - - afterEach(() => { - config.featureToggles.dashboardDsAdHocFiltering = originalToggleValue; - }); - - it('should return empty array when feature toggle is disabled', async () => { - config.featureToggles.dashboardDsAdHocFiltering = false; - - const result = await ds.getDrilldownsApplicability({ - filters: [{ key: 'name', operator: '=', value: 'test' }], - }); - - expect(result).toEqual([]); - }); - it('should mark supported operators as applicable', async () => { const result = await ds.getDrilldownsApplicability({ filters: [ diff --git a/public/app/plugins/datasource/dashboard/datasource.ts b/public/app/plugins/datasource/dashboard/datasource.ts index 9969c9afe7f..34a1781623f 100644 --- a/public/app/plugins/datasource/dashboard/datasource.ts +++ b/public/app/plugins/datasource/dashboard/datasource.ts @@ -20,7 +20,6 @@ import { DataSourceGetDrilldownsApplicabilityOptions, DrilldownsApplicability, } from '@grafana/data'; -import { config } from '@grafana/runtime'; import { isSceneObject, SceneDataProvider, SceneDataTransformer, SceneObject } from '@grafana/scenes'; import { activateSceneObjectAndParentTree, @@ -140,11 +139,10 @@ export class DashboardDatasource extends DataSourceApi { ...field, config: { ...field.config, - // Enable AdHoc filtering for string and numeric fields only when feature toggle and per-panel setting are enabled - filterable: - config.featureToggles.dashboardDsAdHocFiltering && query.adHocFiltersEnabled - ? field.type === FieldType.string || field.type === FieldType.number - : field.config.filterable, + // Enable AdHoc filtering for string and numeric fields only when per-panel setting is enabled + filterable: query.adHocFiltersEnabled + ? field.type === FieldType.string || field.type === FieldType.number + : field.config.filterable, }, state: { ...field.state, @@ -153,7 +151,7 @@ export class DashboardDatasource extends DataSourceApi { }; }); - if (!config.featureToggles.dashboardDsAdHocFiltering || !query.adHocFiltersEnabled || filters.length === 0) { + if (!query.adHocFiltersEnabled || filters.length === 0) { return [...series, ...annotations]; } @@ -247,11 +245,9 @@ export class DashboardDatasource extends DataSourceApi { const field = frame.fields[fieldIndex]; - // Only support string and numeric fields when feature toggle is enabled - if (config.featureToggles.dashboardDsAdHocFiltering) { - if (field.type !== FieldType.string && field.type !== FieldType.number) { - return null; - } + // Only support string and numeric fields + if (field.type !== FieldType.string && field.type !== FieldType.number) { + return null; } // Map operator to matcher ID @@ -357,10 +353,6 @@ export class DashboardDatasource extends DataSourceApi { async getDrilldownsApplicability( options?: DataSourceGetDrilldownsApplicabilityOptions ): Promise { - if (!config.featureToggles.dashboardDsAdHocFiltering) { - return []; - } - // Check if any query has adhoc filters enabled const hasAdHocFiltersEnabled = options?.queries?.some((query) => query.adHocFiltersEnabled);