mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DashboardQueryEditor: Refactor and convert to functional component (#45002)
* DashboardQueryEditor: Refactor and convert to functional component
This commit is contained in:
parent
ef11e783f1
commit
b615d0558b
@ -22,7 +22,7 @@ jest.mock('app/core/config', () => ({
|
||||
|
||||
jest.mock('app/features/plugins/datasource_srv', () => ({
|
||||
getDatasourceSrv: () => ({
|
||||
get: () => ({}),
|
||||
get: () => Promise.resolve({}),
|
||||
getInstanceSettings: () => ({}),
|
||||
}),
|
||||
}));
|
||||
@ -67,7 +67,7 @@ describe('DashboardQueryEditor', () => {
|
||||
jest.spyOn(getDashboardSrv(), 'getCurrent').mockImplementation(() => mockDashboard);
|
||||
});
|
||||
|
||||
it('does not show a panel with the SHARED_DASHBOARD_QUERY datasource as an option in the dropdown', () => {
|
||||
it('does not show a panel with the SHARED_DASHBOARD_QUERY datasource as an option in the dropdown', async () => {
|
||||
render(
|
||||
<DashboardQueryEditor
|
||||
queries={mockQueries}
|
||||
@ -77,13 +77,19 @@ describe('DashboardQueryEditor', () => {
|
||||
/>
|
||||
);
|
||||
const select = screen.getByText('Choose panel');
|
||||
|
||||
userEvent.click(select);
|
||||
expect(screen.getByText('My first panel')).toBeInTheDocument();
|
||||
expect(screen.getByText('Another panel')).toBeInTheDocument();
|
||||
|
||||
const myFirstPanel = await screen.findByText('My first panel');
|
||||
expect(myFirstPanel).toBeInTheDocument();
|
||||
|
||||
const anotherPanel = await screen.findByText('Another panel');
|
||||
expect(anotherPanel).toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByText('A dashboard query panel')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show the current panelInEdit as an option in the dropdown', () => {
|
||||
it('does not show the current panelInEdit as an option in the dropdown', async () => {
|
||||
mockDashboard.initEditPanel(mockDashboard.panels[0]);
|
||||
render(
|
||||
<DashboardQueryEditor
|
||||
@ -94,9 +100,14 @@ describe('DashboardQueryEditor', () => {
|
||||
/>
|
||||
);
|
||||
const select = screen.getByText('Choose panel');
|
||||
|
||||
userEvent.click(select);
|
||||
|
||||
expect(screen.queryByText('My first panel')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Another panel')).toBeInTheDocument();
|
||||
|
||||
const anotherPanel = await screen.findByText('Another panel');
|
||||
expect(anotherPanel).toBeInTheDocument();
|
||||
|
||||
expect(screen.queryByText('A dashboard query panel')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -1,9 +1,12 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { LegacyForms, VerticalGroup } from '@grafana/ui';
|
||||
import { DataQuery, PanelData, SelectableValue } from '@grafana/data';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import pluralize from 'pluralize';
|
||||
import { useId } from '@react-aria/utils';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { InlineField, Select, useStyles2, VerticalGroup } from '@grafana/ui';
|
||||
import { DataQuery, GrafanaTheme2, PanelData, SelectableValue } from '@grafana/data';
|
||||
|
||||
import { DashboardQuery, ResultInfo, SHARED_DASHBOARD_QUERY } from './types';
|
||||
import config from 'app/core/config';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import { PanelModel } from 'app/features/dashboard/state';
|
||||
@ -11,7 +14,7 @@ import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { filterPanelDataToQuery } from 'app/features/query/components/QueryEditorRow';
|
||||
import { DashboardQueryRow } from './DashboardQueryRow';
|
||||
|
||||
const { Select } = LegacyForms;
|
||||
import { DashboardQuery, ResultInfo, SHARED_DASHBOARD_QUERY } from './types';
|
||||
|
||||
function getQueryDisplayText(query: DataQuery): string {
|
||||
return JSON.stringify(query);
|
||||
@ -24,166 +27,136 @@ interface Props {
|
||||
onRunQueries: () => void;
|
||||
}
|
||||
|
||||
type State = {
|
||||
defaultDatasource: string;
|
||||
results: ResultInfo[];
|
||||
};
|
||||
|
||||
export class DashboardQueryEditor extends PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
defaultDatasource: '',
|
||||
results: [],
|
||||
};
|
||||
}
|
||||
|
||||
getQuery(): DashboardQuery {
|
||||
return this.props.queries[0] as DashboardQuery;
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
await this.updateState();
|
||||
}
|
||||
|
||||
async componentDidUpdate(prevProps: Props) {
|
||||
const { panelData, queries } = this.props;
|
||||
|
||||
if (prevProps.panelData !== panelData || prevProps.queries !== queries) {
|
||||
await this.updateState();
|
||||
}
|
||||
}
|
||||
|
||||
async updateState() {
|
||||
const { panelData, queries } = this.props;
|
||||
|
||||
export function DashboardQueryEditor({ panelData, queries, onChange, onRunQueries }: Props) {
|
||||
const { value: defaultDatasource } = useAsync(() => getDatasourceSrv().get());
|
||||
const { value: results, loading: loadingResults } = useAsync(async (): Promise<ResultInfo[]> => {
|
||||
const query = queries[0] as DashboardQuery;
|
||||
const defaultDS = await getDatasourceSrv().get();
|
||||
const dashboard = getDashboardSrv().getCurrent();
|
||||
const panel = dashboard?.getPanelById(query.panelId ?? -124134);
|
||||
|
||||
if (!panel) {
|
||||
this.setState({ defaultDatasource: defaultDS.name });
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
const mainDS = await getDatasourceSrv().get(panel.datasource);
|
||||
const info: ResultInfo[] = [];
|
||||
return Promise.all(
|
||||
panel.targets.map(async (query) => {
|
||||
const ds = query.datasource ? await getDatasourceSrv().get(query.datasource) : mainDS;
|
||||
const fmt = ds.getQueryDisplayText || getQueryDisplayText;
|
||||
|
||||
for (const query of panel.targets) {
|
||||
const ds = query.datasource ? await getDatasourceSrv().get(query.datasource) : mainDS;
|
||||
const fmt = ds.getQueryDisplayText ? ds.getQueryDisplayText : getQueryDisplayText;
|
||||
const queryData = filterPanelDataToQuery(panelData, query.refId) ?? panelData;
|
||||
|
||||
const qData = filterPanelDataToQuery(panelData, query.refId);
|
||||
const queryData = qData ? qData : panelData;
|
||||
|
||||
info.push({
|
||||
refId: query.refId,
|
||||
query: fmt(query),
|
||||
img: ds.meta.info.logos.small,
|
||||
data: queryData.series,
|
||||
error: queryData.error,
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ defaultDatasource: defaultDS.name, results: info });
|
||||
}
|
||||
|
||||
onPanelChanged = (id: number) => {
|
||||
const query = this.getQuery();
|
||||
|
||||
this.props.onChange([
|
||||
{
|
||||
...query,
|
||||
panelId: id,
|
||||
} as DashboardQuery,
|
||||
]);
|
||||
this.props.onRunQueries();
|
||||
};
|
||||
|
||||
renderQueryData(editURL: string) {
|
||||
const { results } = this.state;
|
||||
|
||||
return (
|
||||
<VerticalGroup spacing="sm">
|
||||
{results.map((target, index) => {
|
||||
return <DashboardQueryRow editURL={editURL} target={target} key={`DashboardQueryRow-${index}`} />;
|
||||
})}
|
||||
</VerticalGroup>
|
||||
);
|
||||
}
|
||||
|
||||
getPanelDescription = (panel: PanelModel): string => {
|
||||
const { defaultDatasource } = this.state;
|
||||
const datasource = panel.datasource ? panel.datasource : defaultDatasource;
|
||||
const dsname = getDatasourceSrv().getInstanceSettings(datasource)?.name;
|
||||
|
||||
if (panel.targets.length === 1) {
|
||||
return '1 query to ' + dsname;
|
||||
}
|
||||
|
||||
return panel.targets.length + ' queries to ' + dsname;
|
||||
};
|
||||
|
||||
render() {
|
||||
const dashboard = getDashboardSrv().getCurrent();
|
||||
if (!dashboard) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const query = this.getQuery();
|
||||
|
||||
let selected: SelectableValue<number> | undefined;
|
||||
const panels: Array<SelectableValue<number>> = [];
|
||||
|
||||
for (const panel of dashboard.panels) {
|
||||
const plugin = config.panels[panel.type];
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (panel.targets && panel.id !== dashboard.panelInEdit?.id && panel.datasource?.uid !== SHARED_DASHBOARD_QUERY) {
|
||||
const item = {
|
||||
value: panel.id,
|
||||
label: panel.title ? panel.title : 'Panel ' + panel.id,
|
||||
description: this.getPanelDescription(panel),
|
||||
imgUrl: plugin.info.logos.small,
|
||||
return {
|
||||
refId: query.refId,
|
||||
query: fmt(query),
|
||||
img: ds.meta.info.logos.small,
|
||||
data: queryData.series,
|
||||
error: queryData.error,
|
||||
};
|
||||
})
|
||||
);
|
||||
}, [panelData, queries]);
|
||||
|
||||
panels.push(item);
|
||||
const query = queries[0] as DashboardQuery;
|
||||
|
||||
if (query.panelId === panel.id) {
|
||||
selected = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
const onPanelChanged = useCallback(
|
||||
(id: number) => {
|
||||
onChange([
|
||||
{
|
||||
...query,
|
||||
panelId: id,
|
||||
} as DashboardQuery,
|
||||
]);
|
||||
onRunQueries();
|
||||
},
|
||||
[query, onChange, onRunQueries]
|
||||
);
|
||||
|
||||
if (panels.length < 1) {
|
||||
return (
|
||||
<div className={css({ padding: '10px' })}>
|
||||
This dashboard does not have other panels. Add queries to other panels and try again
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const getPanelDescription = useCallback(
|
||||
(panel: PanelModel): string => {
|
||||
const datasource = panel.datasource ?? defaultDatasource;
|
||||
const dsname = getDatasourceSrv().getInstanceSettings(datasource)?.name;
|
||||
const queryCount = panel.targets.length;
|
||||
return `${queryCount} ${pluralize('query', queryCount)} to ${dsname}`;
|
||||
},
|
||||
[defaultDatasource]
|
||||
);
|
||||
|
||||
// Same as current URL, but different panelId
|
||||
const editURL = `d/${dashboard.uid}/${dashboard.title}?&editPanel=${query.panelId}`;
|
||||
const dashboard = getDashboardSrv().getCurrent();
|
||||
const panels: Array<SelectableValue<number>> = useMemo(
|
||||
() =>
|
||||
dashboard?.panels
|
||||
.filter(
|
||||
(panel) =>
|
||||
config.panels[panel.type] &&
|
||||
panel.targets &&
|
||||
panel.id !== dashboard.panelInEdit?.id &&
|
||||
panel.datasource?.uid !== SHARED_DASHBOARD_QUERY
|
||||
)
|
||||
.map((panel) => ({
|
||||
value: panel.id,
|
||||
label: panel.title ?? 'Panel ' + panel.id,
|
||||
description: getPanelDescription(panel),
|
||||
imgUrl: config.panels[panel.type].info.logos.small,
|
||||
})) ?? [],
|
||||
[dashboard, getPanelDescription]
|
||||
);
|
||||
|
||||
const styles = useStyles2(getStyles);
|
||||
const selectId = useId();
|
||||
|
||||
if (!dashboard) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (panels.length < 1) {
|
||||
return (
|
||||
<div>
|
||||
<div className="gf-form">
|
||||
<div className="gf-form-label">Use results from panel</div>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
placeholder="Choose panel"
|
||||
isSearchable={true}
|
||||
options={panels}
|
||||
value={selected}
|
||||
onChange={(item) => this.onPanelChanged(item.value!)}
|
||||
/>
|
||||
</div>
|
||||
<div className={css({ padding: '16px' })}>{query.panelId && this.renderQueryData(editURL)}</div>
|
||||
</div>
|
||||
<p className={styles.noQueriesText}>
|
||||
This dashboard does not have any other panels. Add queries to other panels and try again.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
const selected = panels.find((panel) => panel.value === query.panelId);
|
||||
// Same as current URL, but different panelId
|
||||
const editURL = `d/${dashboard.uid}/${dashboard.title}?&editPanel=${query.panelId}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<InlineField label="Use results from panel" grow>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
inputId={selectId}
|
||||
placeholder="Choose panel"
|
||||
isSearchable={true}
|
||||
options={panels}
|
||||
value={selected}
|
||||
onChange={(item) => onPanelChanged(item.value!)}
|
||||
/>
|
||||
</InlineField>
|
||||
|
||||
{results && !loadingResults && (
|
||||
<div className={styles.results}>
|
||||
{query.panelId && (
|
||||
<VerticalGroup spacing="sm">
|
||||
{results.map((target, i) => (
|
||||
<DashboardQueryRow editURL={editURL} target={target} key={`DashboardQueryRow-${i}`} />
|
||||
))}
|
||||
</VerticalGroup>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
results: css({
|
||||
padding: theme.spacing(2),
|
||||
}),
|
||||
noQueriesText: css({
|
||||
padding: theme.spacing(1.25),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user