This commit is contained in:
Victor Marin
2025-11-05 16:17:26 +02:00
parent f116bb783f
commit a3fa0f07f9
2 changed files with 186 additions and 148 deletions
@@ -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 };
}
+8
View File
@@ -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",