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 eabcc7b9a1d..6b76c32fd28 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -34,7 +34,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `unifiedRequestLog` | Writes error logs to the request logger | Yes | | `logsExploreTableVisualisation` | A table visualisation for logs in Explore | Yes | | `awsDatasourcesTempCredentials` | Support temporary security credentials in AWS plugins for Grafana Cloud customers | Yes | -| `transformationsRedesign` | Enables the transformations redesign | Yes | | `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 | diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 45c738404e6..edfb1056977 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -161,11 +161,6 @@ export interface FeatureToggles { */ awsDatasourcesTempCredentials?: boolean; /** - * Enables the transformations redesign - * @default true - */ - transformationsRedesign?: boolean; - /** * Enable support for Machine Learning in server-side expressions */ mlExpressions?: boolean; diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 8dad5d1b369..b7362496e38 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -255,15 +255,6 @@ var ( Expression: "true", // enabled by default Owner: awsDatasourcesSquad, }, - { - Name: "transformationsRedesign", - Description: "Enables the transformations redesign", - Stage: FeatureStageGeneralAvailability, - FrontendOnly: true, - Expression: "true", // enabled by default - Owner: grafanaObservabilityMetricsSquad, - AllowSelfServe: true, - }, { Name: "mlExpressions", Description: "Enable support for Machine Learning in server-side expressions", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 837d868756b..2d2688c285c 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -32,7 +32,6 @@ enableDatagridEditing,preview,@grafana/dataviz-squad,false,false,true extraThemes,experimental,@grafana/grafana-frontend-platform,false,false,true logsExploreTableVisualisation,GA,@grafana/observability-logs,false,false,true awsDatasourcesTempCredentials,GA,@grafana/aws-datasources,false,false,false -transformationsRedesign,GA,@grafana/observability-metrics,false,false,true mlExpressions,experimental,@grafana/alerting-squad,false,false,false datasourceAPIServers,experimental,@grafana/grafana-app-platform-squad,false,true,false grafanaAPIServerWithExperimentalAPIs,experimental,@grafana/grafana-app-platform-squad,true,true,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 9a94125f39f..f5792d32d88 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -139,10 +139,6 @@ const ( // Support temporary security credentials in AWS plugins for Grafana Cloud customers FlagAwsDatasourcesTempCredentials = "awsDatasourcesTempCredentials" - // FlagTransformationsRedesign - // Enables the transformations redesign - FlagTransformationsRedesign = "transformationsRedesign" - // FlagMlExpressions // Enable support for Machine Learning in server-side expressions FlagMlExpressions = "mlExpressions" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 282737f8ef8..0fc14a616c5 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -4147,7 +4147,8 @@ "metadata": { "name": "transformationsRedesign", "resourceVersion": "1753448760331", - "creationTimestamp": "2023-07-12T16:35:49Z" + "creationTimestamp": "2023-07-12T16:35:49Z", + "deletionTimestamp": "2025-11-06T20:37:15Z" }, "spec": { "description": "Enables the transformations redesign", diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx index 8f3a9488b58..17aa8a59d5f 100644 --- a/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx +++ b/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx @@ -29,10 +29,7 @@ export const PanelEditorTabs = memo(({ panel, dashboard, tabs, onChangeTab }: Pa const instrumentedOnChangeTab = useCallback( (tab: PanelEditorTab) => { - let eventName = 'panel_editor_tabs_changed'; - if (config.featureToggles.transformationsRedesign) { - eventName = 'transformations_redesign_' + eventName; - } + let eventName = 'transformations_redesign_panel_editor_tabs_changed'; if (!tab.active) { reportInteraction(eventName, { tab_id: tab.id }); diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx index bebae5de3e2..18a2954d77e 100644 --- a/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx +++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx @@ -20,7 +20,6 @@ import { QueryOperationToggleAction, } from 'app/core/components/QueryOperationRow/QueryOperationAction'; import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOperationRow'; -import config from 'app/core/config'; import { PluginStateInfo } from 'app/features/plugins/components/PluginStateInfo'; import { TransformationEditor } from './TransformationEditor'; @@ -89,10 +88,7 @@ export const TransformationOperationRow = ({ const instrumentToggleCallback = useCallback( (callback: (e: React.MouseEvent) => void, toggleId: string, active: boolean | undefined) => (e: React.MouseEvent) => { - let eventName = 'panel_editor_tabs_transformations_toggle'; - if (config.featureToggles.transformationsRedesign) { - eventName = 'transformations_redesign_' + eventName; - } + let eventName = 'transformations_redesign_panel_editor_tabs_transformations_toggle'; reportInteraction(eventName, { action: active ? 'off' : 'on', @@ -198,27 +194,25 @@ export const TransformationOperationRow = ({ (config.featureToggles.transformationsRedesign ? setShowDeleteModal(true) : onRemove(index))} + onClick={() => setShowDeleteModal(true)} /> - {config.featureToggles.transformationsRedesign && ( - { - setShowDeleteModal(false); - onRemove(index); - }} - onDismiss={() => setShowDeleteModal(false)} - /> - )} + { + setShowDeleteModal(false); + onRemove(index); + }} + onDismiss={() => setShowDeleteModal(false)} + /> ); }; diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.test.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.test.tsx index 80ff9f45e1e..4960b1cc973 100644 --- a/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.test.tsx +++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.test.tsx @@ -3,7 +3,6 @@ import userEvent from '@testing-library/user-event'; import { DataTransformerConfig, standardTransformersRegistry } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; -import config from 'app/core/config'; import { getStandardTransformers } from 'app/features/transformers/standardTransformers'; import { PanelModel } from '../../state/PanelModel'; @@ -20,23 +19,15 @@ describe('TransformationsEditor', () => { standardTransformersRegistry.setInit(getStandardTransformers); describe('when no transformations configured', () => { - it('renders transformation list by default and without transformationsRedesign on', () => { - setup(); - const cards = screen.getAllByTestId(/New transform/i); - expect(cards.length).toEqual(standardTransformersRegistry.list().length); - }); - - it('renders transformation empty message with transformationsRedesign feature toggled on', () => { - config.featureToggles.transformationsRedesign = true; + it('renders transformation empty message', () => { setup(); const message = screen.getAllByTestId('data-testid no transformations message'); expect(message.length).toEqual(1); - config.featureToggles.transformationsRedesign = false; }); }); describe('when transformations configured', () => { - function renderEditors() { + it('renders transformation editors', () => { setup([ { id: 'reduce', @@ -45,18 +36,11 @@ describe('TransformationsEditor', () => { ]); const editors = screen.getAllByTestId(/Transformation editor/); expect(editors).toHaveLength(1); - } - - it('renders transformation editors', renderEditors); - it('renders transformation editors with transformationsRedesign feature toggled on', () => { - config.featureToggles.transformationsRedesign = true; - renderEditors(); - config.featureToggles.transformationsRedesign = false; }); }); describe('when Add transformation clicked', () => { - async function renderPicker() { + it('renders transformations picker', async () => { setup([ { id: 'reduce', @@ -69,19 +53,12 @@ describe('TransformationsEditor', () => { const search = screen.getByTestId(selectors.components.Transforms.searchInput); expect(search).toBeDefined(); - } - - it('renders transformations picker', renderPicker); - it('renders transformation picker with transformationsRedesign feature toggled on', async () => { - config.featureToggles.transformationsRedesign = true; - await renderPicker(); - config.featureToggles.transformationsRedesign = false; }); }); describe('actions', () => { describe('debug', () => { - async function showHideDebugger() { + it('should show/hide debugger', async () => { setup([ { id: 'reduce', @@ -96,13 +73,6 @@ describe('TransformationsEditor', () => { await userEvent.click(debugButton); expect(screen.getByTestId(debuggerSelector)).toBeInTheDocument(); - } - - it('should show/hide debugger', showHideDebugger); - it('renders transformation editors with transformationsRedesign feature toggled on', async () => { - config.featureToggles.transformationsRedesign = true; - await showHideDebugger(); - config.featureToggles.transformationsRedesign = false; }); }); }); diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx index afdf6b782ea..1593c6162b7 100644 --- a/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx +++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx @@ -25,14 +25,12 @@ import { ButtonGroup, ScrollContainer, } from '@grafana/ui'; -import config from 'app/core/config'; import { EmptyTransformationsMessage } from 'app/features/dashboard-scene/panel-edit/PanelDataPane/EmptyTransformationsMessage'; import { PanelModel } from '../../state/PanelModel'; import { PanelNotSupported } from '../PanelEditor/PanelNotSupported'; import { TransformationOperationRows } from './TransformationOperationRows'; -import { TransformationPicker } from './TransformationPicker'; import { TransformationPickerNg } from './TransformationPickerNg'; import { TransformationsEditorTransformation } from './types'; @@ -141,21 +139,19 @@ class UnThemedTransformationsEditor extends React.PureComponent, prevState: Readonly): void { - if (config.featureToggles.transformationsRedesign) { - const prevHasTransforms = prevState.transformations.length > 0; - const prevShowPicker = !prevHasTransforms || prevState.showPicker; + const prevHasTransforms = prevState.transformations.length > 0; + const prevShowPicker = !prevHasTransforms || prevState.showPicker; - const currentHasTransforms = this.state.transformations.length > 0; - const currentShowPicker = !currentHasTransforms || this.state.showPicker; + const currentHasTransforms = this.state.transformations.length > 0; + const currentShowPicker = !currentHasTransforms || this.state.showPicker; - if (prevShowPicker !== currentShowPicker) { - // kindOfZero will be a random number between 0 and 0.5. It will be rounded to 0 by the scrollable component. - // We cannot always use 0 as it will not trigger a rerender of the scrollable component consistently - // due to React changes detection algo. - const kindOfZero = Math.random() / 2; + if (prevShowPicker !== currentShowPicker) { + // kindOfZero will be a random number between 0 and 0.5. It will be rounded to 0 by the scrollable component. + // We cannot always use 0 as it will not trigger a rerender of the scrollable component consistently + // due to React changes detection algo. + const kindOfZero = Math.random() / 2; - this.setState({ scrollTop: currentShowPicker ? kindOfZero : Number.MAX_SAFE_INTEGER }); - } + this.setState({ scrollTop: currentShowPicker ? kindOfZero : Number.MAX_SAFE_INTEGER }); } if (prevState.scrollTop !== this.state.scrollTop) { @@ -183,10 +179,7 @@ class UnThemedTransformationsEditor extends React.PureComponent) => { - let eventName = 'panel_editor_tabs_transformations_management'; - if (config.featureToggles.transformationsRedesign) { - eventName = 'transformations_redesign_' + eventName; - } + const eventName = 'transformations_redesign_panel_editor_tabs_transformations_management'; reportInteraction(eventName, { action: 'add', @@ -211,10 +204,7 @@ class UnThemedTransformationsEditor extends React.PureComponent { const { transformations } = this.state; const next = Array.from(transformations); - let eventName = 'panel_editor_tabs_transformations_management'; - if (config.featureToggles.transformationsRedesign) { - eventName = 'transformations_redesign_' + eventName; - } + const eventName = 'transformations_redesign_panel_editor_tabs_transformations_management'; reportInteraction(eventName, { action: 'change', @@ -227,10 +217,7 @@ class UnThemedTransformationsEditor extends React.PureComponent { const { transformations } = this.state; const next = Array.from(transformations); - let eventName = 'panel_editor_tabs_transformations_management'; - if (config.featureToggles.transformationsRedesign) { - eventName = 'transformations_redesign_' + eventName; - } + const eventName = 'transformations_redesign_panel_editor_tabs_transformations_management'; reportInteraction(eventName, { action: 'remove', @@ -298,21 +285,12 @@ class UnThemedTransformationsEditor extends React.PureComponent 0; let suffix: React.ReactNode = null; let xforms = standardTransformersRegistry.list().sort((a, b) => (a.name > b.name ? 1 : b.name > a.name ? -1 : 0)); - // In the case we're not on the transformation - // redesign and there are no transformations - // then we show the picker in that case - if (!transformationsRedesign && noTransforms) { - showPicker = true; - } - if (this.state.selectedFilter !== VIEW_ALL_VALUE) { xforms = xforms.filter( (t) => @@ -357,77 +335,53 @@ class UnThemedTransformationsEditor extends React.PureComponent this.setState({ showPicker: false })} - onSelectedFilterChange={(filter) => this.setState({ selectedFilter: filter })} - onShowIllustrationsChange={(showIllustrations) => this.setState({ showIllustrations })} - onSearchChange={this.onSearchChange} - onSearchKeyDown={this.onSearchKeyDown} - onTransformationAdd={this.onTransformationAdd} - data={this.state.data.series} - selectedFilter={this.state.selectedFilter} - showIllustrations={this.state.showIllustrations} - /> - ); + const picker = ( + this.setState({ showPicker: false })} + onSelectedFilterChange={(filter) => this.setState({ selectedFilter: filter })} + onShowIllustrationsChange={(showIllustrations) => this.setState({ showIllustrations })} + onSearchChange={this.onSearchChange} + onSearchKeyDown={this.onSearchKeyDown} + onTransformationAdd={this.onTransformationAdd} + data={this.state.data.series} + selectedFilter={this.state.selectedFilter} + showIllustrations={this.state.showIllustrations} + /> + ); - deleteAll = ( - <> - - this.onTransformationRemoveAll()} - onDismiss={() => this.setState({ showRemoveAllModal: false })} - /> - - ); - } - // Otherwise we use the old picker - else { - picker = ( - + + this.onTransformationRemoveAll()} + onDismiss={() => this.setState({ showRemoveAllModal: false })} /> - ); - } + + ); - // Compose actions, if we're in the - // redesign a "Delete All Transformations" - // button (with confirm modal) is added const actions = (