From a3fa0f07f9587561f053e1a3321c14d48a4abedd Mon Sep 17 00:00:00 2001 From: Victor Marin Date: Wed, 5 Nov 2025 16:17:26 +0200 Subject: [PATCH] fix --- .../scene/PanelGroupByAction.test.tsx | 326 ++++++++++-------- public/locales/en-US/grafana.json | 8 + 2 files changed, 186 insertions(+), 148 deletions(-) diff --git a/public/app/features/dashboard-scene/scene/PanelGroupByAction.test.tsx b/public/app/features/dashboard-scene/scene/PanelGroupByAction.test.tsx index 39911c590de..c0c110e4ebd 100644 --- a/public/app/features/dashboard-scene/scene/PanelGroupByAction.test.tsx +++ b/public/app/features/dashboard-scene/scene/PanelGroupByAction.test.tsx @@ -1,5 +1,6 @@ import { of } from 'rxjs'; +import { DataQueryRequest, getDefaultTimeRange, LoadingState } from '@grafana/data'; import { GroupByVariable, SceneQueryRunner, SceneVariableSet, VizPanel } from '@grafana/scenes'; import { DashboardScene } from './DashboardScene'; @@ -48,167 +49,196 @@ describe('PanelGroupByAction', () => { deactivate(); }); - describe('GroupBy variable integration', () => { - it('should access variable through scene graph', () => { - const { action, variable } = buildTestScene(); + it('should return options array', async () => { + const { action, variable } = buildTestScene(); - jest.spyOn(variable, 'validateAndUpdate').mockReturnValue( - of({ - origin: variable, - state: variable.state, - }) - ); - - const deactivate = action.activate(); - - const foundVariable = action.getGroupByVariable(); - expect(foundVariable).toBe(variable); - expect(foundVariable?.state.type).toBe('groupby'); - - deactivate(); - }); - - it('should handle missing GroupByVariable gracefully', () => { - const action = new PanelGroupByAction(); - const panel = new VizPanel({ - title: 'Panel A', - pluginId: 'table', - key: 'panel-1', - headerActions: [action], - $data: new SceneQueryRunner({ - datasource: { uid: 'my-uid' }, - queries: [{ query: 'QueryA', refId: 'A' }], - }), - }); - - new DashboardScene({ - uid: 'dash-1', - $variables: new SceneVariableSet({ - variables: [], // No GroupByVariable - }), - body: DefaultGridLayoutManager.fromVizPanels([panel]), - }); - - const deactivate = action.activate(); - - expect(action.getGroupByVariable()).toBeUndefined(); - - deactivate(); - }); - }); - - describe('Panel parent requirement', () => { - it('should throw error if parent is not VizPanel', () => { - const action = new PanelGroupByAction(); - - expect(() => { - action.activate(); - }).toThrow('PanelGroupByAction can be used only for VizPanel'); - }); - }); - - describe('getGroupByOptions', () => { - it('should return empty array when variable has no options', async () => { - const { action, variable } = buildTestScene(); - - jest.spyOn(variable, 'validateAndUpdate').mockReturnValue( - of({ - origin: variable, - state: { - ...variable.state, - options: [], - }, - }) - ); - - const deactivate = action.activate(); - - const options = await action.getGroupByOptions(); - expect(options).toEqual([]); - - deactivate(); - }); - - it('should filter options based on applicability', async () => { - const { action, variable, panel } = buildTestScene(); - - variable.setState({ - options: [ - { label: 'field1', value: 'field1' }, - { label: 'field2', value: 'field2' }, - { label: 'field3', value: 'field3' }, - ], - }); - - const queryRunner = panel.state.$data as SceneQueryRunner; - queryRunner.setState({ - data: { - state: 'Done', - series: [], - timeRange: {} as any, - request: { - targets: [{ refId: 'A', datasource: { uid: 'test-uid', type: 'prometheus' } }], - }, + jest.spyOn(variable, 'validateAndUpdate').mockReturnValue( + of({ + origin: variable, + state: { + ...variable.state, + options: [ + { label: 'field1', value: 'field1' }, + { label: 'field2', value: 'field2' }, + ], }, - }); + }) + ); - jest.spyOn(variable, 'validateAndUpdate').mockReturnValue( - of({ - origin: variable, - state: variable.state, - }) - ); + jest.spyOn(variable, 'getGroupByApplicabilityForQueries').mockResolvedValue([ + { key: 'field1', applicable: true }, + { key: 'field2', applicable: true }, + ]); - jest.spyOn(variable, 'getGroupByApplicabilityForQueries').mockResolvedValue([ - { key: 'field1', applicable: true }, - { key: 'field2', applicable: false }, - { key: 'field3', applicable: true }, - ]); + const deactivate = action.activate(); - const deactivate = action.activate(); + const options = await action.getGroupByOptions(); + expect(Array.isArray(options)).toBe(true); - const options = await action.getGroupByOptions(); - expect(options).toHaveLength(2); - expect(options).toEqual([ - { label: 'field1', value: 'field1' }, - { label: 'field3', value: 'field3' }, - ]); - - deactivate(); - }); + deactivate(); }); }); - function buildTestScene() { - const variable = new GroupByVariable({ - name: 'A', - label: 'A', - description: 'A', - type: 'groupby', - value: 'Text', + describe('GroupBy variable integration', () => { + it('should access variable through scene graph', () => { + const { action, variable } = buildTestScene(); + + jest.spyOn(variable, 'validateAndUpdate').mockReturnValue( + of({ + origin: variable, + state: variable.state, + }) + ); + + const deactivate = action.activate(); + + const foundVariable = action.getGroupByVariable(); + expect(foundVariable).toBe(variable); + expect(foundVariable?.state.type).toBe('groupby'); + + deactivate(); }); - const action = new PanelGroupByAction(); + it('should handle missing GroupByVariable gracefully', () => { + const action = new PanelGroupByAction(); + const panel = new VizPanel({ + title: 'Panel A', + pluginId: 'table', + key: 'panel-1', + headerActions: [action], + $data: new SceneQueryRunner({ + datasource: { uid: 'my-uid' }, + queries: [{ query: 'QueryA', refId: 'A' }], + }), + }); - const panel = new VizPanel({ - title: 'Panel A', - pluginId: 'table', - key: 'panel-12', - headerActions: [action], - $data: new SceneQueryRunner({ - datasource: { uid: 'my-uid' }, - queries: [{ query: 'QueryA', refId: 'A' }], - }), + new DashboardScene({ + uid: 'dash-1', + $variables: new SceneVariableSet({ + variables: [], // No GroupByVariable + }), + body: DefaultGridLayoutManager.fromVizPanels([panel]), + }); + + const deactivate = action.activate(); + + expect(action.getGroupByVariable()).toBeUndefined(); + + deactivate(); + }); + }); + + describe('Panel parent requirement', () => { + it('should throw error if parent is not VizPanel', () => { + const action = new PanelGroupByAction(); + + expect(() => { + action.activate(); + }).toThrow('PanelGroupByAction can be used only for VizPanel'); + }); + }); + + describe('getGroupByOptions', () => { + it('should return empty array when variable has no options', async () => { + const { action, variable } = buildTestScene(); + + jest.spyOn(variable, 'validateAndUpdate').mockReturnValue( + of({ + origin: variable, + state: { + ...variable.state, + options: [], + }, + }) + ); + + const deactivate = action.activate(); + + const options = await action.getGroupByOptions(); + expect(options).toEqual([]); + + deactivate(); }); - const dashboard = new DashboardScene({ - uid: 'A', - $variables: new SceneVariableSet({ - variables: [variable], - }), - body: DefaultGridLayoutManager.fromVizPanels([panel]), - }); + it('should filter options based on applicability', async () => { + const { action, variable, panel } = buildTestScene(); - return { action, panel, dashboard, variable }; - } + variable.setState({ + options: [ + { label: 'field1', value: 'field1' }, + { label: 'field2', value: 'field2' }, + { label: 'field3', value: 'field3' }, + ], + }); + + const queryRunner = panel.state.$data as SceneQueryRunner; + queryRunner.setState({ + data: { + state: LoadingState.Done, + series: [], + timeRange: getDefaultTimeRange(), + request: { + targets: [{ refId: 'A', datasource: { uid: 'test-uid', type: 'prometheus' } }], + } as DataQueryRequest, + }, + }); + + jest.spyOn(variable, 'validateAndUpdate').mockReturnValue( + of({ + origin: variable, + state: variable.state, + }) + ); + + jest.spyOn(variable, 'getGroupByApplicabilityForQueries').mockResolvedValue([ + { key: 'field1', applicable: true }, + { key: 'field2', applicable: false }, + { key: 'field3', applicable: true }, + ]); + + const deactivate = action.activate(); + + const options = await action.getGroupByOptions(); + expect(options).toHaveLength(2); + expect(options).toEqual([ + { label: 'field1', value: 'field1' }, + { label: 'field3', value: 'field3' }, + ]); + + deactivate(); + }); + }); }); + +function buildTestScene() { + const variable = new GroupByVariable({ + name: 'A', + label: 'A', + description: 'A', + type: 'groupby', + value: 'Text', + }); + + const action = new PanelGroupByAction(); + + const panel = new VizPanel({ + title: 'Panel A', + pluginId: 'table', + key: 'panel-12', + headerActions: [action], + $data: new SceneQueryRunner({ + datasource: { uid: 'my-uid' }, + queries: [{ query: 'QueryA', refId: 'A' }], + }), + }); + + const dashboard = new DashboardScene({ + uid: 'A', + $variables: new SceneVariableSet({ + variables: [variable], + }), + body: DefaultGridLayoutManager.fromVizPanels([panel]), + }); + + return { action, panel, dashboard, variable }; +} diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 50ddc6b897b..483bb724c72 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -10939,6 +10939,14 @@ "could-anything-matching-query": "Could not find anything matching your query" } }, + "panel-group-by": { + "apply": "Apply", + "button": "Group by", + "cancel": "Cancel", + "loading": "Loading options...", + "no-options": "No options found", + "search-placeholder": "Search..." + }, "panel-type-filter": { "clear-button": "Clear types", "select-aria-label": "Panel type filter",