mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 21:19:28 -06:00
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:
parent
52ed651958
commit
53cb94a2ad
@ -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' },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
@ -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}
|
||||
|
Loading…
Reference in New Issue
Block a user