mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AzureMonitor: Update UI to experimental package (#52123)
* feat: make azure experimental the default * feat: combine metrics query editor rows fix: linter errors * chore: remove test loop for DimensionFields test
This commit is contained in:
parent
c8b5307c61
commit
5d199a40b7
@ -49,7 +49,6 @@ export interface FeatureToggles {
|
||||
commandPalette?: boolean;
|
||||
cloudWatchDynamicLabels?: boolean;
|
||||
datasourceQueryMultiStatus?: boolean;
|
||||
azureMonitorExperimentalUI?: boolean;
|
||||
traceToMetrics?: boolean;
|
||||
prometheusStreamingJSONParser?: boolean;
|
||||
validateDashboardsOnSave?: boolean;
|
||||
|
@ -187,12 +187,6 @@ var (
|
||||
Description: "Introduce HTTP 207 Multi Status for api/ds/query",
|
||||
State: FeatureStateAlpha,
|
||||
},
|
||||
{
|
||||
Name: "azureMonitorExperimentalUI",
|
||||
Description: "Use grafana-experimental UI in Azure Monitor",
|
||||
State: FeatureStateAlpha,
|
||||
FrontendOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "traceToMetrics",
|
||||
Description: "Enable trace to metrics links",
|
||||
|
@ -139,10 +139,6 @@ const (
|
||||
// Introduce HTTP 207 Multi Status for api/ds/query
|
||||
FlagDatasourceQueryMultiStatus = "datasourceQueryMultiStatus"
|
||||
|
||||
// FlagAzureMonitorExperimentalUI
|
||||
// Use grafana-experimental UI in Azure Monitor
|
||||
FlagAzureMonitorExperimentalUI = "azureMonitorExperimentalUI"
|
||||
|
||||
// FlagTraceToMetrics
|
||||
// Enable trace to metrics links
|
||||
FlagTraceToMetrics = "traceToMetrics"
|
||||
|
@ -1,8 +1,6 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
|
||||
import { EditorRows, EditorRow, EditorFieldGroup } from '@grafana/experimental';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { InlineFieldRow } from '@grafana/ui';
|
||||
|
||||
import Datasource from '../../datasource';
|
||||
import { AzureMonitorErrorish, AzureMonitorOption, AzureMonitorQuery } from '../../types';
|
||||
@ -54,62 +52,34 @@ const ArgQueryEditor: React.FC<ArgQueryEditorProps> = ({
|
||||
.catch((err) => setError(ERROR_SOURCE, err));
|
||||
}, [datasource, onChange, query, setError]);
|
||||
|
||||
if (config.featureToggles.azureMonitorExperimentalUI) {
|
||||
return (
|
||||
<span data-testid="azure-monitor-arg-query-editor-with-experimental-ui">
|
||||
<EditorRows>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<SubscriptionField
|
||||
multiSelect
|
||||
subscriptions={subscriptions}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
</EditorRows>
|
||||
<QueryField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div data-testid="azure-monitor-arg-query-editor">
|
||||
<InlineFieldRow>
|
||||
<SubscriptionField
|
||||
multiSelect
|
||||
subscriptions={subscriptions}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</InlineFieldRow>
|
||||
|
||||
<QueryField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span data-testid="azure-monitor-arg-query-editor-with-experimental-ui">
|
||||
<EditorRows>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<SubscriptionField
|
||||
multiSelect
|
||||
subscriptions={subscriptions}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
</EditorRows>
|
||||
<QueryField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default ArgQueryEditor;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import { EditorField } from '@grafana/experimental';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { InlineField } from '@grafana/ui';
|
||||
import { Props as InlineFieldProps } from '@grafana/ui/src/components/Forms/InlineField';
|
||||
|
||||
@ -16,7 +15,7 @@ const DEFAULT_LABEL_WIDTH = 18;
|
||||
export const Field = (props: Props) => {
|
||||
const { labelWidth, inlineField, ...remainingProps } = props;
|
||||
|
||||
if (config.featureToggles.azureMonitorExperimentalUI && !inlineField) {
|
||||
if (!inlineField) {
|
||||
return <EditorField width={labelWidth || DEFAULT_LABEL_WIDTH} {...remainingProps} />;
|
||||
} else {
|
||||
return <InlineField labelWidth={labelWidth || DEFAULT_LABEL_WIDTH} {...remainingProps} />;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import { EditorRows, EditorRow, EditorFieldGroup } from '@grafana/experimental';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Alert } from '@grafana/ui';
|
||||
|
||||
import Datasource from '../../datasource';
|
||||
@ -35,81 +34,32 @@ const LogsQueryEditor: React.FC<LogsQueryEditorProps> = ({
|
||||
}) => {
|
||||
const migrationError = useMigrations(datasource, query, onChange);
|
||||
|
||||
if (config.featureToggles.azureMonitorExperimentalUI) {
|
||||
return (
|
||||
<span data-testid="azure-monitor-logs-query-editor-with-experimental-ui">
|
||||
<EditorRows>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<ResourceField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
inlineField={true}
|
||||
labelWidth={10}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
selectableEntryTypes={[
|
||||
ResourceRowType.Subscription,
|
||||
ResourceRowType.ResourceGroup,
|
||||
ResourceRowType.Resource,
|
||||
ResourceRowType.Variable,
|
||||
]}
|
||||
setResource={setResource}
|
||||
resourceUri={query.azureLogAnalytics?.resource}
|
||||
queryType="logs"
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
<QueryField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
{!hideFormatAs && (
|
||||
<FormatAsField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
)}
|
||||
|
||||
{migrationError && <Alert title={migrationError.title}>{migrationError.message}</Alert>}
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
</EditorRows>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div data-testid="azure-monitor-logs-query-editor">
|
||||
<ResourceField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
selectableEntryTypes={[
|
||||
ResourceRowType.Subscription,
|
||||
ResourceRowType.ResourceGroup,
|
||||
ResourceRowType.Resource,
|
||||
ResourceRowType.Variable,
|
||||
]}
|
||||
setResource={setResource}
|
||||
resourceUri={query.azureLogAnalytics?.resource}
|
||||
queryType="logs"
|
||||
/>
|
||||
|
||||
return (
|
||||
<span data-testid="azure-monitor-logs-query-editor-with-experimental-ui">
|
||||
<EditorRows>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<ResourceField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
inlineField={true}
|
||||
labelWidth={10}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
selectableEntryTypes={[
|
||||
ResourceRowType.Subscription,
|
||||
ResourceRowType.ResourceGroup,
|
||||
ResourceRowType.Resource,
|
||||
ResourceRowType.Variable,
|
||||
]}
|
||||
setResource={setResource}
|
||||
resourceUri={query.azureLogAnalytics?.resource}
|
||||
queryType="logs"
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
<QueryField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
@ -118,22 +68,25 @@ const LogsQueryEditor: React.FC<LogsQueryEditorProps> = ({
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
{!hideFormatAs && (
|
||||
<FormatAsField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!hideFormatAs && (
|
||||
<FormatAsField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
)}
|
||||
|
||||
{migrationError && <Alert title={migrationError.title}>{migrationError.message}</Alert>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
{migrationError && <Alert title={migrationError.title}>{migrationError.message}</Alert>}
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
</EditorRows>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogsQueryEditor;
|
||||
|
@ -44,7 +44,6 @@ const AggregationField: React.FC<AggregationFieldProps> = ({
|
||||
value={query.azureMonitor?.aggregation}
|
||||
onChange={handleChange}
|
||||
options={options}
|
||||
width={38}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</Field>
|
||||
|
@ -9,7 +9,6 @@ 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,365 +17,346 @@ const variableOptionGroup = {
|
||||
};
|
||||
const user = userEvent.setup();
|
||||
|
||||
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);
|
||||
},
|
||||
},
|
||||
];
|
||||
describe(`Azure Monitor QueryEditor`, () => {
|
||||
const mockDatasource = createMockDatasource();
|
||||
|
||||
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(
|
||||
<DimensionFields
|
||||
data={mockPanelData}
|
||||
subscriptionId="123"
|
||||
query={mockQuery}
|
||||
onQueryChange={onQueryChange}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
setError={() => {}}
|
||||
dimensionOptions={dimensionOptions}
|
||||
/>
|
||||
);
|
||||
|
||||
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}
|
||||
/>
|
||||
);
|
||||
const addDimension = await screen.findByLabelText('Add');
|
||||
await user.click(addDimension);
|
||||
|
||||
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();
|
||||
mockQuery = appendDimensionFilter(mockQuery);
|
||||
expect(onQueryChange).toHaveBeenCalledWith({
|
||||
...mockQuery,
|
||||
azureMonitor: {
|
||||
...mockQuery.azureMonitor,
|
||||
dimensionFilters: [{ dimension: '', operator: 'eq', filters: [] }],
|
||||
},
|
||||
});
|
||||
|
||||
it('correctly filters out dimensions when selected', async () => {
|
||||
let mockQuery = createMockQuery();
|
||||
const mockPanelData = createMockPanelData();
|
||||
mockQuery.azureMonitor = {
|
||||
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: {
|
||||
...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 displays dimension labels', async () => {
|
||||
let mockQuery = createMockQuery();
|
||||
const mockPanelData = createMockPanelData();
|
||||
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(
|
||||
<DimensionFields
|
||||
data={mockPanelData}
|
||||
subscriptionId="123"
|
||||
query={mockQuery}
|
||||
onQueryChange={onQueryChange}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
setError={() => {}}
|
||||
dimensionOptions={dimensionOptions}
|
||||
/>
|
||||
);
|
||||
|
||||
const addDimension = await screen.findByLabelText('Add');
|
||||
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: {
|
||||
...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(
|
||||
<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');
|
||||
},
|
||||
});
|
||||
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 = 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 updates dimension labels', async () => {
|
||||
let mockQuery = createMockQuery();
|
||||
const mockPanelData = createMockPanelData();
|
||||
mockQuery.azureMonitor = {
|
||||
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 = 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' },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
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');
|
||||
},
|
||||
});
|
||||
|
||||
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' },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
rerender(
|
||||
<DimensionFields
|
||||
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' },
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -1,12 +1,13 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { SelectableValue, DataFrame, PanelData } from '@grafana/data';
|
||||
import { Button, Select, HorizontalGroup, VerticalGroup, MultiSelect } from '@grafana/ui';
|
||||
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 { appendDimensionFilter, removeDimensionFilter, setDimensionFilterValue } from './setQueryValue';
|
||||
import { setDimensionFilters } from './setQueryValue';
|
||||
|
||||
interface DimensionFieldsProps extends AzureQueryEditorFieldProps {
|
||||
dimensionOptions: AzureMonitorOption[];
|
||||
@ -28,16 +29,14 @@ const useDimensionLabels = (data: PanelData | undefined, query: AzureMonitorQuer
|
||||
const labels = fields
|
||||
.map((fields) => fields.labels)
|
||||
.flat()
|
||||
.filter((item) => item!);
|
||||
.filter((item): item is Labels => item !== null && item !== undefined);
|
||||
for (const label of labels) {
|
||||
// Labels only exist for series that have a dimension selected
|
||||
if (label) {
|
||||
for (const [dimension, value] of Object.entries(label)) {
|
||||
if (labelsObj[dimension]) {
|
||||
labelsObj[dimension].add(value);
|
||||
} else {
|
||||
labelsObj[dimension] = new Set([value]);
|
||||
}
|
||||
for (const [dimension, value] of Object.entries(label)) {
|
||||
if (labelsObj[dimension]) {
|
||||
labelsObj[dimension].add(value);
|
||||
} else {
|
||||
labelsObj[dimension] = new Set([value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -87,24 +86,14 @@ const DimensionFields: React.FC<DimensionFieldsProps> = ({ data, query, dimensio
|
||||
return t;
|
||||
}, [dimensionFilters, dimensionOptions]);
|
||||
|
||||
const addFilter = () => {
|
||||
onQueryChange(appendDimensionFilter(query));
|
||||
};
|
||||
|
||||
const removeFilter = (index: number) => {
|
||||
onQueryChange(removeDimensionFilter(query, index));
|
||||
};
|
||||
|
||||
const onFieldChange = <Key extends keyof AzureMetricDimension>(
|
||||
filterIndex: number,
|
||||
fieldName: Key,
|
||||
value: AzureMetricDimension[Key]
|
||||
item: Partial<AzureMetricDimension>,
|
||||
value: AzureMetricDimension[Key],
|
||||
onChange: (item: Partial<AzureMetricDimension>) => void
|
||||
) => {
|
||||
onQueryChange(setDimensionFilterValue(query, filterIndex, fieldName, value));
|
||||
};
|
||||
|
||||
const onFilterInputChange = (index: number, v: SelectableValue<string> | null) => {
|
||||
onFieldChange(index, 'filters', [v?.value ?? '']);
|
||||
item[fieldName] = value;
|
||||
onChange(item);
|
||||
};
|
||||
|
||||
const getValidDimensionOptions = (selectedDimension: string) => {
|
||||
@ -133,7 +122,6 @@ const DimensionFields: React.FC<DimensionFieldsProps> = ({ data, query, dimensio
|
||||
}
|
||||
return labelOptions;
|
||||
};
|
||||
|
||||
const getValidOperators = (selectedOperator: string) => {
|
||||
if (dimensionOperators.find((operator: SelectableValue) => operator.value === selectedOperator)) {
|
||||
return dimensionOperators;
|
||||
@ -141,70 +129,76 @@ const DimensionFields: React.FC<DimensionFieldsProps> = ({ data, query, dimensio
|
||||
return [...dimensionOperators, ...(selectedOperator ? [{ label: selectedOperator, value: selectedOperator }] : [])];
|
||||
};
|
||||
|
||||
const onMultiSelectFilterChange = (index: number, v: Array<SelectableValue<string>>) => {
|
||||
onFieldChange(
|
||||
index,
|
||||
'filters',
|
||||
v.map((item) => item.value || '')
|
||||
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="Dimension">
|
||||
<VerticalGroup spacing="xs">
|
||||
{dimensionFilters.map((filter, index) => (
|
||||
<HorizontalGroup key={index} spacing="xs">
|
||||
<Select
|
||||
placeholder="Field"
|
||||
value={filter.dimension}
|
||||
options={getValidDimensionOptions(filter.dimension)}
|
||||
onChange={(v) => onFieldChange(index, 'dimension', v.value ?? '')}
|
||||
width={38}
|
||||
/>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
placeholder="Operation"
|
||||
value={filter.operator}
|
||||
options={getValidOperators(filter.operator)}
|
||||
onChange={(v) => onFieldChange(index, 'operator', v.value ?? '')}
|
||||
allowCustomValue
|
||||
/>
|
||||
{filter.operator === 'eq' || filter.operator === 'ne' ? (
|
||||
<MultiSelect
|
||||
menuShouldPortal
|
||||
placeholder="Select value(s)"
|
||||
value={filter.filters}
|
||||
options={getValidMultiSelectOptions(filter.filters, filter.dimension)}
|
||||
onChange={(v) => onMultiSelectFilterChange(index, v)}
|
||||
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={filter.filters ? filter.filters[0] : ''}
|
||||
allowCustomValue
|
||||
options={getValidFilterOptions(filter.filters ? filter.filters[0] : '', filter.dimension)}
|
||||
onChange={(v) => onFilterInputChange(index, v)}
|
||||
isClearable
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="md"
|
||||
icon="trash-alt"
|
||||
aria-label="Remove"
|
||||
onClick={() => removeFilter(index)}
|
||||
></Button>
|
||||
</HorizontalGroup>
|
||||
))}
|
||||
|
||||
<Button variant="secondary" size="md" onClick={addFilter}>
|
||||
Add new dimension
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
<Field label="Dimensions">
|
||||
<EditorList items={dimensionFilters} onChange={changedFunc} renderItem={renderFilters} />
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
@ -34,7 +34,6 @@ const MetricNameField: React.FC<MetricNameProps> = ({ metricNames, query, variab
|
||||
value={query.azureMonitor?.metricName ?? null}
|
||||
onChange={handleChange}
|
||||
options={options}
|
||||
width={38}
|
||||
allowCustomValue
|
||||
/>
|
||||
</Field>
|
||||
|
@ -46,7 +46,6 @@ const MetricNamespaceField: React.FC<MetricNamespaceFieldProps> = ({
|
||||
value={query.azureMonitor?.metricNamespace}
|
||||
onChange={handleChange}
|
||||
options={options}
|
||||
width={38}
|
||||
allowCustomValue
|
||||
/>
|
||||
</Field>
|
||||
|
@ -3,8 +3,6 @@ import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { selectOptionInTest } from 'test/helpers/selectOptionInTest';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import createMockDatasource from '../../__mocks__/datasource';
|
||||
import { createMockInstanceSetttings } from '../../__mocks__/instanceSettings';
|
||||
import createMockPanelData from '../../__mocks__/panelData';
|
||||
@ -23,15 +21,6 @@ const variableOptionGroup = {
|
||||
options: [],
|
||||
};
|
||||
|
||||
const tests = [
|
||||
{
|
||||
id: 'azure-monitor-metrics-query-editor-with-resource-picker',
|
||||
},
|
||||
{
|
||||
id: 'azure-monitor-metrics-query-editor-with-experimental-ui',
|
||||
},
|
||||
];
|
||||
|
||||
export function createMockResourcePickerData() {
|
||||
const mockDatasource = new ResourcePickerData(createMockInstanceSetttings());
|
||||
|
||||
@ -46,215 +35,210 @@ export function createMockResourcePickerData() {
|
||||
return mockDatasource;
|
||||
}
|
||||
|
||||
for (const t of tests) {
|
||||
describe(`MetricsQueryEditor: ${t.id}`, () => {
|
||||
const originalScrollIntoView = window.HTMLElement.prototype.scrollIntoView;
|
||||
const mockPanelData = createMockPanelData();
|
||||
describe('MetricsQueryEditor', () => {
|
||||
const originalScrollIntoView = window.HTMLElement.prototype.scrollIntoView;
|
||||
const mockPanelData = createMockPanelData();
|
||||
|
||||
beforeEach(() => {
|
||||
window.HTMLElement.prototype.scrollIntoView = function () {};
|
||||
config.featureToggles.azureMonitorExperimentalUI =
|
||||
t.id === 'azure-monitor-metrics-query-editor-with-experimental-ui';
|
||||
});
|
||||
afterEach(() => {
|
||||
window.HTMLElement.prototype.scrollIntoView = originalScrollIntoView;
|
||||
config.featureToggles.azureMonitorExperimentalUI = false;
|
||||
});
|
||||
beforeEach(() => {
|
||||
window.HTMLElement.prototype.scrollIntoView = function () {};
|
||||
});
|
||||
afterEach(() => {
|
||||
window.HTMLElement.prototype.scrollIntoView = originalScrollIntoView;
|
||||
});
|
||||
|
||||
it('should render', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
it('should render', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
|
||||
render(
|
||||
<MetricsQueryEditor
|
||||
data={mockPanelData}
|
||||
query={createMockQuery()}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={() => {}}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
render(
|
||||
<MetricsQueryEditor
|
||||
data={mockPanelData}
|
||||
query={createMockQuery()}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={() => {}}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(await screen.findByTestId(t.id)).toBeInTheDocument();
|
||||
});
|
||||
expect(await screen.findByTestId('azure-monitor-metrics-query-editor-with-experimental-ui')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should change resource when a resource is selected in the ResourcePicker', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const query = createMockQuery();
|
||||
delete query?.azureMonitor?.resourceUri;
|
||||
const onChange = jest.fn();
|
||||
it('should change resource when a resource is selected in the ResourcePicker', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const query = createMockQuery();
|
||||
delete query?.azureMonitor?.resourceUri;
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(
|
||||
<MetricsQueryEditor
|
||||
data={mockPanelData}
|
||||
query={query}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
render(
|
||||
<MetricsQueryEditor
|
||||
data={mockPanelData}
|
||||
query={query}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
const resourcePickerButton = await screen.findByRole('button', { name: 'Select a resource' });
|
||||
expect(resourcePickerButton).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Expand Primary Subscription' })).not.toBeInTheDocument();
|
||||
resourcePickerButton.click();
|
||||
const resourcePickerButton = await screen.findByRole('button', { name: 'Select a resource' });
|
||||
expect(resourcePickerButton).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Expand Primary Subscription' })).not.toBeInTheDocument();
|
||||
resourcePickerButton.click();
|
||||
|
||||
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Primary Subscription' });
|
||||
expect(subscriptionButton).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Expand A Great Resource Group' })).not.toBeInTheDocument();
|
||||
subscriptionButton.click();
|
||||
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Primary Subscription' });
|
||||
expect(subscriptionButton).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Expand A Great Resource Group' })).not.toBeInTheDocument();
|
||||
subscriptionButton.click();
|
||||
|
||||
const resourceGroupButton = await screen.findByRole('button', { name: 'Expand A Great Resource Group' });
|
||||
expect(resourceGroupButton).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('web-server')).not.toBeInTheDocument();
|
||||
resourceGroupButton.click();
|
||||
const resourceGroupButton = await screen.findByRole('button', { name: 'Expand A Great Resource Group' });
|
||||
expect(resourceGroupButton).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('web-server')).not.toBeInTheDocument();
|
||||
resourceGroupButton.click();
|
||||
|
||||
const checkbox = await screen.findByLabelText('web-server');
|
||||
expect(checkbox).toBeInTheDocument();
|
||||
expect(checkbox).not.toBeChecked();
|
||||
await userEvent.click(checkbox);
|
||||
expect(checkbox).toBeChecked();
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
|
||||
const checkbox = await screen.findByLabelText('web-server');
|
||||
expect(checkbox).toBeInTheDocument();
|
||||
expect(checkbox).not.toBeChecked();
|
||||
await userEvent.click(checkbox);
|
||||
expect(checkbox).toBeChecked();
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
|
||||
|
||||
expect(onChange).toBeCalledTimes(1);
|
||||
expect(onChange).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
azureMonitor: expect.objectContaining({
|
||||
resourceUri:
|
||||
'/subscriptions/def-456/resourceGroups/dev-3/providers/Microsoft.Compute/virtualMachines/web-server',
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
expect(onChange).toBeCalledTimes(1);
|
||||
expect(onChange).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
azureMonitor: expect.objectContaining({
|
||||
resourceUri:
|
||||
'/subscriptions/def-456/resourceGroups/dev-3/providers/Microsoft.Compute/virtualMachines/web-server',
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should reset metric namespace, metric name, and aggregation fields after selecting a new resource when a valid query has already been set', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const query = createMockQuery();
|
||||
const onChange = jest.fn();
|
||||
it('should reset metric namespace, metric name, and aggregation fields after selecting a new resource when a valid query has already been set', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const query = createMockQuery();
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(
|
||||
<MetricsQueryEditor
|
||||
data={mockPanelData}
|
||||
query={query}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
render(
|
||||
<MetricsQueryEditor
|
||||
data={mockPanelData}
|
||||
query={query}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
const resourcePickerButton = await screen.findByRole('button', { name: /grafana/ });
|
||||
const resourcePickerButton = await screen.findByRole('button', { name: /grafana/ });
|
||||
|
||||
expect(screen.getByText('Microsoft.Compute/virtualMachines')).toBeInTheDocument();
|
||||
expect(screen.getByText('Metric A')).toBeInTheDocument();
|
||||
expect(screen.getByText('Average')).toBeInTheDocument();
|
||||
expect(screen.getByText('Microsoft.Compute/virtualMachines')).toBeInTheDocument();
|
||||
expect(screen.getByText('Metric A')).toBeInTheDocument();
|
||||
expect(screen.getByText('Average')).toBeInTheDocument();
|
||||
|
||||
expect(resourcePickerButton).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Expand Primary Subscription' })).not.toBeInTheDocument();
|
||||
resourcePickerButton.click();
|
||||
expect(resourcePickerButton).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Expand Primary Subscription' })).not.toBeInTheDocument();
|
||||
resourcePickerButton.click();
|
||||
|
||||
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Dev Subscription' });
|
||||
expect(subscriptionButton).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Expand Development 3' })).not.toBeInTheDocument();
|
||||
subscriptionButton.click();
|
||||
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Dev Subscription' });
|
||||
expect(subscriptionButton).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Expand Development 3' })).not.toBeInTheDocument();
|
||||
subscriptionButton.click();
|
||||
|
||||
const resourceGroupButton = await screen.findByRole('button', { name: 'Expand Development 3' });
|
||||
expect(resourceGroupButton).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('db-server')).not.toBeInTheDocument();
|
||||
resourceGroupButton.click();
|
||||
const resourceGroupButton = await screen.findByRole('button', { name: 'Expand Development 3' });
|
||||
expect(resourceGroupButton).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('db-server')).not.toBeInTheDocument();
|
||||
resourceGroupButton.click();
|
||||
|
||||
const checkbox = await screen.findByLabelText('db-server');
|
||||
expect(checkbox).toBeInTheDocument();
|
||||
expect(checkbox).not.toBeChecked();
|
||||
await userEvent.click(checkbox);
|
||||
expect(checkbox).toBeChecked();
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
|
||||
const checkbox = await screen.findByLabelText('db-server');
|
||||
expect(checkbox).toBeInTheDocument();
|
||||
expect(checkbox).not.toBeChecked();
|
||||
await userEvent.click(checkbox);
|
||||
expect(checkbox).toBeChecked();
|
||||
await userEvent.click(await screen.findByRole('button', { name: 'Apply' }));
|
||||
|
||||
expect(onChange).toBeCalledTimes(1);
|
||||
expect(onChange).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
azureMonitor: expect.objectContaining({
|
||||
resourceUri:
|
||||
'/subscriptions/def-456/resourceGroups/dev-3/providers/Microsoft.Compute/virtualMachines/db-server',
|
||||
metricNamespace: undefined,
|
||||
metricName: undefined,
|
||||
aggregation: undefined,
|
||||
timeGrain: '',
|
||||
dimensionFilters: [],
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should change the metric name when selected', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const onChange = jest.fn();
|
||||
const mockQuery = createMockQuery();
|
||||
mockDatasource.azureMonitorDatasource.getMetricNames = jest.fn().mockResolvedValue([
|
||||
{
|
||||
value: 'metric-a',
|
||||
text: 'Metric A',
|
||||
},
|
||||
{
|
||||
value: 'metric-b',
|
||||
text: 'Metric B',
|
||||
},
|
||||
]);
|
||||
|
||||
render(
|
||||
<MetricsQueryEditor
|
||||
data={mockPanelData}
|
||||
query={createMockQuery()}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
const metrics = await screen.findByLabelText('Metric');
|
||||
expect(metrics).toBeInTheDocument();
|
||||
await selectOptionInTest(metrics, 'Metric B');
|
||||
|
||||
expect(onChange).toHaveBeenLastCalledWith({
|
||||
...mockQuery,
|
||||
azureMonitor: {
|
||||
...mockQuery.azureMonitor,
|
||||
metricName: 'metric-b',
|
||||
expect(onChange).toBeCalledTimes(1);
|
||||
expect(onChange).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
azureMonitor: expect.objectContaining({
|
||||
resourceUri:
|
||||
'/subscriptions/def-456/resourceGroups/dev-3/providers/Microsoft.Compute/virtualMachines/db-server',
|
||||
metricNamespace: undefined,
|
||||
metricName: undefined,
|
||||
aggregation: undefined,
|
||||
timeGrain: '',
|
||||
},
|
||||
});
|
||||
});
|
||||
dimensionFilters: [],
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should change the aggregation type when selected', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const onChange = jest.fn();
|
||||
const mockQuery = createMockQuery();
|
||||
it('should change the metric name when selected', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const onChange = jest.fn();
|
||||
const mockQuery = createMockQuery();
|
||||
mockDatasource.azureMonitorDatasource.getMetricNames = jest.fn().mockResolvedValue([
|
||||
{
|
||||
value: 'metric-a',
|
||||
text: 'Metric A',
|
||||
},
|
||||
{
|
||||
value: 'metric-b',
|
||||
text: 'Metric B',
|
||||
},
|
||||
]);
|
||||
|
||||
render(
|
||||
<MetricsQueryEditor
|
||||
data={mockPanelData}
|
||||
query={createMockQuery()}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
render(
|
||||
<MetricsQueryEditor
|
||||
data={mockPanelData}
|
||||
query={createMockQuery()}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
const aggregation = await screen.findByLabelText('Aggregation');
|
||||
expect(aggregation).toBeInTheDocument();
|
||||
await selectOptionInTest(aggregation, 'Maximum');
|
||||
const metrics = await screen.findByLabelText('Metric');
|
||||
expect(metrics).toBeInTheDocument();
|
||||
await selectOptionInTest(metrics, 'Metric B');
|
||||
|
||||
expect(onChange).toHaveBeenLastCalledWith({
|
||||
...mockQuery,
|
||||
azureMonitor: {
|
||||
...mockQuery.azureMonitor,
|
||||
aggregation: 'Maximum',
|
||||
},
|
||||
});
|
||||
expect(onChange).toHaveBeenLastCalledWith({
|
||||
...mockQuery,
|
||||
azureMonitor: {
|
||||
...mockQuery.azureMonitor,
|
||||
metricName: 'metric-b',
|
||||
aggregation: undefined,
|
||||
timeGrain: '',
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it('should change the aggregation type when selected', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const onChange = jest.fn();
|
||||
const mockQuery = createMockQuery();
|
||||
|
||||
render(
|
||||
<MetricsQueryEditor
|
||||
data={mockPanelData}
|
||||
query={createMockQuery()}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
const aggregation = await screen.findByLabelText('Aggregation');
|
||||
expect(aggregation).toBeInTheDocument();
|
||||
await selectOptionInTest(aggregation, 'Maximum');
|
||||
|
||||
expect(onChange).toHaveBeenLastCalledWith({
|
||||
...mockQuery,
|
||||
azureMonitor: {
|
||||
...mockQuery.azureMonitor,
|
||||
aggregation: 'Maximum',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,10 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { PanelData } from '@grafana/data/src/types';
|
||||
import { EditorRows, EditorRow, EditorFieldGroup } from '@grafana/experimental';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { InlineFieldRow, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import type Datasource from '../../datasource';
|
||||
import type { AzureMonitorQuery, AzureMonitorOption, AzureMonitorErrorish } from '../../types';
|
||||
@ -16,7 +13,6 @@ import DimensionFields from './DimensionFields';
|
||||
import LegendFormatField from './LegendFormatField';
|
||||
import MetricNameField from './MetricNameField';
|
||||
import MetricNamespaceField from './MetricNamespaceField';
|
||||
import NewDimensionFields from './NewDimensionFields';
|
||||
import TimeGrainField from './TimeGrainField';
|
||||
import TopField from './TopField';
|
||||
import { useMetricNames, useMetricNamespaces, useMetricMetadata } from './dataHooks';
|
||||
@ -39,187 +35,94 @@ const MetricsQueryEditor: React.FC<MetricsQueryEditorProps> = ({
|
||||
onChange,
|
||||
setError,
|
||||
}) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const metricsMetadata = useMetricMetadata(query, datasource, onChange);
|
||||
const metricNamespaces = useMetricNamespaces(query, datasource, onChange, setError);
|
||||
const metricNames = useMetricNames(query, datasource, onChange, setError);
|
||||
if (config.featureToggles.azureMonitorExperimentalUI) {
|
||||
return (
|
||||
<span data-testid="azure-monitor-metrics-query-editor-with-experimental-ui">
|
||||
<EditorRows>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<ResourceField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
selectableEntryTypes={[ResourceRowType.Resource]}
|
||||
setResource={setResource}
|
||||
resourceUri={query.azureMonitor?.resourceUri}
|
||||
queryType={'metrics'}
|
||||
/>
|
||||
<MetricNamespaceField
|
||||
metricNamespaces={metricNamespaces}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
<MetricNameField
|
||||
metricNames={metricNames}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<AggregationField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
aggregationOptions={metricsMetadata?.aggOptions ?? []}
|
||||
isLoading={metricsMetadata.isLoading}
|
||||
/>
|
||||
<TimeGrainField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
timeGrainOptions={metricsMetadata?.timeGrains ?? []}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<NewDimensionFields
|
||||
data={data}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
dimensionOptions={metricsMetadata?.dimensions ?? []}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<TopField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
<LegendFormatField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
</EditorRows>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div data-testid="azure-monitor-metrics-query-editor-with-resource-picker">
|
||||
<InlineFieldRow className={styles.row}>
|
||||
<ResourceField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
selectableEntryTypes={[ResourceRowType.Resource]}
|
||||
setResource={setResource}
|
||||
resourceUri={query.azureMonitor?.resourceUri}
|
||||
queryType="metrics"
|
||||
/>
|
||||
</InlineFieldRow>
|
||||
|
||||
<InlineFieldRow className={styles.row}>
|
||||
<MetricNamespaceField
|
||||
metricNamespaces={metricNamespaces}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
<MetricNameField
|
||||
metricNames={metricNames}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</InlineFieldRow>
|
||||
<InlineFieldRow className={styles.row}>
|
||||
<AggregationField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
aggregationOptions={metricsMetadata?.aggOptions ?? []}
|
||||
isLoading={metricsMetadata.isLoading}
|
||||
/>
|
||||
<TimeGrainField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
timeGrainOptions={metricsMetadata?.timeGrains ?? []}
|
||||
/>
|
||||
</InlineFieldRow>
|
||||
<DimensionFields
|
||||
data={data}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
dimensionOptions={metricsMetadata?.dimensions ?? []}
|
||||
/>
|
||||
<TopField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
<LegendFormatField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span data-testid="azure-monitor-metrics-query-editor-with-experimental-ui">
|
||||
<EditorRows>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<ResourceField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
selectableEntryTypes={[ResourceRowType.Resource]}
|
||||
setResource={setResource}
|
||||
resourceUri={query.azureMonitor?.resourceUri}
|
||||
queryType={'metrics'}
|
||||
/>
|
||||
<MetricNamespaceField
|
||||
metricNamespaces={metricNamespaces}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
<MetricNameField
|
||||
metricNames={metricNames}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
<AggregationField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
aggregationOptions={metricsMetadata?.aggOptions ?? []}
|
||||
isLoading={metricsMetadata.isLoading}
|
||||
/>
|
||||
<TimeGrainField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
timeGrainOptions={metricsMetadata?.timeGrains ?? []}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<DimensionFields
|
||||
data={data}
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
dimensionOptions={metricsMetadata?.dimensions ?? []}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<TopField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
<LegendFormatField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
</EditorRows>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = () => ({
|
||||
row: css({
|
||||
rowGap: 0,
|
||||
}),
|
||||
});
|
||||
|
||||
export default MetricsQueryEditor;
|
||||
|
@ -1,206 +0,0 @@
|
||||
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;
|
@ -58,7 +58,6 @@ const TimeGrainField: React.FC<TimeGrainFieldProps> = ({
|
||||
value={query.azureMonitor?.timeGrain}
|
||||
onChange={handleChange}
|
||||
options={timeGrains}
|
||||
width={38}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
|
@ -2,7 +2,6 @@ import { render, screen, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { selectOptionInTest } from 'test/helpers/selectOptionInTest';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
import * as ui from '@grafana/ui';
|
||||
|
||||
import createMockDatasource from '../../__mocks__/datasource';
|
||||
@ -30,7 +29,7 @@ describe('Azure Monitor QueryEditor', () => {
|
||||
|
||||
render(<QueryEditor query={mockQuery} datasource={mockDatasource} onChange={() => {}} onRunQuery={() => {}} />);
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('azure-monitor-metrics-query-editor-with-resource-picker')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('azure-monitor-metrics-query-editor-with-experimental-ui')).toBeInTheDocument()
|
||||
);
|
||||
});
|
||||
|
||||
@ -42,7 +41,9 @@ describe('Azure Monitor QueryEditor', () => {
|
||||
};
|
||||
|
||||
render(<QueryEditor query={mockQuery} datasource={mockDatasource} onChange={() => {}} onRunQuery={() => {}} />);
|
||||
await waitFor(() => expect(screen.queryByTestId('azure-monitor-logs-query-editor')).toBeInTheDocument());
|
||||
await waitFor(() =>
|
||||
expect(screen.queryByTestId('azure-monitor-logs-query-editor-with-experimental-ui')).toBeInTheDocument()
|
||||
);
|
||||
});
|
||||
|
||||
it('changes the query type when selected', async () => {
|
||||
@ -52,7 +53,7 @@ describe('Azure Monitor QueryEditor', () => {
|
||||
render(<QueryEditor query={mockQuery} datasource={mockDatasource} onChange={onChange} onRunQuery={() => {}} />);
|
||||
await waitFor(() => expect(screen.getByTestId('azure-monitor-query-editor')).toBeInTheDocument());
|
||||
|
||||
const metrics = await screen.findByLabelText('Service');
|
||||
const metrics = await screen.findByLabelText(/Service/);
|
||||
await selectOptionInTest(metrics, 'Logs');
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith({
|
||||
@ -68,16 +69,12 @@ describe('Azure Monitor QueryEditor', () => {
|
||||
<QueryEditor query={createMockQuery()} datasource={mockDatasource} onChange={() => {}} onRunQuery={() => {}} />
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('azure-monitor-metrics-query-editor-with-resource-picker')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('azure-monitor-metrics-query-editor-with-experimental-ui')).toBeInTheDocument()
|
||||
);
|
||||
expect(screen.getByText('An error occurred while requesting metadata from Azure Monitor')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the experimental QueryHeader when feature toggle is enabled', async () => {
|
||||
const originalConfigValue = config.featureToggles.azureMonitorExperimentalUI;
|
||||
|
||||
config.featureToggles.azureMonitorExperimentalUI = true;
|
||||
|
||||
const mockDatasource = createMockDatasource();
|
||||
const mockQuery = {
|
||||
...createMockQuery(),
|
||||
@ -87,19 +84,5 @@ describe('Azure Monitor QueryEditor', () => {
|
||||
render(<QueryEditor query={mockQuery} datasource={mockDatasource} onChange={() => {}} onRunQuery={() => {}} />);
|
||||
|
||||
await waitFor(() => expect(screen.getByTestId('azure-monitor-experimental-header')).toBeInTheDocument());
|
||||
|
||||
config.featureToggles.azureMonitorExperimentalUI = originalConfigValue;
|
||||
});
|
||||
|
||||
it('should not render the experimental QueryHeader when feature toggle is disabled', async () => {
|
||||
const mockDatasource = createMockDatasource();
|
||||
const mockQuery = {
|
||||
...createMockQuery(),
|
||||
queryType: AzureQueryType.AzureMonitor,
|
||||
};
|
||||
|
||||
render(<QueryEditor query={mockQuery} datasource={mockDatasource} onChange={() => {}} onRunQuery={() => {}} />);
|
||||
|
||||
await waitFor(() => expect(screen.queryByTestId('azure-monitor-experimental-header')).not.toBeInTheDocument());
|
||||
});
|
||||
});
|
||||
|
@ -2,7 +2,6 @@ import { debounce } from 'lodash';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import { QueryEditorProps } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Alert, CodeEditor } from '@grafana/ui';
|
||||
|
||||
import AzureMonitorDatasource from '../../datasource';
|
||||
@ -20,7 +19,6 @@ import NewMetricsQueryEditor from '../MetricsQueryEditor/MetricsQueryEditor';
|
||||
import { QueryHeader } from '../QueryHeader';
|
||||
import { Space } from '../Space';
|
||||
|
||||
import QueryTypeField from './QueryTypeField';
|
||||
import usePreparedQuery from './usePreparedQuery';
|
||||
|
||||
export type AzureMonitorQueryEditorProps = QueryEditorProps<
|
||||
@ -57,10 +55,7 @@ const QueryEditor: React.FC<AzureMonitorQueryEditorProps> = ({
|
||||
|
||||
return (
|
||||
<div data-testid="azure-monitor-query-editor">
|
||||
{config.featureToggles.azureMonitorExperimentalUI && <QueryHeader query={query} onQueryChange={onQueryChange} />}
|
||||
{!config.featureToggles.azureMonitorExperimentalUI && (
|
||||
<QueryTypeField query={query} onQueryChange={onQueryChange} />
|
||||
)}
|
||||
<QueryHeader query={query} onQueryChange={onQueryChange} />
|
||||
|
||||
<EditorForQueryType
|
||||
data={data}
|
||||
|
@ -1,45 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { Select } from '@grafana/ui';
|
||||
|
||||
import { AzureMonitorQuery, AzureQueryType } from '../../types';
|
||||
import { Field } from '../Field';
|
||||
|
||||
interface QueryTypeFieldProps {
|
||||
query: AzureMonitorQuery;
|
||||
onQueryChange: (newQuery: AzureMonitorQuery) => void;
|
||||
}
|
||||
|
||||
const QueryTypeField: React.FC<QueryTypeFieldProps> = ({ query, onQueryChange }) => {
|
||||
const queryTypes: Array<{ value: AzureQueryType; label: string }> = [
|
||||
{ value: AzureQueryType.AzureMonitor, label: 'Metrics' },
|
||||
{ value: AzureQueryType.LogAnalytics, label: 'Logs' },
|
||||
{ value: AzureQueryType.AzureResourceGraph, label: 'Azure Resource Graph' },
|
||||
];
|
||||
|
||||
const handleChange = useCallback(
|
||||
(change: SelectableValue<AzureQueryType>) => {
|
||||
change.value &&
|
||||
onQueryChange({
|
||||
...query,
|
||||
queryType: change.value,
|
||||
});
|
||||
},
|
||||
[onQueryChange, query]
|
||||
);
|
||||
|
||||
return (
|
||||
<Field label="Service">
|
||||
<Select
|
||||
inputId="azure-monitor-query-type-field"
|
||||
value={query.queryType}
|
||||
options={queryTypes}
|
||||
onChange={handleChange}
|
||||
width={38}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
||||
export default QueryTypeField;
|
Loading…
Reference in New Issue
Block a user