mirror of
https://github.com/grafana/grafana.git
synced 2024-11-22 08:56:43 -06:00
Cloud Monitoring: Update GroupBy fields to use experimental UI components (#50541)
* Cloud Monitoring: Update GroupBy fields to use experimental UI components * let group by field grow horizontally * remove fixed width constants from inputs * add test * Cloud Monitoring: Update GraphPeriod to use experimental UI components (#50545) * Cloud Monitoring: Update GraphPeriod to use experimental UI components * Cloud Monitoring: Update Preprocessing to use experimental UI components (#50548) * Cloud Monitoring: Update Preprocessing to use experimental UI components * add tests * make overrides optional * move preprocessor back into its own row
This commit is contained in:
parent
902101c524
commit
e889dfdc5c
@ -252,7 +252,7 @@
|
||||
"@grafana/aws-sdk": "0.0.36",
|
||||
"@grafana/data": "workspace:*",
|
||||
"@grafana/e2e-selectors": "workspace:*",
|
||||
"@grafana/experimental": "^0.0.2-canary.30",
|
||||
"@grafana/experimental": "^0.0.2-canary.32",
|
||||
"@grafana/google-sdk": "0.0.3",
|
||||
"@grafana/lezer-logql": "^0.0.12",
|
||||
"@grafana/runtime": "workspace:*",
|
||||
|
@ -0,0 +1,15 @@
|
||||
import { MetricDescriptor, MetricKind, ValueTypes } from '../types';
|
||||
|
||||
export const createMockMetricDescriptor = (overrides?: Partial<MetricDescriptor>): MetricDescriptor => {
|
||||
return {
|
||||
metricKind: MetricKind.CUMULATIVE,
|
||||
valueType: ValueTypes.DOUBLE,
|
||||
type: 'type',
|
||||
unit: 'unit',
|
||||
service: 'service',
|
||||
serviceShortName: 'srv',
|
||||
displayName: 'displayName',
|
||||
description: 'description',
|
||||
...overrides,
|
||||
};
|
||||
};
|
@ -0,0 +1,65 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { openMenu } from 'react-select-event';
|
||||
import { TemplateSrvStub } from 'test/specs/helpers';
|
||||
|
||||
import { ValueTypes, MetricKind } from '../../types';
|
||||
|
||||
import { Aggregation, Props } from './Aggregation';
|
||||
|
||||
const props: Props = {
|
||||
onChange: () => {},
|
||||
// @ts-ignore
|
||||
templateSrv: new TemplateSrvStub(),
|
||||
metricDescriptor: {
|
||||
valueType: '',
|
||||
metricKind: '',
|
||||
} as any,
|
||||
crossSeriesReducer: '',
|
||||
groupBys: [],
|
||||
templateVariableOptions: [],
|
||||
};
|
||||
|
||||
describe('Aggregation', () => {
|
||||
it('renders correctly', () => {
|
||||
render(<Aggregation {...props} />);
|
||||
expect(screen.getByTestId('cloud-monitoring-aggregation')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('options', () => {
|
||||
describe('when DOUBLE and GAUGE is passed as props', () => {
|
||||
const nextProps = {
|
||||
...props,
|
||||
metricDescriptor: {
|
||||
valueType: ValueTypes.DOUBLE,
|
||||
metricKind: MetricKind.GAUGE,
|
||||
} as any,
|
||||
};
|
||||
|
||||
it('should not have the reduce values', () => {
|
||||
render(<Aggregation {...nextProps} />);
|
||||
const label = screen.getByLabelText('Group by function');
|
||||
openMenu(label);
|
||||
expect(screen.queryByText('count true')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('count false')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when MONEY and CUMULATIVE is passed as props', () => {
|
||||
const nextProps = {
|
||||
...props,
|
||||
metricDescriptor: {
|
||||
valueType: ValueTypes.MONEY,
|
||||
metricKind: MetricKind.CUMULATIVE,
|
||||
} as any,
|
||||
};
|
||||
|
||||
it('should have the reduce values', () => {
|
||||
render(<Aggregation {...nextProps} />);
|
||||
const label = screen.getByLabelText('Group by function');
|
||||
openMenu(label);
|
||||
expect(screen.getByText('none')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,68 @@
|
||||
import React, { FC, useMemo } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { EditorField } from '@grafana/experimental';
|
||||
import { Select } from '@grafana/ui';
|
||||
|
||||
import { getAggregationOptionsByMetric } from '../../functions';
|
||||
import { MetricDescriptor, MetricKind, ValueTypes } from '../../types';
|
||||
|
||||
export interface Props {
|
||||
refId: string;
|
||||
onChange: (metricDescriptor: string) => void;
|
||||
metricDescriptor?: MetricDescriptor;
|
||||
crossSeriesReducer: string;
|
||||
groupBys: string[];
|
||||
templateVariableOptions: Array<SelectableValue<string>>;
|
||||
}
|
||||
|
||||
export const Aggregation: FC<Props> = (props) => {
|
||||
const aggOptions = useAggregationOptionsByMetric(props);
|
||||
const selected = useSelectedFromOptions(aggOptions, props);
|
||||
|
||||
return (
|
||||
<EditorField label="Group by function" data-testid="cloud-monitoring-aggregation">
|
||||
<Select
|
||||
width="auto"
|
||||
onChange={({ value }) => props.onChange(value!)}
|
||||
value={selected}
|
||||
options={[
|
||||
{
|
||||
label: 'Template Variables',
|
||||
options: props.templateVariableOptions,
|
||||
},
|
||||
{
|
||||
label: 'Aggregations',
|
||||
expanded: true,
|
||||
options: aggOptions,
|
||||
},
|
||||
]}
|
||||
placeholder="Select Reducer"
|
||||
inputId={`${props.refId}-group-by-function`}
|
||||
/>
|
||||
</EditorField>
|
||||
);
|
||||
};
|
||||
|
||||
const useAggregationOptionsByMetric = ({ metricDescriptor }: Props): Array<SelectableValue<string>> => {
|
||||
const valueType = metricDescriptor?.valueType;
|
||||
const metricKind = metricDescriptor?.metricKind;
|
||||
|
||||
return useMemo(() => {
|
||||
if (!valueType || !metricKind) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return getAggregationOptionsByMetric(valueType as ValueTypes, metricKind as MetricKind).map((a) => ({
|
||||
...a,
|
||||
label: a.text,
|
||||
}));
|
||||
}, [valueType, metricKind]);
|
||||
};
|
||||
|
||||
const useSelectedFromOptions = (aggOptions: Array<SelectableValue<string>>, props: Props) => {
|
||||
return useMemo(() => {
|
||||
const allOptions = [...aggOptions, ...props.templateVariableOptions];
|
||||
return allOptions.find((s) => s.value === props.crossSeriesReducer);
|
||||
}, [aggOptions, props.crossSeriesReducer, props.templateVariableOptions]);
|
||||
};
|
@ -0,0 +1,39 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { select } from 'react-select-event';
|
||||
|
||||
import { GraphPeriod, Props } from './GraphPeriod';
|
||||
|
||||
const props: Props = {
|
||||
onChange: jest.fn(),
|
||||
refId: 'A',
|
||||
variableOptionGroup: { options: [] },
|
||||
};
|
||||
|
||||
describe('Graph Period', () => {
|
||||
it('should enable graph_period by default', () => {
|
||||
render(<GraphPeriod {...props} />);
|
||||
expect(screen.getByLabelText('Graph period')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it('should disable graph_period when toggled', async () => {
|
||||
const onChange = jest.fn();
|
||||
render(<GraphPeriod {...props} onChange={onChange} />);
|
||||
const s = screen.getByTestId('A-switch-graph-period');
|
||||
await userEvent.click(s);
|
||||
expect(onChange).toHaveBeenCalledWith('disabled');
|
||||
});
|
||||
|
||||
it('should set a different value when selected', async () => {
|
||||
const onChange = jest.fn();
|
||||
render(<GraphPeriod {...props} onChange={onChange} />);
|
||||
const selectEl = screen.getByLabelText('Graph period');
|
||||
expect(selectEl).toBeInTheDocument();
|
||||
|
||||
await select(selectEl, '1m', {
|
||||
container: document.body,
|
||||
});
|
||||
expect(onChange).toHaveBeenCalledWith('1m');
|
||||
});
|
||||
});
|
@ -0,0 +1,49 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { EditorField, EditorRow } from '@grafana/experimental';
|
||||
import { HorizontalGroup, Switch } from '@grafana/ui';
|
||||
|
||||
import { GRAPH_PERIODS, SELECT_WIDTH } from '../../constants';
|
||||
import { PeriodSelect } from '../index';
|
||||
|
||||
export interface Props {
|
||||
refId: string;
|
||||
onChange: (period: string) => void;
|
||||
variableOptionGroup: SelectableValue<string>;
|
||||
graphPeriod?: string;
|
||||
}
|
||||
|
||||
export const GraphPeriod: FunctionComponent<Props> = ({ refId, onChange, graphPeriod, variableOptionGroup }) => {
|
||||
return (
|
||||
<EditorRow>
|
||||
<EditorField
|
||||
label="Graph period"
|
||||
htmlFor={`${refId}-graph-period`}
|
||||
tooltip={
|
||||
<>
|
||||
Set <code>graph_period</code> which forces a preferred period between points. Automatically set to the
|
||||
current interval if left blank.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<HorizontalGroup>
|
||||
<Switch
|
||||
data-testid={`${refId}-switch-graph-period`}
|
||||
value={graphPeriod !== 'disabled'}
|
||||
onChange={(e) => onChange(e.currentTarget.checked ? '' : 'disabled')}
|
||||
/>
|
||||
<PeriodSelect
|
||||
inputId={`${refId}-graph-period`}
|
||||
templateVariableOptions={variableOptionGroup.options}
|
||||
current={graphPeriod}
|
||||
onChange={onChange}
|
||||
selectWidth={SELECT_WIDTH}
|
||||
disabled={graphPeriod === 'disabled'}
|
||||
aligmentPeriods={GRAPH_PERIODS}
|
||||
/>
|
||||
</HorizontalGroup>
|
||||
</EditorField>
|
||||
</EditorRow>
|
||||
);
|
||||
};
|
@ -0,0 +1,42 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { openMenu, select } from 'react-select-event';
|
||||
|
||||
import { createMockMetricQuery } from '../../__mocks__/cloudMonitoringQuery';
|
||||
|
||||
import { GroupBy, Props } from './GroupBy';
|
||||
|
||||
const props: Props = {
|
||||
onChange: jest.fn(),
|
||||
refId: 'refId',
|
||||
metricDescriptor: {
|
||||
valueType: '',
|
||||
metricKind: '',
|
||||
} as any,
|
||||
variableOptionGroup: { options: [] },
|
||||
labels: [],
|
||||
query: createMockMetricQuery(),
|
||||
};
|
||||
|
||||
describe('GroupBy', () => {
|
||||
it('renders group by fields', () => {
|
||||
render(<GroupBy {...props} />);
|
||||
expect(screen.getByLabelText('Group by')).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('Group by function')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('can select a group by', async () => {
|
||||
const onChange = jest.fn();
|
||||
render(<GroupBy {...props} onChange={onChange} />);
|
||||
|
||||
const groupBy = screen.getByLabelText('Group by');
|
||||
const option = 'metadata.system_labels.cloud_account';
|
||||
|
||||
expect(screen.queryByText(option)).not.toBeInTheDocument();
|
||||
await openMenu(groupBy);
|
||||
expect(screen.getByText(option)).toBeInTheDocument();
|
||||
|
||||
await select(groupBy, option, { container: document.body });
|
||||
expect(onChange).toBeCalledWith(expect.objectContaining({ groupBys: expect.arrayContaining([option]) }));
|
||||
});
|
||||
});
|
@ -0,0 +1,64 @@
|
||||
import React, { FunctionComponent, useMemo } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { EditorField, EditorFieldGroup, EditorRow } from '@grafana/experimental';
|
||||
import { MultiSelect } from '@grafana/ui';
|
||||
|
||||
import { SYSTEM_LABELS } from '../../constants';
|
||||
import { labelsToGroupedOptions } from '../../functions';
|
||||
import { MetricDescriptor, MetricQuery } from '../../types';
|
||||
|
||||
import { Aggregation } from './Aggregation';
|
||||
|
||||
export interface Props {
|
||||
refId: string;
|
||||
variableOptionGroup: SelectableValue<string>;
|
||||
labels: string[];
|
||||
metricDescriptor?: MetricDescriptor;
|
||||
onChange: (query: MetricQuery) => void;
|
||||
query: MetricQuery;
|
||||
}
|
||||
|
||||
export const GroupBy: FunctionComponent<Props> = ({
|
||||
refId,
|
||||
labels: groupBys = [],
|
||||
query,
|
||||
onChange,
|
||||
variableOptionGroup,
|
||||
metricDescriptor,
|
||||
}) => {
|
||||
const options = useMemo(
|
||||
() => [variableOptionGroup, ...labelsToGroupedOptions([...groupBys, ...SYSTEM_LABELS])],
|
||||
[groupBys, variableOptionGroup]
|
||||
);
|
||||
|
||||
return (
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<EditorField
|
||||
label="Group by"
|
||||
tooltip="You can reduce the amount of data returned for a metric by combining different time series. To combine multiple time series, you can specify a grouping and a function. Grouping is done on the basis of labels. The grouping function is used to combine the time series in the group into a single time series."
|
||||
>
|
||||
<MultiSelect
|
||||
inputId={`${refId}-group-by`}
|
||||
width="auto"
|
||||
placeholder="Choose label"
|
||||
options={options}
|
||||
value={query.groupBys ?? []}
|
||||
onChange={(options) => {
|
||||
onChange({ ...query, groupBys: options.map((o) => o.value!) });
|
||||
}}
|
||||
/>
|
||||
</EditorField>
|
||||
<Aggregation
|
||||
metricDescriptor={metricDescriptor}
|
||||
templateVariableOptions={variableOptionGroup.options}
|
||||
crossSeriesReducer={query.crossSeriesReducer}
|
||||
groupBys={query.groupBys ?? []}
|
||||
onChange={(crossSeriesReducer) => onChange({ ...query, crossSeriesReducer })}
|
||||
refId={refId}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
);
|
||||
};
|
@ -18,9 +18,9 @@ import {
|
||||
} from '../../types';
|
||||
import { Project } from '../index';
|
||||
|
||||
import { GraphPeriod } from './../GraphPeriod';
|
||||
import { MQLQueryEditor } from './../MQLQueryEditor';
|
||||
import { AliasBy } from './AliasBy';
|
||||
import { GraphPeriod } from './GraphPeriod';
|
||||
import { VisualMetricQueryEditor } from './VisualMetricQueryEditor';
|
||||
|
||||
export interface Props {
|
||||
|
@ -0,0 +1,95 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
|
||||
import { TemplateSrvMock } from 'app/features/templating/template_srv.mock';
|
||||
|
||||
import { createMockMetricDescriptor } from '../../__mocks__/cloudMonitoringMetricDescriptor';
|
||||
import { createMockMetricQuery } from '../../__mocks__/cloudMonitoringQuery';
|
||||
import { MetricKind, ValueTypes } from '../../types';
|
||||
|
||||
import { Preprocessor } from './Preprocessor';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getTemplateSrv: () => new TemplateSrvMock({}),
|
||||
}));
|
||||
|
||||
describe('Preprocessor', () => {
|
||||
it('only provides "None" as an option if no metric descriptor is provided', () => {
|
||||
const query = createMockMetricQuery();
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(<Preprocessor onChange={onChange} query={query} />);
|
||||
expect(screen.getByText('Pre-processing')).toBeInTheDocument();
|
||||
expect(screen.getByText('None')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Rate')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Delta')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('only provides "None" as an option if metric kind is "Gauge"', () => {
|
||||
const query = createMockMetricQuery();
|
||||
const onChange = jest.fn();
|
||||
const metricDescriptor = createMockMetricDescriptor({ metricKind: MetricKind.GAUGE });
|
||||
|
||||
render(<Preprocessor onChange={onChange} query={query} metricDescriptor={metricDescriptor} />);
|
||||
expect(screen.getByText('Pre-processing')).toBeInTheDocument();
|
||||
expect(screen.getByText('None')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Rate')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Delta')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('only provides "None" as an option if value type is "Distribution"', () => {
|
||||
const query = createMockMetricQuery();
|
||||
const onChange = jest.fn();
|
||||
const metricDescriptor = createMockMetricDescriptor({ valueType: ValueTypes.DISTRIBUTION });
|
||||
|
||||
render(<Preprocessor onChange={onChange} query={query} metricDescriptor={metricDescriptor} />);
|
||||
expect(screen.getByText('Pre-processing')).toBeInTheDocument();
|
||||
expect(screen.getByText('None')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Rate')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Delta')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('provides "None" and "Rate" as options if metric kind is not "Delta" or "Cumulative" and value type is not "Distribution"', () => {
|
||||
const query = createMockMetricQuery();
|
||||
const onChange = jest.fn();
|
||||
const metricDescriptor = createMockMetricDescriptor({ metricKind: MetricKind.DELTA });
|
||||
|
||||
render(<Preprocessor onChange={onChange} query={query} metricDescriptor={metricDescriptor} />);
|
||||
expect(screen.getByText('Pre-processing')).toBeInTheDocument();
|
||||
expect(screen.getByText('None')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Rate')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Delta')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('provides all options if metric kind is "Cumulative" and value type is not "Distribution"', () => {
|
||||
const query = createMockMetricQuery();
|
||||
const onChange = jest.fn();
|
||||
const metricDescriptor = createMockMetricDescriptor({ metricKind: MetricKind.CUMULATIVE });
|
||||
|
||||
render(<Preprocessor onChange={onChange} query={query} metricDescriptor={metricDescriptor} />);
|
||||
expect(screen.getByText('Pre-processing')).toBeInTheDocument();
|
||||
expect(screen.getByText('None')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Rate')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Delta')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('provides all options if metric kind is "Cumulative" and value type is not "Distribution"', async () => {
|
||||
const query = createMockMetricQuery();
|
||||
const onChange = jest.fn();
|
||||
const metricDescriptor = createMockMetricDescriptor({ metricKind: MetricKind.CUMULATIVE });
|
||||
|
||||
render(<Preprocessor onChange={onChange} query={query} metricDescriptor={metricDescriptor} />);
|
||||
const none = screen.getByLabelText('None');
|
||||
const rate = screen.getByLabelText('Rate');
|
||||
const delta = screen.getByLabelText('Delta');
|
||||
expect(none).toBeChecked();
|
||||
expect(rate).not.toBeChecked();
|
||||
expect(delta).not.toBeChecked();
|
||||
|
||||
await userEvent.click(rate);
|
||||
|
||||
expect(onChange).toBeCalledWith(expect.objectContaining({ preprocessor: 'rate' }));
|
||||
});
|
||||
});
|
@ -0,0 +1,69 @@
|
||||
import React, { FunctionComponent, useMemo } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { EditorField, EditorRow } from '@grafana/experimental';
|
||||
import { RadioButtonGroup } from '@grafana/ui';
|
||||
|
||||
import { getAlignmentPickerData } from '../../functions';
|
||||
import { MetricDescriptor, MetricKind, MetricQuery, PreprocessorType, ValueTypes } from '../../types';
|
||||
|
||||
const NONE_OPTION = { label: 'None', value: PreprocessorType.None };
|
||||
|
||||
export interface Props {
|
||||
metricDescriptor?: MetricDescriptor;
|
||||
onChange: (query: MetricQuery) => void;
|
||||
query: MetricQuery;
|
||||
}
|
||||
|
||||
export const Preprocessor: FunctionComponent<Props> = ({ query, metricDescriptor, onChange }) => {
|
||||
const options = useOptions(metricDescriptor);
|
||||
return (
|
||||
<EditorRow>
|
||||
<EditorField
|
||||
label="Pre-processing"
|
||||
tooltip="Preprocessing options are displayed when the selected metric has a metric kind of delta or cumulative. The specific options available are determined by the metic's value type. If you select 'Rate', data points are aligned and converted to a rate per time series. If you select 'Delta', data points are aligned by their delta (difference) per time series"
|
||||
>
|
||||
<RadioButtonGroup
|
||||
onChange={(value: PreprocessorType) => {
|
||||
const { valueType, metricKind, perSeriesAligner: psa } = query;
|
||||
const { perSeriesAligner } = getAlignmentPickerData(valueType, metricKind, psa, value);
|
||||
onChange({ ...query, preprocessor: value, perSeriesAligner });
|
||||
}}
|
||||
value={query.preprocessor ?? PreprocessorType.None}
|
||||
options={options}
|
||||
/>
|
||||
</EditorField>
|
||||
</EditorRow>
|
||||
);
|
||||
};
|
||||
|
||||
const useOptions = (metricDescriptor?: MetricDescriptor): Array<SelectableValue<PreprocessorType>> => {
|
||||
const metricKind = metricDescriptor?.metricKind;
|
||||
const valueType = metricDescriptor?.valueType;
|
||||
|
||||
return useMemo(() => {
|
||||
if (!metricKind || metricKind === MetricKind.GAUGE || valueType === ValueTypes.DISTRIBUTION) {
|
||||
return [NONE_OPTION];
|
||||
}
|
||||
|
||||
const options = [
|
||||
NONE_OPTION,
|
||||
{
|
||||
label: 'Rate',
|
||||
value: PreprocessorType.Rate,
|
||||
description: 'Data points are aligned and converted to a rate per time series',
|
||||
},
|
||||
];
|
||||
|
||||
return metricKind === MetricKind.CUMULATIVE
|
||||
? [
|
||||
...options,
|
||||
{
|
||||
label: 'Delta',
|
||||
value: PreprocessorType.Delta,
|
||||
description: 'Data points are aligned by their delta (difference) per time series',
|
||||
},
|
||||
]
|
||||
: options;
|
||||
}, [metricKind, valueType]);
|
||||
};
|
@ -4,9 +4,11 @@ import { SelectableValue } from '@grafana/data';
|
||||
|
||||
import CloudMonitoringDatasource from '../../datasource';
|
||||
import { CustomMetaData, MetricDescriptor, MetricQuery, SLOQuery } from '../../types';
|
||||
import { GroupBy, LabelFilter, Metrics, Preprocessor } from '../index';
|
||||
import { LabelFilter, Metrics } from '../index';
|
||||
|
||||
import { Alignment } from './Alignment';
|
||||
import { GroupBy } from './GroupBy';
|
||||
import { Preprocessor } from './Preprocessor';
|
||||
|
||||
export interface Props {
|
||||
refId: string;
|
||||
|
10
yarn.lock
10
yarn.lock
@ -4463,9 +4463,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/experimental@npm:^0.0.2-canary.30":
|
||||
version: 0.0.2-canary.30
|
||||
resolution: "@grafana/experimental@npm:0.0.2-canary.30"
|
||||
"@grafana/experimental@npm:^0.0.2-canary.32":
|
||||
version: 0.0.2-canary.32
|
||||
resolution: "@grafana/experimental@npm:0.0.2-canary.32"
|
||||
dependencies:
|
||||
"@types/uuid": ^8.3.3
|
||||
uuid: ^8.3.2
|
||||
@ -4473,7 +4473,7 @@ __metadata:
|
||||
"@emotion/css": 11.1.3
|
||||
react: 17.0.1
|
||||
react-select: 5.2.1
|
||||
checksum: b5b453b9372cde8f89021c50ae1191a2506ebbb069ad6331d22a5c7267ecd8c0db7f9f5fdd9cfab49fafce4e0b6809609e67449b78fe3ccc63cedfeceb64b911
|
||||
checksum: 71924e6d03335fbedf1553ce08c2bbe95819ad746af48728aa81923f3f8ca0f0e8cf3db979095da5048f2b256e96fbb88aa8efb480e84913d81dd66faefeadec
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -20646,7 +20646,7 @@ __metadata:
|
||||
"@grafana/e2e": "workspace:*"
|
||||
"@grafana/e2e-selectors": "workspace:*"
|
||||
"@grafana/eslint-config": 3.0.0
|
||||
"@grafana/experimental": ^0.0.2-canary.30
|
||||
"@grafana/experimental": ^0.0.2-canary.32
|
||||
"@grafana/google-sdk": 0.0.3
|
||||
"@grafana/lezer-logql": ^0.0.12
|
||||
"@grafana/runtime": "workspace:*"
|
||||
|
Loading…
Reference in New Issue
Block a user