AzureMonitor: add NewDimension component using experimental UI (#48946)

* AzureMonitor: add NewDimension component using experimental UI

This new component is exercised by the same unit test file as the current Dimension component.

Also cleans up a few unneeded `await` keywords in the Dimensions test file.

* AzureMonitor: make tweaks based on PR comments.

- I was importing the wrong Field component
- We can use a typeguard to avoid the strange `if`.
This commit is contained in:
Adam Simpson 2022-06-03 02:35:53 +00:00 committed by GitHub
parent 52ed651958
commit 53cb94a2ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 557 additions and 330 deletions

View File

@ -10,6 +10,7 @@ import createMockPanelData from '../../__mocks__/panelData';
import createMockQuery from '../../__mocks__/query';
import DimensionFields from './DimensionFields';
import NewDimensionFields from './NewDimensionFields';
import { appendDimensionFilter, setDimensionFilterValue } from './setQueryValue';
const variableOptionGroup = {
@ -18,342 +19,365 @@ const variableOptionGroup = {
};
const user = userEvent.setup();
describe('Azure Monitor QueryEditor', () => {
const mockDatasource = createMockDatasource();
const tests = [
{
component: DimensionFields,
label: 'Dimension Fields',
addDimension: async () => {
const addDimension = await screen.findByText('Add new dimension');
await user.click(addDimension);
},
},
{
component: NewDimensionFields,
label: 'Dimension Fields experimental UI',
addDimension: async () => {
const addDimension = await screen.findByLabelText('Add');
await user.click(addDimension);
},
},
];
it('should render a dimension filter', async () => {
let mockQuery = createMockQuery();
const mockPanelData = createMockPanelData();
const onQueryChange = jest.fn();
const dimensionOptions = [
{ label: 'Test Dimension 1', value: 'TestDimension1' },
{ label: 'Test Dimension 2', value: 'TestDimension2' },
];
const { rerender } = render(
<DimensionFields
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const addDimension = await screen.findByText('Add new dimension');
await user.click(addDimension);
mockQuery = appendDimensionFilter(mockQuery);
expect(onQueryChange).toHaveBeenCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: '', operator: 'eq', filters: [] }],
},
for (const t of tests) {
describe(`Azure Monitor QueryEditor: ${t.label}`, () => {
const mockDatasource = createMockDatasource();
it('should render a dimension filter', async () => {
let mockQuery = createMockQuery();
const mockPanelData = createMockPanelData();
const onQueryChange = jest.fn();
const dimensionOptions = [
{ label: 'Test Dimension 1', value: 'TestDimension1' },
{ label: 'Test Dimension 2', value: 'TestDimension2' },
];
const { rerender } = render(
<t.component
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
await t.addDimension();
mockQuery = appendDimensionFilter(mockQuery);
expect(onQueryChange).toHaveBeenCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: '', operator: 'eq', filters: [] }],
},
});
rerender(
<t.component
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const dimensionSelect = await screen.findByText('Field');
await selectOptionInTest(dimensionSelect, 'Test Dimension 1');
expect(onQueryChange).toHaveBeenCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: 'TestDimension1', operator: 'eq', filters: [] }],
},
});
expect(screen.queryByText('Test Dimension 1')).toBeInTheDocument();
expect(screen.queryByText('==')).toBeInTheDocument();
});
rerender(
<DimensionFields
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const dimensionSelect = await screen.findByText('Field');
await selectOptionInTest(dimensionSelect, 'Test Dimension 1');
expect(onQueryChange).toHaveBeenCalledWith({
...mockQuery,
azureMonitor: {
it('correctly filters out dimensions when selected', async () => {
let mockQuery = createMockQuery();
const mockPanelData = createMockPanelData();
mockQuery.azureMonitor = {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: 'TestDimension1', operator: 'eq', filters: [] }],
},
};
const onQueryChange = jest.fn();
const dimensionOptions = [
{ label: 'Test Dimension 1', value: 'TestDimension1' },
{ label: 'Test Dimension 2', value: 'TestDimension2' },
];
const { rerender } = render(
<t.component
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
await t.addDimension();
mockQuery = appendDimensionFilter(mockQuery);
rerender(
<t.component
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const dimensionSelect = await screen.findByText('Field');
await user.click(dimensionSelect);
const options = await screen.findAllByLabelText('Select option');
expect(options).toHaveLength(1);
expect(options[0]).toHaveTextContent('Test Dimension 2');
});
expect(screen.queryByText('Test Dimension 1')).toBeInTheDocument();
expect(screen.queryByText('==')).toBeInTheDocument();
});
it('correctly filters out dimensions when selected', async () => {
let mockQuery = createMockQuery();
const mockPanelData = createMockPanelData();
mockQuery.azureMonitor = {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: 'TestDimension1', operator: 'eq', filters: [] }],
};
const onQueryChange = jest.fn();
const dimensionOptions = [
{ label: 'Test Dimension 1', value: 'TestDimension1' },
{ label: 'Test Dimension 2', value: 'TestDimension2' },
];
const { rerender } = render(
<DimensionFields
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const addDimension = await screen.findByText('Add new dimension');
await user.click(addDimension);
mockQuery = appendDimensionFilter(mockQuery);
rerender(
<DimensionFields
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const dimensionSelect = await screen.findByText('Field');
await user.click(dimensionSelect);
const options = await screen.findAllByLabelText('Select option');
expect(options).toHaveLength(1);
expect(options[0]).toHaveTextContent('Test Dimension 2');
});
it('correctly displays dimension labels', async () => {
let mockQuery = createMockQuery();
const mockPanelData = createMockPanelData();
mockQuery.azureMonitor = {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: 'TestDimension1', operator: 'eq', filters: [] }],
};
mockPanelData.series = [
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel' },
},
],
},
];
const onQueryChange = jest.fn();
const dimensionOptions = [{ label: 'Test Dimension 1', value: 'TestDimension1' }];
render(
<DimensionFields
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const labelSelect = await screen.findByText('Select value(s)');
await user.click(labelSelect);
const options = await screen.findAllByLabelText('Select option');
expect(options).toHaveLength(1);
expect(options[0]).toHaveTextContent('testlabel');
});
it('correctly updates dimension labels', async () => {
let mockQuery = createMockQuery();
const mockPanelData = createMockPanelData();
mockQuery.azureMonitor = {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: 'TestDimension1', operator: 'eq', filters: ['testlabel'] }],
};
mockPanelData.series = [
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel' },
},
],
},
];
const onQueryChange = jest.fn();
const dimensionOptions = [{ label: 'Test Dimension 1', value: 'TestDimension1' }];
const { rerender } = render(
<DimensionFields
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
await screen.findByText('testlabel');
const labelClear = await screen.findByLabelText('Remove testlabel');
await user.click(labelClear);
mockQuery = setDimensionFilterValue(mockQuery, 0, 'filters', []);
expect(onQueryChange).toHaveBeenCalledWith({
...mockQuery,
azureMonitor: {
it('correctly displays dimension labels', async () => {
let mockQuery = createMockQuery();
const mockPanelData = createMockPanelData();
mockQuery.azureMonitor = {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: 'TestDimension1', operator: 'eq', filters: [] }],
},
});
mockPanelData.series = [
...mockPanelData.series,
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel2' },
},
],
},
];
rerender(
<DimensionFields
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const labelSelect = await screen.getByLabelText('dimension-labels-select');
await openMenu(labelSelect);
const options = await screen.findAllByLabelText('Select option');
expect(options).toHaveLength(2);
expect(options[0]).toHaveTextContent('testlabel');
expect(options[1]).toHaveTextContent('testlabel2');
});
};
it('correctly selects multiple dimension labels', async () => {
let mockQuery = createMockQuery();
const mockPanelData = createMockPanelData();
mockPanelData.series = [
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel' },
},
],
},
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel2' },
},
],
},
];
const onQueryChange = jest.fn();
const dimensionOptions = [{ label: 'Test Dimension 1', value: 'TestDimension1' }];
mockQuery = appendDimensionFilter(mockQuery, 'TestDimension1');
const { rerender } = render(
<DimensionFields
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const labelSelect = await screen.getByLabelText('dimension-labels-select');
await user.click(labelSelect);
await openMenu(labelSelect);
await screen.getByText('testlabel');
await screen.getByText('testlabel2');
await selectOptionInTest(labelSelect, 'testlabel');
mockQuery = setDimensionFilterValue(mockQuery, 0, 'filters', ['testlabel']);
expect(onQueryChange).toHaveBeenCalledWith({
...mockQuery,
azureMonitor: {
mockPanelData.series = [
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel' },
},
],
},
];
const onQueryChange = jest.fn();
const dimensionOptions = [{ label: 'Test Dimension 1', value: 'TestDimension1' }];
render(
<t.component
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const labelSelect = await screen.findByText('Select value(s)');
await user.click(labelSelect);
const options = await screen.findAllByLabelText('Select option');
expect(options).toHaveLength(1);
expect(options[0]).toHaveTextContent('testlabel');
});
it('correctly updates dimension labels', async () => {
let mockQuery = createMockQuery();
const mockPanelData = createMockPanelData();
mockQuery.azureMonitor = {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: 'TestDimension1', operator: 'eq', filters: ['testlabel'] }],
},
};
mockPanelData.series = [
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel' },
},
],
},
];
const onQueryChange = jest.fn();
const dimensionOptions = [{ label: 'Test Dimension 1', value: 'TestDimension1' }];
const { rerender } = render(
<t.component
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
await screen.findByText('testlabel');
const labelClear = await screen.findByLabelText('Remove testlabel');
await user.click(labelClear);
mockQuery = setDimensionFilterValue(mockQuery, 0, 'filters', []);
expect(onQueryChange).toHaveBeenCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: 'TestDimension1', operator: 'eq', filters: [] }],
},
});
mockPanelData.series = [
...mockPanelData.series,
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel2' },
},
],
},
];
rerender(
<t.component
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const labelSelect = screen.getByLabelText('dimension-labels-select');
await openMenu(labelSelect);
const options = await screen.findAllByLabelText('Select option');
expect(options).toHaveLength(2);
expect(options[0]).toHaveTextContent('testlabel');
expect(options[1]).toHaveTextContent('testlabel2');
});
mockPanelData.series = [
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel' },
},
],
},
];
rerender(
<DimensionFields
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const labelSelect2 = await screen.getByLabelText('dimension-labels-select');
await openMenu(labelSelect2);
const refreshedOptions = await screen.findAllByLabelText('Select options menu');
expect(refreshedOptions).toHaveLength(1);
expect(refreshedOptions[0]).toHaveTextContent('testlabel2');
await selectOptionInTest(labelSelect2, 'testlabel2');
mockQuery = setDimensionFilterValue(mockQuery, 0, 'filters', ['testlabel', 'testlabel2']);
expect(onQueryChange).toHaveBeenCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: 'TestDimension1', operator: 'eq', filters: ['testlabel', 'testlabel2'] }],
},
it('correctly selects multiple dimension labels', async () => {
let mockQuery = createMockQuery();
const mockPanelData = createMockPanelData();
mockPanelData.series = [
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel' },
},
],
},
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel2' },
},
],
},
];
const onQueryChange = jest.fn();
const dimensionOptions = [{ label: 'Test Dimension 1', value: 'TestDimension1' }];
mockQuery = appendDimensionFilter(mockQuery, 'TestDimension1');
const { rerender } = render(
<t.component
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const labelSelect = screen.getByLabelText('dimension-labels-select');
await user.click(labelSelect);
await openMenu(labelSelect);
screen.getByText('testlabel');
screen.getByText('testlabel2');
await selectOptionInTest(labelSelect, 'testlabel');
mockQuery = setDimensionFilterValue(mockQuery, 0, 'filters', ['testlabel']);
expect(onQueryChange).toHaveBeenCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: 'TestDimension1', operator: 'eq', filters: ['testlabel'] }],
},
});
mockPanelData.series = [
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel' },
},
],
},
];
rerender(
<t.component
data={mockPanelData}
subscriptionId="123"
query={mockQuery}
onQueryChange={onQueryChange}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
setError={() => {}}
dimensionOptions={dimensionOptions}
/>
);
const labelSelect2 = screen.getByLabelText('dimension-labels-select');
await openMenu(labelSelect2);
const refreshedOptions = await screen.findAllByLabelText('Select options menu');
expect(refreshedOptions).toHaveLength(1);
expect(refreshedOptions[0]).toHaveTextContent('testlabel2');
await selectOptionInTest(labelSelect2, 'testlabel2');
mockQuery = setDimensionFilterValue(mockQuery, 0, 'filters', ['testlabel', 'testlabel2']);
expect(onQueryChange).toHaveBeenCalledWith({
...mockQuery,
azureMonitor: {
...mockQuery.azureMonitor,
dimensionFilters: [{ dimension: 'TestDimension1', operator: 'eq', filters: ['testlabel', 'testlabel2'] }],
},
});
mockPanelData.series = [
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel' },
},
],
},
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel2' },
},
],
},
];
});
mockPanelData.series = [
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel' },
},
],
},
{
...mockPanelData.series[0],
fields: [
{
...mockPanelData.series[0].fields[0],
name: 'Test Dimension 1',
labels: { testdimension1: 'testlabel2' },
},
],
},
];
});
});
}

View File

@ -0,0 +1,206 @@
import React, { useEffect, useMemo, useState } from 'react';
import { SelectableValue, DataFrame, PanelData, Labels } from '@grafana/data';
import { AccessoryButton, EditorList } from '@grafana/experimental';
import { Select, HorizontalGroup, MultiSelect } from '@grafana/ui';
import { AzureMetricDimension, AzureMonitorOption, AzureMonitorQuery, AzureQueryEditorFieldProps } from '../../types';
import { Field } from '../Field';
import { setDimensionFilters } from './setQueryValue';
interface DimensionFieldsProps extends AzureQueryEditorFieldProps {
dimensionOptions: AzureMonitorOption[];
}
interface DimensionLabels {
[key: string]: Set<string>;
}
const useDimensionLabels = (data: PanelData | undefined, query: AzureMonitorQuery) => {
const [dimensionLabels, setDimensionLabels] = useState<DimensionLabels>({});
useEffect(() => {
let labelsObj: DimensionLabels = {};
if (data?.series?.length) {
// Identify which series' in the dataframe are relevant to the current query
const series: DataFrame[] = data.series.flat().filter((series) => series.refId === query.refId);
const fields = series.flatMap((series) => series.fields);
// Retrieve labels for series fields
const labels = fields
.map((fields) => fields.labels)
.flat()
.filter((item): item is Labels => item !== null && item !== undefined);
for (const label of labels) {
// Labels only exist for series that have a dimension selected
for (const [dimension, value] of Object.entries(label)) {
if (labelsObj[dimension]) {
labelsObj[dimension].add(value);
} else {
labelsObj[dimension] = new Set([value]);
}
}
}
}
setDimensionLabels((prevLabels) => {
const newLabels: DimensionLabels = {};
const currentLabels = Object.keys(labelsObj);
if (currentLabels.length === 0) {
return prevLabels;
}
for (const label of currentLabels) {
if (prevLabels[label] && labelsObj[label].size < prevLabels[label].size) {
newLabels[label] = prevLabels[label];
} else {
newLabels[label] = labelsObj[label];
}
}
return newLabels;
});
}, [data?.series, query.refId]);
return dimensionLabels;
};
const NewDimensionFields: React.FC<DimensionFieldsProps> = ({ data, query, dimensionOptions, onQueryChange }) => {
const dimensionFilters = useMemo(
() => query.azureMonitor?.dimensionFilters ?? [],
[query.azureMonitor?.dimensionFilters]
);
const dimensionLabels = useDimensionLabels(data, query);
const dimensionOperators: Array<SelectableValue<string>> = [
{ label: '==', value: 'eq' },
{ label: '!=', value: 'ne' },
{ label: 'starts with', value: 'sw' },
];
const validDimensionOptions = useMemo(() => {
// We filter out any dimensions that have already been used in a filter as the API doesn't support having multiple filters with the same dimension name.
// The Azure portal also doesn't support this feature so it makes sense for consistency.
let t = dimensionOptions;
if (dimensionFilters.length) {
t = dimensionOptions.filter(
(val) => !dimensionFilters.some((dimensionFilter) => dimensionFilter.dimension === val.value)
);
}
return t;
}, [dimensionFilters, dimensionOptions]);
const onFieldChange = <Key extends keyof AzureMetricDimension>(
fieldName: Key,
item: Partial<AzureMetricDimension>,
value: AzureMetricDimension[Key],
onChange: (item: Partial<AzureMetricDimension>) => void
) => {
item[fieldName] = value;
onChange(item);
};
const getValidDimensionOptions = (selectedDimension: string) => {
return validDimensionOptions.concat(dimensionOptions.filter((item) => item.value === selectedDimension));
};
const getValidFilterOptions = (selectedFilter: string | undefined, dimension: string) => {
const dimensionFilters = Array.from(dimensionLabels[dimension.toLowerCase()] ?? []);
if (dimensionFilters.find((filter) => filter === selectedFilter)) {
return dimensionFilters.map((filter) => ({ value: filter, label: filter }));
}
return [...dimensionFilters, ...(selectedFilter && selectedFilter !== '*' ? [selectedFilter] : [])].map((item) => ({
value: item,
label: item,
}));
};
const getValidMultiSelectOptions = (selectedFilters: string[] | undefined, dimension: string) => {
const labelOptions = getValidFilterOptions(undefined, dimension);
if (selectedFilters) {
for (const filter of selectedFilters) {
if (!labelOptions.find((label) => label.value === filter)) {
labelOptions.push({ value: filter, label: filter });
}
}
}
return labelOptions;
};
const getValidOperators = (selectedOperator: string) => {
if (dimensionOperators.find((operator: SelectableValue) => operator.value === selectedOperator)) {
return dimensionOperators;
}
return [...dimensionOperators, ...(selectedOperator ? [{ label: selectedOperator, value: selectedOperator }] : [])];
};
const changedFunc = (changed: Array<Partial<AzureMetricDimension>>) => {
const properData: AzureMetricDimension[] = changed.map((x) => {
return {
dimension: x.dimension ?? '',
operator: x.operator ?? 'eq',
filters: x.filters ?? [],
};
});
onQueryChange(setDimensionFilters(query, properData));
};
const renderFilters = (
item: Partial<AzureMetricDimension>,
onChange: (item: Partial<AzureMetricDimension>) => void,
onDelete: () => void
) => {
return (
<HorizontalGroup spacing="none">
<Select
menuShouldPortal
placeholder="Field"
value={item.dimension}
options={getValidDimensionOptions(item.dimension || '')}
onChange={(e) => onFieldChange('dimension', item, e.value ?? '', onChange)}
/>
<Select
menuShouldPortal
placeholder="Operation"
value={item.operator}
options={getValidOperators(item.operator || 'eq')}
onChange={(e) => onFieldChange('operator', item, e.value ?? '', onChange)}
allowCustomValue
/>
{item.operator === 'eq' || item.operator === 'ne' ? (
<MultiSelect
menuShouldPortal
placeholder="Select value(s)"
value={item.filters}
options={getValidMultiSelectOptions(item.filters, item.dimension ?? '')}
onChange={(e) =>
onFieldChange(
'filters',
item,
e.map((x) => x.value ?? ''),
onChange
)
}
aria-label={'dimension-labels-select'}
allowCustomValue
/>
) : (
// The API does not currently allow for multiple "starts with" clauses to be used.
<Select
menuShouldPortal
placeholder="Select value"
value={item.filters ? item.filters[0] : ''}
allowCustomValue
options={getValidFilterOptions(item.filters ? item.filters[0] : '', item.dimension ?? '')}
onChange={(e) => onFieldChange('filters', item, [e?.value ?? ''], onChange)}
isClearable
/>
)}
<AccessoryButton aria-label="Remove" icon="times" variant="secondary" onClick={onDelete} type="button" />
</HorizontalGroup>
);
};
return (
<Field label="Dimensions">
<EditorList items={dimensionFilters} onChange={changedFunc} renderItem={renderFilters} />
</Field>
);
};
export default NewDimensionFields;

View File

@ -13,6 +13,7 @@ import DimensionFields from '../MetricsQueryEditor/DimensionFields';
import LegendFormatField from '../MetricsQueryEditor/LegendFormatField';
import MetricNameField from '../MetricsQueryEditor/MetricNameField';
import MetricNamespaceField from '../MetricsQueryEditor/MetricNamespaceField';
import NewDimensionFields from '../MetricsQueryEditor/NewDimensionFields';
import TimeGrainField from '../MetricsQueryEditor/TimeGrainField';
import TopField from '../MetricsQueryEditor/TopField';
import { setResource } from '../MetricsQueryEditor/setQueryValue';
@ -105,7 +106,7 @@ const MetricsQueryEditor: React.FC<MetricsQueryEditorProps> = ({
</EditorRow>
<EditorRow>
<EditorFieldGroup>
<DimensionFields
<NewDimensionFields
data={data}
query={query}
datasource={datasource}
@ -125,10 +126,6 @@ const MetricsQueryEditor: React.FC<MetricsQueryEditorProps> = ({
onQueryChange={onChange}
setError={setError}
/>
</EditorFieldGroup>
</EditorRow>
<EditorRow>
<EditorFieldGroup>
<LegendFormatField
query={query}
datasource={datasource}