mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboards: Show repeated row with Dashboard ds warning (#73787)
This commit is contained in:
parent
5bd58cac57
commit
36d7cc9384
@ -180,6 +180,13 @@ export const Pages = {
|
||||
Annotations: {
|
||||
marker: 'data-testid annotation-marker',
|
||||
},
|
||||
Rows: {
|
||||
Repeated: {
|
||||
ConfigSection: {
|
||||
warningMessage: 'data-testid Repeated rows warning message',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dashboards: {
|
||||
url: '/dashboards',
|
||||
|
@ -2,6 +2,8 @@ import { screen, render } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
|
||||
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard/types';
|
||||
|
||||
import { PanelModel } from '../../state/PanelModel';
|
||||
|
||||
import { DashboardRow } from './DashboardRow';
|
||||
@ -17,6 +19,7 @@ describe('DashboardRow', () => {
|
||||
canEdit: true,
|
||||
},
|
||||
events: { subscribe: jest.fn() },
|
||||
getRowPanels: () => [],
|
||||
};
|
||||
|
||||
panel = new PanelModel({ collapsed: false });
|
||||
@ -67,4 +70,28 @@ describe('DashboardRow', () => {
|
||||
expect(screen.queryByRole('button', { name: 'Delete row' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Row options' })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should return warning message when row panel has a panel with dashboard ds set', async () => {
|
||||
const panel = new PanelModel({
|
||||
datasource: {
|
||||
type: 'datasource',
|
||||
uid: SHARED_DASHBOARD_QUERY,
|
||||
},
|
||||
});
|
||||
const rowPanel = new PanelModel({ collapsed: true, panels: [panel] });
|
||||
const dashboardRow = new DashboardRow({ panel: rowPanel, dashboard: dashboardMock });
|
||||
expect(dashboardRow.getWarning()).toBeDefined();
|
||||
});
|
||||
|
||||
it('Should not return warning message when row panel does not have a panel with dashboard ds set', async () => {
|
||||
const panel = new PanelModel({
|
||||
datasource: {
|
||||
type: 'datasource',
|
||||
uid: 'ds-uid',
|
||||
},
|
||||
});
|
||||
const rowPanel = new PanelModel({ collapsed: true, panels: [panel] });
|
||||
const dashboardRow = new DashboardRow({ panel: rowPanel, dashboard: dashboardMock });
|
||||
expect(dashboardRow.getWarning()).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
import classNames from 'classnames';
|
||||
import { indexOf } from 'lodash';
|
||||
import React from 'react';
|
||||
import { Unsubscribable } from 'rxjs';
|
||||
|
||||
@ -6,6 +7,7 @@ import { selectors } from '@grafana/e2e-selectors';
|
||||
import { getTemplateSrv, RefreshEvent } from '@grafana/runtime';
|
||||
import { Icon } from '@grafana/ui';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard/types';
|
||||
|
||||
import { ShowConfirmModalEvent } from '../../../../types/events';
|
||||
import { DashboardModel } from '../../state/DashboardModel';
|
||||
@ -38,6 +40,23 @@ export class DashboardRow extends React.Component<DashboardRowProps> {
|
||||
this.props.dashboard.toggleRow(this.props.panel);
|
||||
};
|
||||
|
||||
getWarning = () => {
|
||||
const panels = !!this.props.panel.panels?.length
|
||||
? this.props.panel.panels
|
||||
: this.props.dashboard.getRowPanels(indexOf(this.props.dashboard.panels, this.props.panel));
|
||||
const isAnyPanelUsingDashboardDS = panels.some((p) => p.datasource?.uid === SHARED_DASHBOARD_QUERY);
|
||||
if (isAnyPanelUsingDashboardDS) {
|
||||
return (
|
||||
<p>
|
||||
Panels in this row use the {SHARED_DASHBOARD_QUERY} data source. These panels will reference the panel in the
|
||||
original row, not the ones in the repeated rows.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
onUpdate = (title: string, repeat?: string | null) => {
|
||||
this.props.panel.setProperty('title', title);
|
||||
this.props.panel.setProperty('repeat', repeat ?? undefined);
|
||||
@ -94,6 +113,7 @@ export class DashboardRow extends React.Component<DashboardRowProps> {
|
||||
title={this.props.panel.title}
|
||||
repeat={this.props.panel.repeat}
|
||||
onUpdate={this.onUpdate}
|
||||
warning={this.getWarning()}
|
||||
/>
|
||||
<button type="button" className="pointer" onClick={this.onDelete} aria-label="Delete row">
|
||||
<Icon name="trash-alt" />
|
||||
|
@ -9,9 +9,10 @@ export interface RowOptionsButtonProps {
|
||||
title: string;
|
||||
repeat?: string | null;
|
||||
onUpdate: OnRowOptionsUpdate;
|
||||
warning?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const RowOptionsButton = ({ repeat, title, onUpdate }: RowOptionsButtonProps) => {
|
||||
export const RowOptionsButton = ({ repeat, title, onUpdate, warning }: RowOptionsButtonProps) => {
|
||||
const onUpdateChange = (hideModal: () => void) => (title: string, repeat?: string | null) => {
|
||||
onUpdate(title, repeat);
|
||||
hideModal();
|
||||
@ -26,7 +27,13 @@ export const RowOptionsButton = ({ repeat, title, onUpdate }: RowOptionsButtonPr
|
||||
className="pointer"
|
||||
aria-label="Row options"
|
||||
onClick={() => {
|
||||
showModal(RowOptionsModal, { title, repeat, onDismiss: hideModal, onUpdate: onUpdateChange(hideModal) });
|
||||
showModal(RowOptionsModal, {
|
||||
title,
|
||||
repeat,
|
||||
onDismiss: hideModal,
|
||||
onUpdate: onUpdateChange(hideModal),
|
||||
warning,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Icon name="cog" />
|
||||
|
@ -0,0 +1,34 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { RowOptionsForm } from './RowOptionsForm';
|
||||
|
||||
jest.mock('../RepeatRowSelect/RepeatRowSelect', () => ({
|
||||
RepeatRowSelect: () => <div />,
|
||||
}));
|
||||
describe('DashboardRow', () => {
|
||||
it('Should show warning component when has warningMessage prop', () => {
|
||||
render(
|
||||
<TestProvider>
|
||||
<RowOptionsForm repeat={'3'} title="" onCancel={jest.fn()} onUpdate={jest.fn()} warning="a warning message" />
|
||||
</TestProvider>
|
||||
);
|
||||
expect(
|
||||
screen.getByTestId(selectors.pages.Dashboard.Rows.Repeated.ConfigSection.warningMessage)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should not show warning component when does not have warningMessage prop', () => {
|
||||
render(
|
||||
<TestProvider>
|
||||
<RowOptionsForm repeat={'3'} title="" onCancel={jest.fn()} onUpdate={jest.fn()} />
|
||||
</TestProvider>
|
||||
);
|
||||
expect(
|
||||
screen.queryByTestId(selectors.pages.Dashboard.Rows.Repeated.ConfigSection.warningMessage)
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -1,6 +1,7 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { Button, Field, Form, Modal, Input } from '@grafana/ui';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Button, Field, Form, Modal, Input, Alert } from '@grafana/ui';
|
||||
|
||||
import { RepeatRowSelect } from '../RepeatRowSelect/RepeatRowSelect';
|
||||
|
||||
@ -11,9 +12,10 @@ export interface Props {
|
||||
repeat?: string | null;
|
||||
onUpdate: OnRowOptionsUpdate;
|
||||
onCancel: () => void;
|
||||
warning?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const RowOptionsForm = ({ repeat, title, onUpdate, onCancel }: Props) => {
|
||||
export const RowOptionsForm = ({ repeat, title, warning, onUpdate, onCancel }: Props) => {
|
||||
const [newRepeat, setNewRepeat] = useState<string | null | undefined>(repeat);
|
||||
const onChangeRepeat = useCallback((name?: string | null) => setNewRepeat(name), [setNewRepeat]);
|
||||
|
||||
@ -29,11 +31,20 @@ export const RowOptionsForm = ({ repeat, title, onUpdate, onCancel }: Props) =>
|
||||
<Field label="Title">
|
||||
<Input {...register('title')} type="text" />
|
||||
</Field>
|
||||
|
||||
<Field label="Repeat for">
|
||||
<RepeatRowSelect repeat={newRepeat} onChange={onChangeRepeat} />
|
||||
</Field>
|
||||
|
||||
{warning && (
|
||||
<Alert
|
||||
data-testid={selectors.pages.Dashboard.Rows.Repeated.ConfigSection.warningMessage}
|
||||
severity="warning"
|
||||
title=""
|
||||
topSpacing={3}
|
||||
bottomSpacing={0}
|
||||
>
|
||||
{warning}
|
||||
</Alert>
|
||||
)}
|
||||
<Modal.ButtonRow>
|
||||
<Button type="button" variant="secondary" onClick={onCancel} fill="outline">
|
||||
Cancel
|
||||
|
@ -8,15 +8,16 @@ import { OnRowOptionsUpdate, RowOptionsForm } from './RowOptionsForm';
|
||||
export interface RowOptionsModalProps {
|
||||
title: string;
|
||||
repeat?: string | null;
|
||||
warning?: React.ReactNode;
|
||||
onDismiss: () => void;
|
||||
onUpdate: OnRowOptionsUpdate;
|
||||
}
|
||||
|
||||
export const RowOptionsModal = ({ repeat, title, onDismiss, onUpdate }: RowOptionsModalProps) => {
|
||||
export const RowOptionsModal = ({ repeat, title, onDismiss, onUpdate, warning }: RowOptionsModalProps) => {
|
||||
const styles = getStyles();
|
||||
return (
|
||||
<Modal isOpen={true} title="Row options" icon="copy" onDismiss={onDismiss} className={styles.modal}>
|
||||
<RowOptionsForm repeat={repeat} title={title} onCancel={onDismiss} onUpdate={onUpdate} />
|
||||
<RowOptionsForm repeat={repeat} title={title} onCancel={onDismiss} onUpdate={onUpdate} warning={warning} />
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user