mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DashboardScene: Rerender dashboard links on timerange change (#94570)
* fix * refactor * refactor
This commit is contained in:
parent
ef805f271e
commit
aaba5a43bd
@ -1,36 +1,22 @@
|
||||
import { act, render } from '@testing-library/react';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { toUtc } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { SceneDataLayerControls, SceneVariableSet, TextBoxVariable, VariableValueSelectors } from '@grafana/scenes';
|
||||
|
||||
import { DashboardControls, DashboardControlsState } from './DashboardControls';
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
|
||||
const mockGetAnchorInfo = jest.fn((link) => ({
|
||||
href: `/dashboard/${link.title}`,
|
||||
title: link.title,
|
||||
tooltip: link.tooltip || null,
|
||||
}));
|
||||
|
||||
// Mock the getLinkSrv function
|
||||
jest.mock('app/features/panel/panellinks/link_srv', () => ({
|
||||
getLinkSrv: jest.fn(() => ({
|
||||
getAnchorInfo: mockGetAnchorInfo,
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('DashboardControls', () => {
|
||||
describe('Given a standard scene', () => {
|
||||
it('should initialize with default values', () => {
|
||||
const { controls: scene } = buildTestScene();
|
||||
const scene = buildTestScene();
|
||||
expect(scene.state.variableControls).toEqual([]);
|
||||
expect(scene.state.timePicker).toBeDefined();
|
||||
expect(scene.state.refreshPicker).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return if time controls are hidden', () => {
|
||||
const { controls: scene } = buildTestScene({
|
||||
const scene = buildTestScene({
|
||||
hideTimeControls: false,
|
||||
hideVariableControls: false,
|
||||
hideLinksControls: false,
|
||||
@ -45,14 +31,14 @@ describe('DashboardControls', () => {
|
||||
|
||||
describe('Component', () => {
|
||||
it('should render', () => {
|
||||
const { controls: scene } = buildTestScene();
|
||||
const scene = buildTestScene();
|
||||
expect(() => {
|
||||
render(<scene.Component model={scene} />);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should render visible controls', async () => {
|
||||
const { controls: scene } = buildTestScene({
|
||||
const scene = buildTestScene({
|
||||
variableControls: [new VariableValueSelectors({}), new SceneDataLayerControls()],
|
||||
});
|
||||
const renderer = render(<scene.Component model={scene} />);
|
||||
@ -65,7 +51,7 @@ describe('DashboardControls', () => {
|
||||
});
|
||||
|
||||
it('should render with hidden controls', async () => {
|
||||
const { controls: scene } = buildTestScene({
|
||||
const scene = buildTestScene({
|
||||
hideTimeControls: true,
|
||||
hideVariableControls: true,
|
||||
hideLinksControls: true,
|
||||
@ -79,13 +65,13 @@ describe('DashboardControls', () => {
|
||||
|
||||
describe('UrlSync', () => {
|
||||
it('should return keys', () => {
|
||||
const { controls: scene } = buildTestScene();
|
||||
const scene = buildTestScene();
|
||||
// @ts-expect-error
|
||||
expect(scene._urlSync.getKeys()).toEqual(['_dash.hideTimePicker', '_dash.hideVariables', '_dash.hideLinks']);
|
||||
});
|
||||
|
||||
it('should not return url state for hide flags', () => {
|
||||
const { controls: scene } = buildTestScene();
|
||||
const scene = buildTestScene();
|
||||
expect(scene.getUrlState()).toEqual({});
|
||||
scene.setState({
|
||||
hideTimeControls: true,
|
||||
@ -96,7 +82,7 @@ describe('DashboardControls', () => {
|
||||
});
|
||||
|
||||
it('should update from url', () => {
|
||||
const { controls: scene } = buildTestScene();
|
||||
const scene = buildTestScene();
|
||||
scene.updateFromUrl({
|
||||
'_dash.hideTimePicker': 'true',
|
||||
'_dash.hideVariables': 'true',
|
||||
@ -116,7 +102,7 @@ describe('DashboardControls', () => {
|
||||
});
|
||||
|
||||
it('should not override state if no new state comes from url', () => {
|
||||
const { controls: scene } = buildTestScene({
|
||||
const scene = buildTestScene({
|
||||
hideTimeControls: true,
|
||||
hideVariableControls: true,
|
||||
hideLinksControls: true,
|
||||
@ -128,7 +114,7 @@ describe('DashboardControls', () => {
|
||||
});
|
||||
|
||||
it('should not call setState if no changes', () => {
|
||||
const { controls: scene } = buildTestScene({
|
||||
const scene = buildTestScene({
|
||||
hideTimeControls: true,
|
||||
hideVariableControls: true,
|
||||
hideLinksControls: true,
|
||||
@ -144,34 +130,9 @@ describe('DashboardControls', () => {
|
||||
expect(setState).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('Should update link hrefs when time range changes', () => {
|
||||
const { controls, dashboard } = buildTestScene();
|
||||
render(<controls.Component model={controls} />);
|
||||
|
||||
//clear initial calls to getAnchorInfo
|
||||
mockGetAnchorInfo.mockClear();
|
||||
|
||||
act(() => {
|
||||
// Update time range
|
||||
dashboard.state.$timeRange?.setState({
|
||||
value: {
|
||||
from: toUtc('2021-01-01'),
|
||||
to: toUtc('2021-01-02'),
|
||||
raw: { from: toUtc('2020-01-01'), to: toUtc('2020-01-02') },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
//expect getAnchorInfo to be called after time range change
|
||||
expect(mockGetAnchorInfo).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
function buildTestScene(state?: Partial<DashboardControlsState>): {
|
||||
dashboard: DashboardScene;
|
||||
controls: DashboardControls;
|
||||
} {
|
||||
function buildTestScene(state?: Partial<DashboardControlsState>): DashboardControls {
|
||||
const variable = new TextBoxVariable({
|
||||
name: 'A',
|
||||
label: 'A',
|
||||
@ -206,5 +167,5 @@ function buildTestScene(state?: Partial<DashboardControlsState>): {
|
||||
dashboard.activate();
|
||||
variable.activate();
|
||||
|
||||
return { dashboard, controls: dashboard.state.controls as DashboardControls };
|
||||
return dashboard.state.controls as DashboardControls;
|
||||
}
|
||||
|
@ -122,10 +122,9 @@ function DashboardControlsRenderer({ model }: SceneComponentProps<DashboardContr
|
||||
const { variableControls, refreshPicker, timePicker, hideTimeControls, hideVariableControls, hideLinksControls } =
|
||||
model.useState();
|
||||
const dashboard = getDashboardSceneFor(model);
|
||||
const { links, editPanel, $timeRange } = dashboard.useState();
|
||||
const { links, editPanel } = dashboard.useState();
|
||||
const styles = useStyles2(getStyles);
|
||||
const showDebugger = location.search.includes('scene-debugger');
|
||||
$timeRange!.useState();
|
||||
|
||||
if (!model.hasControls()) {
|
||||
return null;
|
||||
@ -136,7 +135,7 @@ function DashboardControlsRenderer({ model }: SceneComponentProps<DashboardContr
|
||||
<Stack grow={1} wrap={'wrap'}>
|
||||
{!hideVariableControls && variableControls.map((c) => <c.Component model={c} key={c.state.key} />)}
|
||||
<Box grow={1} />
|
||||
{!hideLinksControls && !editPanel && <DashboardLinksControls links={links} uid={dashboard.state.uid} />}
|
||||
{!hideLinksControls && !editPanel && <DashboardLinksControls links={links} dashboard={dashboard} />}
|
||||
{editPanel && <PanelEditControls panelEditor={editPanel} />}
|
||||
</Stack>
|
||||
{!hideTimeControls && (
|
||||
|
@ -0,0 +1,100 @@
|
||||
import { act, render } from '@testing-library/react';
|
||||
|
||||
import { toUtc } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { SceneTimeRange } from '@grafana/scenes';
|
||||
|
||||
import { DashboardControls } from './DashboardControls';
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
|
||||
const mockGetAnchorInfo = jest.fn((link) => ({
|
||||
href: `/dashboard/${link.title}`,
|
||||
title: link.title,
|
||||
tooltip: link.tooltip || null,
|
||||
}));
|
||||
|
||||
// Mock the getLinkSrv function
|
||||
jest.mock('app/features/panel/panellinks/link_srv', () => ({
|
||||
getLinkSrv: jest.fn(() => ({
|
||||
getAnchorInfo: mockGetAnchorInfo,
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('DashboardLinksControls', () => {
|
||||
it('renders dashboard links correctly', () => {
|
||||
const { controls } = buildTestScene();
|
||||
const renderer = render(<controls.Component model={controls} />);
|
||||
|
||||
// // Expect two dashboard link containers to be rendered
|
||||
const linkContainers = renderer.getAllByTestId(selectors.components.DashboardLinks.container);
|
||||
expect(linkContainers).toHaveLength(2);
|
||||
|
||||
// Check link titles and hrefs
|
||||
const links = renderer.getAllByTestId(selectors.components.DashboardLinks.link);
|
||||
expect(links[0]).toHaveTextContent('Link 1');
|
||||
expect(links[1]).toHaveTextContent('Link 2');
|
||||
});
|
||||
|
||||
it('updates link hrefs when time range changes', () => {
|
||||
const { controls, dashboard } = buildTestScene();
|
||||
render(<controls.Component model={controls} />);
|
||||
|
||||
//clear initial calls to getAnchorInfo
|
||||
mockGetAnchorInfo.mockClear();
|
||||
|
||||
act(() => {
|
||||
// Update time range
|
||||
dashboard.state.$timeRange?.setState({
|
||||
value: {
|
||||
from: toUtc('2021-01-01'),
|
||||
to: toUtc('2021-01-02'),
|
||||
raw: { from: toUtc('2020-01-01'), to: toUtc('2020-01-02') },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
//expect getAnchorInfo to be called twice, once for each link, after time range change
|
||||
expect(mockGetAnchorInfo).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
function buildTestScene(): { controls: DashboardControls; dashboard: DashboardScene } {
|
||||
const dashboard = new DashboardScene({
|
||||
uid: 'A',
|
||||
links: [
|
||||
{
|
||||
title: 'Link 1',
|
||||
url: 'http://localhost:3000/$A',
|
||||
type: 'link',
|
||||
asDropdown: false,
|
||||
icon: '',
|
||||
includeVars: true,
|
||||
keepTime: true,
|
||||
tags: [],
|
||||
targetBlank: false,
|
||||
tooltip: 'Link 1',
|
||||
},
|
||||
{
|
||||
title: 'Link 2',
|
||||
url: 'http://localhost:3000/$A',
|
||||
type: 'link',
|
||||
asDropdown: false,
|
||||
icon: '',
|
||||
includeVars: true,
|
||||
keepTime: true,
|
||||
tags: [],
|
||||
targetBlank: false,
|
||||
tooltip: 'Link 2',
|
||||
},
|
||||
],
|
||||
controls: new DashboardControls({}),
|
||||
$timeRange: new SceneTimeRange({
|
||||
from: 'now-1',
|
||||
to: 'now',
|
||||
}),
|
||||
});
|
||||
|
||||
dashboard.activate();
|
||||
|
||||
return { controls: dashboard.state.controls as DashboardControls, dashboard };
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { sanitizeUrl } from '@grafana/data/src/text/sanitize';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { sceneGraph } from '@grafana/scenes';
|
||||
import { DashboardLink } from '@grafana/schema';
|
||||
import { Tooltip } from '@grafana/ui';
|
||||
import {
|
||||
@ -10,12 +11,17 @@ import { getLinkSrv } from 'app/features/panel/panellinks/link_srv';
|
||||
|
||||
import { LINK_ICON_MAP } from '../settings/links/utils';
|
||||
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
|
||||
export interface Props {
|
||||
links: DashboardLink[];
|
||||
uid?: string;
|
||||
dashboard: DashboardScene;
|
||||
}
|
||||
|
||||
export function DashboardLinksControls({ links, uid }: Props) {
|
||||
export function DashboardLinksControls({ links, dashboard }: Props) {
|
||||
sceneGraph.getTimeRange(dashboard).useState();
|
||||
const uid = dashboard.state.uid;
|
||||
|
||||
if (!links || !uid) {
|
||||
return null;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user