diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AggregationField.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AggregationField.test.tsx new file mode 100644 index 00000000000..c4fbc01027e --- /dev/null +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AggregationField.test.tsx @@ -0,0 +1,45 @@ +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import createMockDatasource from '../../__mocks__/datasource'; +import createMockQuery from '../../__mocks__/query'; +import { AzureMonitorOption } from '../../types'; + +import AggregationField from './AggregationField'; + +const props = { + aggregationOptions: [], + query: createMockQuery(), + datasource: createMockDatasource(), + variableOptionGroup: { label: 'Templates', options: [] }, + onQueryChange: jest.fn(), + setError: jest.fn(), + isLoading: false, +}; + +describe('AggregationField', () => { + it('should render the current value', async () => { + const aggregationOptions: AzureMonitorOption[] = [{ label: 'foo', value: 'foo' }]; + const query = { + ...props.query, + azureMonitor: { + aggregation: 'foo', + }, + }; + render(); + expect(screen.queryByText('foo')).toBeInTheDocument(); + }); + + it('should render the current value even if it is not in the list of options', async () => { + const aggregationOptions: AzureMonitorOption[] = [{ label: 'foo', value: 'foo' }]; + const query = { + ...props.query, + azureMonitor: { + aggregation: 'bar', + }, + }; + render(); + expect(screen.queryByText('bar')).toBeInTheDocument(); + expect(screen.queryByText('foo')).not.toBeInTheDocument(); + }); +}); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AggregationField.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AggregationField.tsx index 854b7fe03af..1407bdd60f2 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AggregationField.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/AggregationField.tsx @@ -1,9 +1,10 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback } from 'react'; import { SelectableValue } from '@grafana/data'; import { Select } from '@grafana/ui'; import { AzureQueryEditorFieldProps, AzureMonitorOption } from '../../types'; +import { addValueToOptions } from '../../utils/common'; import { Field } from '../Field'; import { setAggregation } from './setQueryValue'; @@ -32,10 +33,7 @@ const AggregationField: React.FC = ({ [onQueryChange, query] ); - const options = useMemo( - () => [...aggregationOptions, variableOptionGroup], - [aggregationOptions, variableOptionGroup] - ); + const options = addValueToOptions(aggregationOptions, variableOptionGroup, query.azureMonitor?.aggregation); return ( diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricNameField.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricNameField.test.tsx new file mode 100644 index 00000000000..aff65cef929 --- /dev/null +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricNameField.test.tsx @@ -0,0 +1,44 @@ +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import createMockDatasource from '../../__mocks__/datasource'; +import createMockQuery from '../../__mocks__/query'; +import { AzureMonitorOption } from '../../types'; + +import MetricNameField from './MetricNameField'; + +const props = { + metricNames: [], + query: createMockQuery(), + datasource: createMockDatasource(), + variableOptionGroup: { label: 'Templates', options: [] }, + onQueryChange: jest.fn(), + setError: jest.fn(), +}; + +describe('MetricNameField', () => { + it('should render the current value', async () => { + const metricNames: AzureMonitorOption[] = [{ label: 'foo', value: 'foo' }]; + const query = { + ...props.query, + azureMonitor: { + metricName: 'foo', + }, + }; + render(); + expect(screen.queryByText('foo')).toBeInTheDocument(); + }); + + it('should render the current value even if it is not in the list of options', async () => { + const metricNames: AzureMonitorOption[] = [{ label: 'foo', value: 'foo' }]; + const query = { + ...props.query, + azureMonitor: { + metricName: 'bar', + }, + }; + render(); + expect(screen.queryByText('bar')).toBeInTheDocument(); + expect(screen.queryByText('foo')).not.toBeInTheDocument(); + }); +}); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricNameField.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricNameField.tsx index 62106a63a81..4f73c551690 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricNameField.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricNameField.tsx @@ -1,10 +1,11 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback } from 'react'; import { SelectableValue } from '@grafana/data'; import { Select } from '@grafana/ui'; import { selectors } from '../../e2e/selectors'; import { AzureQueryEditorFieldProps, AzureMonitorOption } from '../../types'; +import { addValueToOptions } from '../../utils/common'; import { Field } from '../Field'; import { setMetricName } from './setQueryValue'; @@ -26,7 +27,7 @@ const MetricNameField: React.FC = ({ metricNames, query, variab [onQueryChange, query] ); - const options = useMemo(() => [...metricNames, variableOptionGroup], [metricNames, variableOptionGroup]); + const options = addValueToOptions(metricNames, variableOptionGroup, query.azureMonitor?.metricName); return ( diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricNamespaceField.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricNamespaceField.tsx index 124c20a6a3b..4f53d8a4c8e 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricNamespaceField.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/MetricNamespaceField.tsx @@ -1,9 +1,10 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback } from 'react'; import { SelectableValue } from '@grafana/data'; import { Select } from '@grafana/ui'; import { AzureQueryEditorFieldProps, AzureMonitorOption } from '../../types'; +import { addValueToOptions } from '../../utils/common'; import { Field } from '../Field'; import { setCustomNamespace } from './setQueryValue'; @@ -30,14 +31,8 @@ const MetricNamespaceField: React.FC = ({ [onQueryChange, query] ); - const options = useMemo(() => [...metricNamespaces, variableOptionGroup], [metricNamespaces, variableOptionGroup]); - const optionValues = metricNamespaces - .map((m) => m.value.toLowerCase()) - .concat(variableOptionGroup.options.map((p) => p.value)); const value = query.azureMonitor?.customNamespace || query.azureMonitor?.metricNamespace; - if (value && !optionValues.includes(value.toLowerCase())) { - options.push({ label: value, value }); - } + const options = addValueToOptions(metricNamespaces, variableOptionGroup, value); return ( diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/TimeGrainField.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/TimeGrainField.test.tsx new file mode 100644 index 00000000000..e1c0aa5bf00 --- /dev/null +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/TimeGrainField.test.tsx @@ -0,0 +1,45 @@ +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import createMockDatasource from '../../__mocks__/datasource'; +import createMockQuery from '../../__mocks__/query'; +import { AzureMonitorOption } from '../../types'; + +import TimeGrainField from './TimeGrainField'; + +const props = { + timeGrainOptions: [], + query: createMockQuery(), + datasource: createMockDatasource(), + variableOptionGroup: { label: 'Templates', options: [] }, + onQueryChange: jest.fn(), + setError: jest.fn(), + isLoading: false, +}; + +describe('TimeGrainField', () => { + it('should render the current value', async () => { + const timeGrainOptions: AzureMonitorOption[] = [{ label: '15m', value: '15m' }]; + const query = { + ...props.query, + azureMonitor: { + timeGrain: '15m', + }, + }; + render(); + expect(screen.queryByText('15m')).toBeInTheDocument(); + }); + + it('should render the current value even if it is not in the list of options', async () => { + const timeGrainOptions: AzureMonitorOption[] = [{ label: '15m', value: '15m' }]; + const query = { + ...props.query, + azureMonitor: { + timeGrain: '1h', + }, + }; + render(); + expect(screen.queryByText('1h')).toBeInTheDocument(); + expect(screen.queryByText('15m')).not.toBeInTheDocument(); + }); +}); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/TimeGrainField.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/TimeGrainField.tsx index 7d70eff174a..d39de88c99c 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/TimeGrainField.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/MetricsQueryEditor/TimeGrainField.tsx @@ -5,6 +5,7 @@ import { Select } from '@grafana/ui'; import TimegrainConverter from '../../time_grain_converter'; import { AzureQueryEditorFieldProps, AzureMonitorOption } from '../../types'; +import { addValueToOptions } from '../../utils/common'; import { Field } from '../Field'; import { setTimeGrain } from './setQueryValue'; @@ -48,8 +49,10 @@ const TimeGrainField: React.FC = ({ const baseTimeGrains = timeGrainOptions.map((v) => (v.value === 'auto' ? { ...v, description: autoInterval } : v)); - return [...baseTimeGrains, variableOptionGroup]; - }, [timeGrainOptions, variableOptionGroup]); + const options = addValueToOptions(baseTimeGrains, variableOptionGroup, query.azureMonitor?.timeGrain); + + return options; + }, [timeGrainOptions, variableOptionGroup, query.azureMonitor?.timeGrain]); return ( diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/types/types.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/types/types.ts index 04879f9c8e4..e3757299436 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/types/types.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/types/types.ts @@ -158,12 +158,14 @@ export interface AzureMonitorOption { options?: AzureMonitorOption[]; } +export type VariableOptionGroup = { label: string; options: AzureMonitorOption[] }; + export interface AzureQueryEditorFieldProps { data?: PanelData; query: AzureMonitorQuery; datasource: Datasource; subscriptionId?: string; - variableOptionGroup: { label: string; options: AzureMonitorOption[] }; + variableOptionGroup: VariableOptionGroup; onQueryChange: (newQuery: AzureMonitorQuery) => void; setError: (source: string, error: AzureMonitorErrorish | undefined) => void; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/common.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/common.ts index 4a89f1e2ab2..59e6f1d9b27 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/common.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/common.ts @@ -4,7 +4,7 @@ import { rangeUtil } from '@grafana/data'; import { VariableWithMultiSupport } from 'app/features/variables/types'; import TimegrainConverter from '../time_grain_converter'; -import { AzureMonitorOption } from '../types'; +import { AzureMonitorOption, VariableOptionGroup } from '../types'; export const hasOption = (options: AzureMonitorOption[], value: string): boolean => options.some((v) => (v.options ? hasOption(v.options, value) : v.value === value)); @@ -22,6 +22,21 @@ export const findOptions = (options: AzureMonitorOption[], values: string[] = [] export const toOption = (v: { text: string; value: string }) => ({ value: v.value, label: v.text }); +export const addValueToOptions = ( + values: AzureMonitorOption[], + variableOptionGroup: VariableOptionGroup, + value?: string +) => { + const options = [...values, variableOptionGroup]; + + const optionValues = values.map((m) => m.value.toLowerCase()).concat(variableOptionGroup.options.map((p) => p.value)); + if (value && !optionValues.includes(value.toLowerCase())) { + options.push({ label: value, value }); + } + + return options; +}; + export function convertTimeGrainsToMs(timeGrains: T[]) { const allowedTimeGrainsMs: number[] = []; timeGrains.forEach((tg: any) => {