diff --git a/public/app/plugins/datasource/cloud-monitoring/__mocks__/cloudMonitoringQuery.ts b/public/app/plugins/datasource/cloud-monitoring/__mocks__/cloudMonitoringQuery.ts index d2943f326d8..a5aa7e49749 100644 --- a/public/app/plugins/datasource/cloud-monitoring/__mocks__/cloudMonitoringQuery.ts +++ b/public/app/plugins/datasource/cloud-monitoring/__mocks__/cloudMonitoringQuery.ts @@ -1,4 +1,4 @@ -import { CloudMonitoringQuery, EditorMode, MetricQuery, QueryType } from '../types'; +import { AlignmentTypes, CloudMonitoringQuery, EditorMode, MetricQuery, QueryType, SLOQuery } from '../types'; export const createMockMetricQuery: (overrides?: Partial) => MetricQuery = ( overrides?: Partial @@ -13,12 +13,29 @@ export const createMockMetricQuery: (overrides?: Partial) => Metric }; }; -export const createMockQuery: () => CloudMonitoringQuery = () => { +export const createMockSLOQuery: (overrides?: Partial) => SLOQuery = (overrides) => { + return { + projectName: 'projectName', + alignmentPeriod: 'cloud-monitoring-auto', + perSeriesAligner: AlignmentTypes.ALIGN_MEAN, + aliasBy: '', + selectorName: 'select_slo_health', + serviceId: '', + serviceName: '', + sloId: '', + sloName: '', + ...overrides, + }; +}; + +export const createMockQuery: (overrides?: Partial) => CloudMonitoringQuery = (overrides) => { return { refId: 'cloudMonitoringRefId', queryType: QueryType.METRICS, intervalMs: 0, type: 'timeSeriesQuery', - metricQuery: createMockMetricQuery(), + ...overrides, + metricQuery: createMockMetricQuery(overrides?.metricQuery), + sloQuery: createMockSLOQuery(overrides?.sloQuery), }; }; diff --git a/public/app/plugins/datasource/cloud-monitoring/components/Experimental/QueryHeader.test.tsx b/public/app/plugins/datasource/cloud-monitoring/components/Experimental/QueryHeader.test.tsx new file mode 100644 index 00000000000..d01b52661aa --- /dev/null +++ b/public/app/plugins/datasource/cloud-monitoring/components/Experimental/QueryHeader.test.tsx @@ -0,0 +1,107 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { openMenu, select } from 'react-select-event'; + +import { createMockQuery, createMockSLOQuery } from '../../__mocks__/cloudMonitoringQuery'; +import { EditorMode, QueryType } from '../../types'; + +import { QueryHeader } from './QueryHeader'; + +describe('QueryHeader', () => { + it('renders an editor mode radio group if query type is a metric query', () => { + const query = createMockQuery(); + const { metricQuery } = query; + const sloQuery = createMockSLOQuery(); + const onChange = jest.fn(); + const onRunQuery = jest.fn(); + + render( + + ); + + expect(screen.getByLabelText(/Query type/)).toBeInTheDocument(); + expect(screen.getByLabelText('Builder')).toBeInTheDocument(); + expect(screen.getByLabelText('MQL')).toBeInTheDocument(); + }); + + it('does not render an editor mode radio group if query type is a SLO query', () => { + const query = createMockQuery({ queryType: QueryType.SLO }); + const { metricQuery } = query; + const sloQuery = createMockSLOQuery(); + const onChange = jest.fn(); + const onRunQuery = jest.fn(); + + render( + + ); + + expect(screen.getByLabelText(/Query type/)).toBeInTheDocument(); + expect(screen.queryByLabelText('Builder')).not.toBeInTheDocument(); + expect(screen.queryByLabelText('MQL')).not.toBeInTheDocument(); + }); + + it('can change query types', async () => { + const query = createMockQuery(); + const { metricQuery } = query; + const sloQuery = createMockSLOQuery(); + const onChange = jest.fn(); + const onRunQuery = jest.fn(); + + render( + + ); + + const queryType = screen.getByLabelText(/Query type/); + await openMenu(queryType); + await select(screen.getByLabelText('Select options menu'), 'Service Level Objectives (SLO)'); + expect(onChange).toBeCalledWith(expect.objectContaining({ queryType: QueryType.SLO })); + }); + + it('can change editor modes when query is a metric query type', async () => { + const query = createMockQuery(); + const { metricQuery } = query; + const sloQuery = createMockSLOQuery(); + const onChange = jest.fn(); + const onRunQuery = jest.fn(); + + render( + + ); + + const builder = screen.getByLabelText('Builder'); + const MQL = screen.getByLabelText('MQL'); + expect(builder).toBeChecked(); + expect(MQL).not.toBeChecked(); + + await userEvent.click(MQL); + + expect(onChange).toBeCalledWith( + expect.objectContaining({ metricQuery: expect.objectContaining({ editorMode: EditorMode.MQL }) }) + ); + }); +}); diff --git a/public/app/plugins/datasource/cloud-monitoring/components/Experimental/QueryHeader.tsx b/public/app/plugins/datasource/cloud-monitoring/components/Experimental/QueryHeader.tsx new file mode 100644 index 00000000000..894a5723549 --- /dev/null +++ b/public/app/plugins/datasource/cloud-monitoring/components/Experimental/QueryHeader.tsx @@ -0,0 +1,57 @@ +import React from 'react'; + +import { EditorHeader, FlexItem, InlineSelect } from '@grafana/experimental'; +import { RadioButtonGroup } from '@grafana/ui'; + +import { QUERY_TYPES } from '../../constants'; +import { EditorMode, CloudMonitoringQuery, QueryType, SLOQuery, MetricQuery } from '../../types'; + +export interface QueryEditorHeaderProps { + query: CloudMonitoringQuery; + metricQuery: MetricQuery; + sloQuery: SLOQuery; + onChange: (value: CloudMonitoringQuery) => void; + onRunQuery: () => void; +} + +const EDITOR_MODES = [ + { label: 'Builder', value: EditorMode.Visual }, + { label: 'MQL', value: EditorMode.MQL }, +]; + +export const QueryHeader = (props: QueryEditorHeaderProps) => { + const { query, metricQuery, sloQuery, onChange, onRunQuery } = props; + const { queryType } = query; + const { editorMode } = metricQuery; + + return ( + + { + onChange({ ...query, sloQuery, queryType: value! }); + onRunQuery(); + }} + /> + + {queryType !== QueryType.SLO && ( + { + onChange({ + ...query, + metricQuery: { + ...metricQuery, + editorMode: value, + }, + }); + }} + /> + )} + + ); +}; diff --git a/public/app/plugins/datasource/cloud-monitoring/components/QueryEditor.tsx b/public/app/plugins/datasource/cloud-monitoring/components/QueryEditor.tsx index 62fe9c3748f..d3e583fb92d 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/QueryEditor.tsx +++ b/public/app/plugins/datasource/cloud-monitoring/components/QueryEditor.tsx @@ -11,6 +11,7 @@ import CloudMonitoringDatasource from '../datasource'; import { CloudMonitoringQuery, EditorMode, MetricQuery, QueryType, SLOQuery, CloudMonitoringOptions } from '../types'; import { MetricQueryEditor as ExperimentalMetricQueryEditor } from './Experimental/MetricQueryEditor'; +import { QueryHeader } from './Experimental/QueryHeader'; import { defaultQuery } from './MetricQueryEditor'; import { defaultQuery as defaultSLOQuery } from './SLO/SLOQueryEditor'; @@ -57,7 +58,42 @@ export class QueryEditor extends PureComponent { options: datasource.getVariables().map(toOption), }; - return ( + return config.featureToggles.cloudMonitoringExperimentalUI ? ( + + + {queryType === QueryType.METRICS && ( + { + this.props.onChange({ ...this.props.query, metricQuery }); + }} + onRunQuery={onRunQuery} + datasource={datasource} + query={metricQuery} + /> + )} + + {queryType === QueryType.SLO && ( + this.onQueryChange('sloQuery', query)} + onRunQuery={onRunQuery} + datasource={datasource} + query={sloQuery} + /> + )} + + ) : ( { /> - {queryType === QueryType.METRICS && - (config.featureToggles.cloudMonitoringExperimentalUI ? ( - { - this.props.onChange({ ...this.props.query, metricQuery }); - }} - onRunQuery={onRunQuery} - datasource={datasource} - query={metricQuery} - /> - ) : ( - { - this.props.onChange({ ...this.props.query, metricQuery }); - }} - onRunQuery={onRunQuery} - datasource={datasource} - query={metricQuery} - /> - ))} + {queryType === QueryType.METRICS && ( + { + this.props.onChange({ ...this.props.query, metricQuery }); + }} + onRunQuery={onRunQuery} + datasource={datasource} + query={metricQuery} + /> + )} {queryType === QueryType.SLO && ( { onRunQuery={onRunQuery} datasource={datasource} query={sloQuery} - > + /> )} );