mirror of
https://github.com/grafana/grafana.git
synced 2025-01-27 16:57:14 -06:00
Prometheus: Frontend library exports (#81909)
* fix components folders for exports * export files in the components folder * export metrics modal and fix exports * export promquail and fix exports * export querybuilder/components and fix exports * export querybuilder * main index file exports * run prettier
This commit is contained in:
parent
32a7aa33b8
commit
ec0eb8fd79
@ -4,7 +4,11 @@ import React from 'react';
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
import { PromQuery } from '../types';
|
||||
|
||||
import { PromExploreExtraField, PromExploreExtraFieldProps, testIds } from './PromExploreExtraField';
|
||||
import {
|
||||
PromExploreExtraField,
|
||||
PromExploreExtraFieldProps,
|
||||
promExploreExtraFieldTestIds,
|
||||
} from './PromExploreExtraField';
|
||||
|
||||
const setup = (propOverrides?: PromExploreExtraFieldProps) => {
|
||||
const query = { exemplar: false } as PromQuery;
|
||||
@ -27,11 +31,11 @@ const setup = (propOverrides?: PromExploreExtraFieldProps) => {
|
||||
describe('PromExploreExtraField', () => {
|
||||
it('should render step field', () => {
|
||||
setup();
|
||||
expect(screen.getByTestId(testIds.stepField)).toBeInTheDocument();
|
||||
expect(screen.getByTestId(promExploreExtraFieldTestIds.stepField)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render query type field', () => {
|
||||
setup();
|
||||
expect(screen.getByTestId(testIds.queryTypeField)).toBeInTheDocument();
|
||||
expect(screen.getByTestId(promExploreExtraFieldTestIds.queryTypeField)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -49,10 +49,14 @@ export const PromExploreExtraField = memo(({ query, datasource, onChange, onRunQ
|
||||
const onQueryTypeChange = getQueryTypeChangeHandler(query, onChange);
|
||||
|
||||
return (
|
||||
<div aria-label="Prometheus extra field" className="gf-form-inline" data-testid={testIds.extraFieldEditor}>
|
||||
<div
|
||||
aria-label="Prometheus extra field"
|
||||
className="gf-form-inline"
|
||||
data-testid={promExploreExtraFieldTestIds.extraFieldEditor}
|
||||
>
|
||||
{/*Query type field*/}
|
||||
<div
|
||||
data-testid={testIds.queryTypeField}
|
||||
data-testid={promExploreExtraFieldTestIds.queryTypeField}
|
||||
className={cx(
|
||||
'gf-form explore-input-margin',
|
||||
css`
|
||||
@ -71,7 +75,7 @@ export const PromExploreExtraField = memo(({ query, datasource, onChange, onRunQ
|
||||
</div>
|
||||
{/*Step field*/}
|
||||
<div
|
||||
data-testid={testIds.stepField}
|
||||
data-testid={promExploreExtraFieldTestIds.stepField}
|
||||
className={cx(
|
||||
'gf-form',
|
||||
css`
|
||||
@ -134,7 +138,7 @@ export function getQueryTypeChangeHandler(query: PromQuery, onChange: (update: P
|
||||
};
|
||||
}
|
||||
|
||||
export const testIds = {
|
||||
export const promExploreExtraFieldTestIds = {
|
||||
extraFieldEditor: 'prom-editor-extra-field',
|
||||
stepField: 'prom-editor-extra-field-step',
|
||||
queryTypeField: 'prom-editor-extra-field-query-type',
|
||||
|
@ -7,7 +7,7 @@ import { CoreApp } from '@grafana/data';
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
|
||||
import { PromQueryEditorByApp } from './PromQueryEditorByApp';
|
||||
import { testIds as alertingTestIds } from './PromQueryEditorForAlerting';
|
||||
import { alertingTestIds } from './PromQueryEditorForAlerting';
|
||||
import { Props } from './monaco-query-field/MonacoQueryFieldProps';
|
||||
|
||||
// the monaco-based editor uses lazy-loading and that does not work
|
||||
|
@ -15,11 +15,11 @@ export function PromQueryEditorForAlerting(props: PromQueryEditorProps) {
|
||||
history={[]}
|
||||
range={range}
|
||||
data={data}
|
||||
data-testid={testIds.editor}
|
||||
data-testid={alertingTestIds.editor}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const testIds = {
|
||||
export const alertingTestIds = {
|
||||
editor: 'prom-editor-cloud-alerting',
|
||||
};
|
||||
|
10
packages/grafana-prometheus/src/components/index.ts
Normal file
10
packages/grafana-prometheus/src/components/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export * from './AnnotationQueryEditor';
|
||||
export * from './PromCheatSheet';
|
||||
export * from './PrometheusMetricsBrowser';
|
||||
export * from './PromExemplarField';
|
||||
export * from './PromExploreExtraField';
|
||||
export * from './PromQueryEditorByApp';
|
||||
export * from './PromQueryEditorForAlerting';
|
||||
export * from './PromQueryField';
|
||||
export * from './types';
|
||||
export * from './VariableQueryEditor';
|
@ -1 +1,16 @@
|
||||
export * from './components';
|
||||
export * from './configuration';
|
||||
export * from './querybuilder';
|
||||
|
||||
export * from './add_label_to_query';
|
||||
export * from './dataquery.gen';
|
||||
export * from './datasource';
|
||||
export * from './language_provider';
|
||||
export * from './language_utils';
|
||||
export * from './metric_find_query';
|
||||
export * from './promql';
|
||||
export * from './query_hints';
|
||||
export * from './result_transformer';
|
||||
export * from './tracking';
|
||||
export * from './types';
|
||||
export * from './variables';
|
||||
|
@ -9,7 +9,7 @@ import { AsyncSelect, Select } from '@grafana/ui';
|
||||
import { truncateResult } from '../../language_utils';
|
||||
import { QueryBuilderLabelFilter } from '../shared/types';
|
||||
|
||||
export interface Props {
|
||||
export interface LabelFilterItemProps {
|
||||
defaultOp: string;
|
||||
item: Partial<QueryBuilderLabelFilter>;
|
||||
onChange: (value: QueryBuilderLabelFilter) => void;
|
||||
@ -33,7 +33,7 @@ export function LabelFilterItem({
|
||||
invalidValue,
|
||||
getLabelValuesAutofillSuggestions,
|
||||
debounceDuration,
|
||||
}: Props) {
|
||||
}: LabelFilterItemProps) {
|
||||
const [state, setState] = useState<{
|
||||
labelNames?: SelectableValue[];
|
||||
labelValues?: SelectableValue[];
|
||||
|
@ -5,7 +5,7 @@ import React, { ComponentProps } from 'react';
|
||||
import { selectOptionInTest } from '../../gcopypaste/test/helpers/selectOptionInTest';
|
||||
import { getLabelSelects } from '../testUtils';
|
||||
|
||||
import { LabelFilters, MISSING_LABEL_FILTER_ERROR_MESSAGE, Props } from './LabelFilters';
|
||||
import { LabelFilters, MISSING_LABEL_FILTER_ERROR_MESSAGE, LabelFiltersProps } from './LabelFilters';
|
||||
|
||||
describe('LabelFilters', () => {
|
||||
it('renders empty input without labels', async () => {
|
||||
@ -131,7 +131,7 @@ describe('LabelFilters', () => {
|
||||
});
|
||||
|
||||
function setup(propOverrides?: Partial<ComponentProps<typeof LabelFilters>>) {
|
||||
const defaultProps: Props = {
|
||||
const defaultProps: LabelFiltersProps = {
|
||||
onChange: jest.fn(),
|
||||
getLabelValuesAutofillSuggestions: async (query: string, labelName?: string) => [
|
||||
{ label: 'bar', value: 'bar' },
|
||||
|
@ -12,7 +12,7 @@ import { LabelFilterItem } from './LabelFilterItem';
|
||||
|
||||
export const MISSING_LABEL_FILTER_ERROR_MESSAGE = 'Select at least 1 label filter (label and value)';
|
||||
|
||||
export interface Props {
|
||||
export interface LabelFiltersProps {
|
||||
labelsFilters: QueryBuilderLabelFilter[];
|
||||
onChange: (labelFilters: Array<Partial<QueryBuilderLabelFilter>>) => void;
|
||||
onGetLabelNames: (forLabel: Partial<QueryBuilderLabelFilter>) => Promise<SelectableValue[]>;
|
||||
@ -33,7 +33,7 @@ export function LabelFilters({
|
||||
getLabelValuesAutofillSuggestions,
|
||||
debounceDuration,
|
||||
variableEditor,
|
||||
}: Props) {
|
||||
}: LabelFiltersProps) {
|
||||
const defaultOp = '=';
|
||||
const [items, setItems] = useState<Array<Partial<QueryBuilderLabelFilter>>>([{ op: defaultOp }]);
|
||||
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
formatPrometheusLabelFilters,
|
||||
formatPrometheusLabelFiltersToString,
|
||||
MetricSelect,
|
||||
Props,
|
||||
MetricSelectProps,
|
||||
} from './MetricSelect';
|
||||
|
||||
const instanceSettings = {
|
||||
@ -48,7 +48,7 @@ dataSourceMock.metricFindQuery = jest.fn((query: string) => {
|
||||
);
|
||||
});
|
||||
|
||||
const props: Props = {
|
||||
const props: MetricSelectProps = {
|
||||
labelsFilters: [],
|
||||
datasource: dataSourceMock,
|
||||
query: {
|
||||
|
@ -33,7 +33,7 @@ import { tracking } from './metrics-modal/state/helpers';
|
||||
// We are matching words split with space
|
||||
const splitSeparator = ' ';
|
||||
|
||||
export interface Props {
|
||||
export interface MetricSelectProps {
|
||||
metricLookupDisabled: boolean;
|
||||
query: PromVisualQuery;
|
||||
onChange: (query: PromVisualQuery) => void;
|
||||
@ -55,7 +55,7 @@ export function MetricSelect({
|
||||
metricLookupDisabled,
|
||||
onBlur,
|
||||
variableEditor,
|
||||
}: Props) {
|
||||
}: MetricSelectProps) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const [state, setState] = useState<{
|
||||
metrics?: Array<SelectableValue<any>>;
|
||||
|
@ -11,7 +11,7 @@ import { PromVisualQueryBinary } from '../types';
|
||||
|
||||
import { PromQueryBuilder } from './PromQueryBuilder';
|
||||
|
||||
export interface Props {
|
||||
export interface NestedQueryProps {
|
||||
nestedQuery: PromVisualQueryBinary;
|
||||
datasource: PrometheusDatasource;
|
||||
index: number;
|
||||
@ -21,7 +21,7 @@ export interface Props {
|
||||
showExplain: boolean;
|
||||
}
|
||||
|
||||
export const NestedQuery = React.memo<Props>((props) => {
|
||||
export const NestedQuery = React.memo<NestedQueryProps>((props) => {
|
||||
const { nestedQuery, index, datasource, onChange, onRemove, onRunQuery, showExplain } = props;
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { PromVisualQuery, PromVisualQueryBinary } from '../types';
|
||||
|
||||
import { NestedQuery } from './NestedQuery';
|
||||
|
||||
export interface Props {
|
||||
export interface NestedQueryListProps {
|
||||
query: PromVisualQuery;
|
||||
datasource: PrometheusDatasource;
|
||||
onChange: (query: PromVisualQuery) => void;
|
||||
@ -15,7 +15,7 @@ export interface Props {
|
||||
showExplain: boolean;
|
||||
}
|
||||
|
||||
export function NestedQueryList(props: Props) {
|
||||
export function NestedQueryList(props: NestedQueryListProps) {
|
||||
const { query, datasource, onChange, onRunQuery, showExplain } = props;
|
||||
const nestedQueries = query.binaryQueries ?? [];
|
||||
|
||||
|
@ -27,7 +27,7 @@ import { PromQail } from './promQail/PromQail';
|
||||
import { QueryAssistantButton } from './promQail/QueryAssistantButton';
|
||||
import { isLLMPluginEnabled } from './promQail/state/helpers';
|
||||
|
||||
export interface Props {
|
||||
export interface PromQueryBuilderProps {
|
||||
query: PromVisualQuery;
|
||||
datasource: PrometheusDatasource;
|
||||
onChange: (update: PromVisualQuery) => void;
|
||||
@ -40,7 +40,7 @@ export interface Props {
|
||||
// AI/ML + Prometheus
|
||||
const prometheusPromQAIL = config.featureToggles.prometheusPromQAIL;
|
||||
|
||||
export const PromQueryBuilder = React.memo<Props>((props) => {
|
||||
export const PromQueryBuilder = React.memo<PromQueryBuilderProps>((props) => {
|
||||
const { datasource, query, onChange, onRunQuery, data, showExplain } = props;
|
||||
const [highlightedOp, setHighlightedOp] = useState<QueryBuilderOperation | undefined>();
|
||||
const [showDrawer, setShowDrawer] = useState<boolean>(false);
|
||||
|
@ -14,7 +14,7 @@ import { PromQueryBuilder } from './PromQueryBuilder';
|
||||
import { QueryPreview } from './QueryPreview';
|
||||
import { getSettings, MetricsModalSettings } from './metrics-modal/state/state';
|
||||
|
||||
export interface Props {
|
||||
export interface PromQueryBuilderContainerProps {
|
||||
query: PromQuery;
|
||||
datasource: PrometheusDatasource;
|
||||
onChange: (update: PromQuery) => void;
|
||||
@ -33,7 +33,7 @@ const prometheusMetricEncyclopedia = config.featureToggles.prometheusMetricEncyc
|
||||
/**
|
||||
* This component is here just to contain the translation logic between string query and the visual query builder model.
|
||||
*/
|
||||
export function PromQueryBuilderContainer(props: Props) {
|
||||
export function PromQueryBuilderContainer(props: PromQueryBuilderContainerProps) {
|
||||
const { query, onChange, onRunQuery, datasource, data, showExplain } = props;
|
||||
const [state, dispatch] = useReducer(stateSlice.reducer, { expr: query.expr });
|
||||
// Only rebuild visual query if expr changes from outside
|
||||
|
@ -12,11 +12,11 @@ import { PromVisualQuery } from '../types';
|
||||
|
||||
export const EXPLAIN_LABEL_FILTER_CONTENT = 'Fetch all series matching metric name and label filters.';
|
||||
|
||||
export interface Props {
|
||||
export interface PromQueryBuilderExplainedProps {
|
||||
query: string;
|
||||
}
|
||||
|
||||
export const PromQueryBuilderExplained = React.memo<Props>(({ query }) => {
|
||||
export const PromQueryBuilderExplained = React.memo<PromQueryBuilderExplainedProps>(({ query }) => {
|
||||
const visQuery = buildVisualQueryFromString(query || '').query;
|
||||
const lang = { grammar: promqlGrammar, name: 'promql' };
|
||||
|
||||
|
@ -22,113 +22,115 @@ export interface UIOptions {
|
||||
resolution: boolean;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
export interface PromQueryBuilderOptionsProps {
|
||||
query: PromQuery;
|
||||
app?: CoreApp;
|
||||
onChange: (update: PromQuery) => void;
|
||||
onRunQuery: () => void;
|
||||
}
|
||||
|
||||
export const PromQueryBuilderOptions = React.memo<Props>(({ query, app, onChange, onRunQuery }) => {
|
||||
const onChangeFormat = (value: SelectableValue<PromQueryFormat>) => {
|
||||
onChange({ ...query, format: value.value });
|
||||
onRunQuery();
|
||||
};
|
||||
export const PromQueryBuilderOptions = React.memo<PromQueryBuilderOptionsProps>(
|
||||
({ query, app, onChange, onRunQuery }) => {
|
||||
const onChangeFormat = (value: SelectableValue<PromQueryFormat>) => {
|
||||
onChange({ ...query, format: value.value });
|
||||
onRunQuery();
|
||||
};
|
||||
|
||||
const onChangeStep = (evt: React.FormEvent<HTMLInputElement>) => {
|
||||
onChange({ ...query, interval: evt.currentTarget.value });
|
||||
onRunQuery();
|
||||
};
|
||||
const onChangeStep = (evt: React.FormEvent<HTMLInputElement>) => {
|
||||
onChange({ ...query, interval: evt.currentTarget.value });
|
||||
onRunQuery();
|
||||
};
|
||||
|
||||
const queryTypeOptions = getQueryTypeOptions(
|
||||
app === CoreApp.Explore || app === CoreApp.Correlations || app === CoreApp.PanelEditor
|
||||
);
|
||||
const queryTypeOptions = getQueryTypeOptions(
|
||||
app === CoreApp.Explore || app === CoreApp.Correlations || app === CoreApp.PanelEditor
|
||||
);
|
||||
|
||||
const onQueryTypeChange = getQueryTypeChangeHandler(query, onChange);
|
||||
const onQueryTypeChange = getQueryTypeChangeHandler(query, onChange);
|
||||
|
||||
const onExemplarChange = (event: SyntheticEvent<HTMLInputElement>) => {
|
||||
const isEnabled = event.currentTarget.checked;
|
||||
onChange({ ...query, exemplar: isEnabled });
|
||||
onRunQuery();
|
||||
};
|
||||
const onExemplarChange = (event: SyntheticEvent<HTMLInputElement>) => {
|
||||
const isEnabled = event.currentTarget.checked;
|
||||
onChange({ ...query, exemplar: isEnabled });
|
||||
onRunQuery();
|
||||
};
|
||||
|
||||
const onIntervalFactorChange = (value: SelectableValue<number>) => {
|
||||
onChange({ ...query, intervalFactor: value.value });
|
||||
onRunQuery();
|
||||
};
|
||||
const onIntervalFactorChange = (value: SelectableValue<number>) => {
|
||||
onChange({ ...query, intervalFactor: value.value });
|
||||
onRunQuery();
|
||||
};
|
||||
|
||||
const formatOption = FORMAT_OPTIONS.find((option) => option.value === query.format) || FORMAT_OPTIONS[0];
|
||||
const queryTypeValue = getQueryTypeValue(query);
|
||||
const queryTypeLabel = queryTypeOptions.find((x) => x.value === queryTypeValue)!.label;
|
||||
const formatOption = FORMAT_OPTIONS.find((option) => option.value === query.format) || FORMAT_OPTIONS[0];
|
||||
const queryTypeValue = getQueryTypeValue(query);
|
||||
const queryTypeLabel = queryTypeOptions.find((x) => x.value === queryTypeValue)!.label;
|
||||
|
||||
return (
|
||||
<EditorRow>
|
||||
<div data-testid={selectors.components.DataSource.Prometheus.queryEditor.options}>
|
||||
<QueryOptionGroup
|
||||
title="Options"
|
||||
collapsedInfo={getCollapsedInfo(query, formatOption.label!, queryTypeLabel, app)}
|
||||
>
|
||||
<PromQueryLegendEditor
|
||||
legendFormat={query.legendFormat}
|
||||
onChange={(legendFormat) => onChange({ ...query, legendFormat })}
|
||||
onRunQuery={onRunQuery}
|
||||
/>
|
||||
<EditorField
|
||||
label="Min step"
|
||||
tooltip={
|
||||
<>
|
||||
An additional lower limit for the step parameter of the Prometheus query and for the{' '}
|
||||
<code>$__interval</code> and <code>$__rate_interval</code> variables.
|
||||
</>
|
||||
}
|
||||
return (
|
||||
<EditorRow>
|
||||
<div data-testid={selectors.components.DataSource.Prometheus.queryEditor.options}>
|
||||
<QueryOptionGroup
|
||||
title="Options"
|
||||
collapsedInfo={getCollapsedInfo(query, formatOption.label!, queryTypeLabel, app)}
|
||||
>
|
||||
<AutoSizeInput
|
||||
type="text"
|
||||
aria-label="Set lower limit for the step parameter"
|
||||
placeholder={'auto'}
|
||||
minWidth={10}
|
||||
onCommitChange={onChangeStep}
|
||||
defaultValue={query.interval}
|
||||
id={selectors.components.DataSource.Prometheus.queryEditor.step}
|
||||
<PromQueryLegendEditor
|
||||
legendFormat={query.legendFormat}
|
||||
onChange={(legendFormat) => onChange({ ...query, legendFormat })}
|
||||
onRunQuery={onRunQuery}
|
||||
/>
|
||||
</EditorField>
|
||||
<EditorField label="Format">
|
||||
<Select
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.format}
|
||||
value={formatOption}
|
||||
allowCustomValue
|
||||
onChange={onChangeFormat}
|
||||
options={FORMAT_OPTIONS}
|
||||
/>
|
||||
</EditorField>
|
||||
<EditorField label="Type" data-testid={selectors.components.DataSource.Prometheus.queryEditor.type}>
|
||||
<RadioButtonGroup options={queryTypeOptions} value={queryTypeValue} onChange={onQueryTypeChange} />
|
||||
</EditorField>
|
||||
{shouldShowExemplarSwitch(query, app) && (
|
||||
<EditorField label="Exemplars">
|
||||
<EditorSwitch
|
||||
value={query.exemplar || false}
|
||||
onChange={onExemplarChange}
|
||||
id={selectors.components.DataSource.Prometheus.queryEditor.exemplars}
|
||||
<EditorField
|
||||
label="Min step"
|
||||
tooltip={
|
||||
<>
|
||||
An additional lower limit for the step parameter of the Prometheus query and for the{' '}
|
||||
<code>$__interval</code> and <code>$__rate_interval</code> variables.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<AutoSizeInput
|
||||
type="text"
|
||||
aria-label="Set lower limit for the step parameter"
|
||||
placeholder={'auto'}
|
||||
minWidth={10}
|
||||
onCommitChange={onChangeStep}
|
||||
defaultValue={query.interval}
|
||||
id={selectors.components.DataSource.Prometheus.queryEditor.step}
|
||||
/>
|
||||
</EditorField>
|
||||
)}
|
||||
{query.intervalFactor && query.intervalFactor > 1 && (
|
||||
<EditorField label="Resolution">
|
||||
<EditorField label="Format">
|
||||
<Select
|
||||
aria-label="Select resolution"
|
||||
isSearchable={false}
|
||||
options={INTERVAL_FACTOR_OPTIONS}
|
||||
onChange={onIntervalFactorChange}
|
||||
value={INTERVAL_FACTOR_OPTIONS.find((option) => option.value === query.intervalFactor)}
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.format}
|
||||
value={formatOption}
|
||||
allowCustomValue
|
||||
onChange={onChangeFormat}
|
||||
options={FORMAT_OPTIONS}
|
||||
/>
|
||||
</EditorField>
|
||||
)}
|
||||
</QueryOptionGroup>
|
||||
</div>
|
||||
</EditorRow>
|
||||
);
|
||||
});
|
||||
<EditorField label="Type" data-testid={selectors.components.DataSource.Prometheus.queryEditor.type}>
|
||||
<RadioButtonGroup options={queryTypeOptions} value={queryTypeValue} onChange={onQueryTypeChange} />
|
||||
</EditorField>
|
||||
{shouldShowExemplarSwitch(query, app) && (
|
||||
<EditorField label="Exemplars">
|
||||
<EditorSwitch
|
||||
value={query.exemplar || false}
|
||||
onChange={onExemplarChange}
|
||||
id={selectors.components.DataSource.Prometheus.queryEditor.exemplars}
|
||||
/>
|
||||
</EditorField>
|
||||
)}
|
||||
{query.intervalFactor && query.intervalFactor > 1 && (
|
||||
<EditorField label="Resolution">
|
||||
<Select
|
||||
aria-label="Select resolution"
|
||||
isSearchable={false}
|
||||
options={INTERVAL_FACTOR_OPTIONS}
|
||||
onChange={onIntervalFactorChange}
|
||||
value={INTERVAL_FACTOR_OPTIONS.find((option) => option.value === query.intervalFactor)}
|
||||
/>
|
||||
</EditorField>
|
||||
)}
|
||||
</QueryOptionGroup>
|
||||
</div>
|
||||
</EditorRow>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
function shouldShowExemplarSwitch(query: PromQuery, app?: CoreApp) {
|
||||
if (app === CoreApp.UnifiedAlerting || !query.range) {
|
||||
|
@ -10,11 +10,11 @@ import { PromQueryEditorProps } from '../../components/types';
|
||||
|
||||
import { PromQueryBuilderExplained } from './PromQueryBuilderExplained';
|
||||
|
||||
type Props = PromQueryEditorProps & {
|
||||
type PromQueryCodeEditorProps = PromQueryEditorProps & {
|
||||
showExplain: boolean;
|
||||
};
|
||||
|
||||
export function PromQueryCodeEditor(props: Props) {
|
||||
export function PromQueryCodeEditor(props: PromQueryCodeEditorProps) {
|
||||
const { query, datasource, range, onRunQuery, onChange, data, app, showExplain } = props;
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { AutoSizeInput, Select } from '@grafana/ui';
|
||||
|
||||
import { LegendFormatMode } from '../../types';
|
||||
|
||||
export interface Props {
|
||||
export interface PromQueryLegendEditorProps {
|
||||
legendFormat: string | undefined;
|
||||
onChange: (legendFormat: string) => void;
|
||||
onRunQuery: () => void;
|
||||
@ -26,73 +26,75 @@ const legendModeOptions = [
|
||||
/**
|
||||
* Tests for this component are on the parent level (PromQueryBuilderOptions).
|
||||
*/
|
||||
export const PromQueryLegendEditor = React.memo<Props>(({ legendFormat, onChange, onRunQuery }) => {
|
||||
const mode = getLegendMode(legendFormat);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
export const PromQueryLegendEditor = React.memo<PromQueryLegendEditorProps>(
|
||||
({ legendFormat, onChange, onRunQuery }) => {
|
||||
const mode = getLegendMode(legendFormat);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const onLegendFormatChanged = (evt: React.FormEvent<HTMLInputElement>) => {
|
||||
let newFormat = evt.currentTarget.value;
|
||||
if (newFormat.length === 0) {
|
||||
newFormat = LegendFormatMode.Auto;
|
||||
}
|
||||
const onLegendFormatChanged = (evt: React.FormEvent<HTMLInputElement>) => {
|
||||
let newFormat = evt.currentTarget.value;
|
||||
if (newFormat.length === 0) {
|
||||
newFormat = LegendFormatMode.Auto;
|
||||
}
|
||||
|
||||
if (newFormat !== legendFormat) {
|
||||
onChange(newFormat);
|
||||
if (newFormat !== legendFormat) {
|
||||
onChange(newFormat);
|
||||
onRunQuery();
|
||||
}
|
||||
};
|
||||
|
||||
const onLegendModeChanged = (value: SelectableValue<LegendFormatMode>) => {
|
||||
switch (value.value!) {
|
||||
case LegendFormatMode.Auto:
|
||||
onChange(LegendFormatMode.Auto);
|
||||
break;
|
||||
case LegendFormatMode.Custom:
|
||||
onChange('{{label_name}}');
|
||||
setTimeout(() => {
|
||||
inputRef.current?.focus();
|
||||
inputRef.current?.setSelectionRange(2, 12, 'forward');
|
||||
}, 10);
|
||||
break;
|
||||
case LegendFormatMode.Verbose:
|
||||
onChange('');
|
||||
break;
|
||||
}
|
||||
onRunQuery();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const onLegendModeChanged = (value: SelectableValue<LegendFormatMode>) => {
|
||||
switch (value.value!) {
|
||||
case LegendFormatMode.Auto:
|
||||
onChange(LegendFormatMode.Auto);
|
||||
break;
|
||||
case LegendFormatMode.Custom:
|
||||
onChange('{{label_name}}');
|
||||
setTimeout(() => {
|
||||
inputRef.current?.focus();
|
||||
inputRef.current?.setSelectionRange(2, 12, 'forward');
|
||||
}, 10);
|
||||
break;
|
||||
case LegendFormatMode.Verbose:
|
||||
onChange('');
|
||||
break;
|
||||
}
|
||||
onRunQuery();
|
||||
};
|
||||
|
||||
return (
|
||||
<EditorField
|
||||
label="Legend"
|
||||
tooltip="Series name override or template. Ex. {{hostname}} will be replaced with label value for hostname."
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.legend}
|
||||
>
|
||||
<>
|
||||
{mode === LegendFormatMode.Custom && (
|
||||
<AutoSizeInput
|
||||
id="legendFormat"
|
||||
minWidth={22}
|
||||
placeholder="auto"
|
||||
defaultValue={legendFormat}
|
||||
onCommitChange={onLegendFormatChanged}
|
||||
ref={inputRef}
|
||||
/>
|
||||
)}
|
||||
{mode !== LegendFormatMode.Custom && (
|
||||
<Select
|
||||
inputId="legend.mode"
|
||||
isSearchable={false}
|
||||
placeholder="Select legend mode"
|
||||
options={legendModeOptions}
|
||||
width={22}
|
||||
onChange={onLegendModeChanged}
|
||||
value={legendModeOptions.find((x) => x.value === mode)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</EditorField>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<EditorField
|
||||
label="Legend"
|
||||
tooltip="Series name override or template. Ex. {{hostname}} will be replaced with label value for hostname."
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.legend}
|
||||
>
|
||||
<>
|
||||
{mode === LegendFormatMode.Custom && (
|
||||
<AutoSizeInput
|
||||
id="legendFormat"
|
||||
minWidth={22}
|
||||
placeholder="auto"
|
||||
defaultValue={legendFormat}
|
||||
onCommitChange={onLegendFormatChanged}
|
||||
ref={inputRef}
|
||||
/>
|
||||
)}
|
||||
{mode !== LegendFormatMode.Custom && (
|
||||
<Select
|
||||
inputId="legend.mode"
|
||||
isSearchable={false}
|
||||
placeholder="Select legend mode"
|
||||
options={legendModeOptions}
|
||||
width={22}
|
||||
onChange={onLegendModeChanged}
|
||||
value={legendModeOptions.find((x) => x.value === mode)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</EditorField>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
PromQueryLegendEditor.displayName = 'PromQueryLegendEditor';
|
||||
|
||||
|
@ -5,11 +5,11 @@ import { EditorFieldGroup, EditorRow } from '@grafana/experimental';
|
||||
import promqlGrammar from '../../promql';
|
||||
import { RawQuery } from '../shared/RawQuery';
|
||||
|
||||
export interface Props {
|
||||
export interface QueryPreviewProps {
|
||||
query: string;
|
||||
}
|
||||
|
||||
export function QueryPreview({ query }: Props) {
|
||||
export function QueryPreview({ query }: QueryPreviewProps) {
|
||||
if (!query) {
|
||||
return null;
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
export * from './LabelFilterItem';
|
||||
export * from './LabelFilters';
|
||||
export * from './LabelParamEditor';
|
||||
export * from './MetricSelect';
|
||||
export * from './MetricsLabelsSection';
|
||||
export * from './NestedQuery';
|
||||
export * from './NestedQueryList';
|
||||
export * from './PromQueryBuilder';
|
||||
export * from './PromQueryBuilderContainer';
|
||||
export * from './PromQueryBuilderExplained';
|
||||
export * from './PromQueryBuilderOptions';
|
||||
export * from './PromQueryCodeEditor';
|
||||
export * from './PromQueryEditorSelector';
|
||||
export * from './PromQueryLegendEditor';
|
||||
export * from './QueryPreview';
|
||||
export * from './metrics-modal';
|
||||
export * from './promQail';
|
@ -4,7 +4,7 @@ import React from 'react';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Icon, Switch, Tooltip, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { testIds } from './MetricsModal';
|
||||
import { metricsModaltestIds } from './MetricsModal';
|
||||
import { placeholders } from './state/helpers';
|
||||
import { MetricsModalState } from './state/state';
|
||||
|
||||
@ -27,7 +27,7 @@ export function AdditionalSettings(props: AdditionalSettingsProps) {
|
||||
<>
|
||||
<div className={styles.selectItem}>
|
||||
<Switch
|
||||
data-testid={testIds.searchWithMetadata}
|
||||
data-testid={metricsModaltestIds.searchWithMetadata}
|
||||
value={state.fullMetaSearch}
|
||||
disabled={state.useBackend || !state.hasMetadata}
|
||||
onChange={() => onChangeFullMetaSearch()}
|
||||
@ -47,7 +47,11 @@ export function AdditionalSettings(props: AdditionalSettingsProps) {
|
||||
<div className={styles.selectItemLabel}>Disable text wrap</div>
|
||||
</div>
|
||||
<div className={styles.selectItem}>
|
||||
<Switch data-testid={testIds.setUseBackend} value={state.useBackend} onChange={() => onChangeUseBackend()} />
|
||||
<Switch
|
||||
data-testid={metricsModaltestIds.setUseBackend}
|
||||
value={state.useBackend}
|
||||
onChange={() => onChangeUseBackend()}
|
||||
/>
|
||||
<div className={styles.selectItemLabel}>{placeholders.setUseBackend} </div>
|
||||
<Tooltip
|
||||
content={'Filter metric names by regex search, using an additional call on the Prometheus API.'}
|
||||
|
@ -10,7 +10,7 @@ import { EmptyLanguageProviderMock } from '../../../language_provider.mock';
|
||||
import { PromOptions } from '../../../types';
|
||||
import { PromVisualQuery } from '../../types';
|
||||
|
||||
import { MetricsModal, testIds } from './MetricsModal';
|
||||
import { MetricsModal, metricsModaltestIds } from './MetricsModal';
|
||||
|
||||
// don't care about interaction tracking in our unit tests
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
@ -117,7 +117,7 @@ describe('MetricsModal', () => {
|
||||
|
||||
it('shows results metrics per page chosen by the user', async () => {
|
||||
setup(defaultQuery, listOfMetrics);
|
||||
const resultsPerPageInput = screen.getByTestId(testIds.resultsPerPage);
|
||||
const resultsPerPageInput = screen.getByTestId(metricsModaltestIds.resultsPerPage);
|
||||
await userEvent.type(resultsPerPageInput, '12');
|
||||
const metricInsideRange = screen.getByText('j');
|
||||
expect(metricInsideRange).toBeInTheDocument();
|
||||
@ -130,7 +130,7 @@ describe('MetricsModal', () => {
|
||||
// doesn't break on loading
|
||||
expect(screen.getByText('0')).toBeInTheDocument();
|
||||
});
|
||||
const resultsPerPageInput = screen.getByTestId(testIds.resultsPerPage);
|
||||
const resultsPerPageInput = screen.getByTestId(metricsModaltestIds.resultsPerPage);
|
||||
// doesn't break on changing results per page
|
||||
await userEvent.type(resultsPerPageInput, '11');
|
||||
const metricInsideRange = screen.getByText('9');
|
||||
@ -149,7 +149,7 @@ describe('MetricsModal', () => {
|
||||
expect(metricAll).toBeInTheDocument();
|
||||
expect(metricABucket).toBeInTheDocument();
|
||||
});
|
||||
const searchMetric = screen.getByTestId(testIds.searchMetric);
|
||||
const searchMetric = screen.getByTestId(metricsModaltestIds.searchMetric);
|
||||
expect(searchMetric).toBeInTheDocument();
|
||||
await userEvent.type(searchMetric, 'a_buck');
|
||||
|
||||
@ -169,15 +169,15 @@ describe('MetricsModal', () => {
|
||||
expect(metricABucket).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const showSettingsButton = screen.getByTestId(testIds.showAdditionalSettings);
|
||||
const showSettingsButton = screen.getByTestId(metricsModaltestIds.showAdditionalSettings);
|
||||
expect(showSettingsButton).toBeInTheDocument();
|
||||
await userEvent.click(showSettingsButton);
|
||||
|
||||
const metadataSwitch = screen.getByTestId(testIds.searchWithMetadata);
|
||||
const metadataSwitch = screen.getByTestId(metricsModaltestIds.searchWithMetadata);
|
||||
expect(metadataSwitch).toBeInTheDocument();
|
||||
await userEvent.click(metadataSwitch);
|
||||
|
||||
const searchMetric = screen.getByTestId(testIds.searchMetric);
|
||||
const searchMetric = screen.getByTestId(metricsModaltestIds.searchMetric);
|
||||
expect(searchMetric).toBeInTheDocument();
|
||||
await userEvent.type(searchMetric, 'functions');
|
||||
|
||||
|
@ -197,7 +197,7 @@ export const MetricsModal = (props: MetricsModalProps) => {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
data-testid={testIds.metricModal}
|
||||
data-testid={metricsModaltestIds.metricModal}
|
||||
isOpen={isOpen}
|
||||
title="Metrics explorer"
|
||||
onDismiss={onClose}
|
||||
@ -212,7 +212,7 @@ export const MetricsModal = (props: MetricsModalProps) => {
|
||||
<div className={cx(styles.inputItem, styles.inputItemFirst)}>
|
||||
<Input
|
||||
autoFocus={true}
|
||||
data-testid={testIds.searchMetric}
|
||||
data-testid={metricsModaltestIds.searchMetric}
|
||||
placeholder={placeholders.browse}
|
||||
value={state.fuzzySearchQuery}
|
||||
onInput={(e) => {
|
||||
@ -225,7 +225,7 @@ export const MetricsModal = (props: MetricsModalProps) => {
|
||||
{state.hasMetadata && (
|
||||
<div className={styles.inputItem}>
|
||||
<MultiSelect
|
||||
data-testid={testIds.selectType}
|
||||
data-testid={metricsModaltestIds.selectType}
|
||||
inputId="my-select"
|
||||
options={typeOptions}
|
||||
value={state.selectedTypes}
|
||||
@ -249,7 +249,7 @@ export const MetricsModal = (props: MetricsModalProps) => {
|
||||
variant="secondary"
|
||||
size="md"
|
||||
onClick={() => dispatch(showAdditionalSettings())}
|
||||
data-testid={testIds.showAdditionalSettings}
|
||||
data-testid={metricsModaltestIds.showAdditionalSettings}
|
||||
className={styles.noBorder}
|
||||
>
|
||||
Additional Settings
|
||||
@ -301,7 +301,7 @@ export const MetricsModal = (props: MetricsModalProps) => {
|
||||
<div className={styles.resultsPerPageWrapper}>
|
||||
<p className={styles.resultsPerPageLabel}># Results per page </p>
|
||||
<Input
|
||||
data-testid={testIds.resultsPerPage}
|
||||
data-testid={metricsModaltestIds.resultsPerPage}
|
||||
value={calculateResultsPerPage(state.resultsPerPage, DEFAULT_RESULTS_PER_PAGE, MAXIMUM_RESULTS_PER_PAGE)}
|
||||
placeholder="results per page"
|
||||
width={10}
|
||||
@ -323,7 +323,7 @@ export const MetricsModal = (props: MetricsModalProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const testIds = {
|
||||
export const metricsModaltestIds = {
|
||||
metricModal: 'metric-modal',
|
||||
searchMetric: 'search-metric',
|
||||
searchWithMetadata: 'search-with-metadata',
|
||||
|
@ -0,0 +1 @@
|
||||
export * from './MetricsModal';
|
@ -10,7 +10,7 @@ import { EmptyLanguageProviderMock } from '../../../language_provider.mock';
|
||||
import { PromOptions } from '../../../types';
|
||||
import { PromVisualQuery } from '../../types';
|
||||
|
||||
import { PromQail, testIds } from './PromQail';
|
||||
import { PromQail, queryAssistanttestIds } from './PromQail';
|
||||
|
||||
// don't care about interaction tracking in our unit tests
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
@ -56,7 +56,7 @@ describe('PromQail', () => {
|
||||
expect(screen.getByText('Do you know what you want to query?')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const aiPrompt = screen.getByTestId(testIds.clickForAi);
|
||||
const aiPrompt = screen.getByTestId(queryAssistanttestIds.clickForAi);
|
||||
|
||||
userEvent.click(aiPrompt);
|
||||
|
||||
@ -75,7 +75,7 @@ describe('PromQail', () => {
|
||||
expect(screen.getByText('Do you know what you want to query?')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const historicalPrompt = screen.getByTestId(testIds.clickForHistorical);
|
||||
const historicalPrompt = screen.getByTestId(queryAssistanttestIds.clickForHistorical);
|
||||
|
||||
userEvent.click(historicalPrompt);
|
||||
|
||||
@ -142,7 +142,7 @@ function setup(query: PromVisualQuery) {
|
||||
}
|
||||
|
||||
function clickSecurityButton() {
|
||||
const securityInfoButton = screen.getByTestId(testIds.securityInfoButton);
|
||||
const securityInfoButton = screen.getByTestId(queryAssistanttestIds.securityInfoButton);
|
||||
|
||||
userEvent.click(securityInfoButton);
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ export const PromQail = (props: PromQailProps) => {
|
||||
fill="solid"
|
||||
variant="primary"
|
||||
onClick={() => dispatch(showStartingMessage(false))}
|
||||
data-testid={testIds.securityInfoButton}
|
||||
data-testid={queryAssistanttestIds.securityInfoButton}
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
@ -181,7 +181,7 @@ export const PromQail = (props: PromQailProps) => {
|
||||
className={styles.leftButton}
|
||||
fill="solid"
|
||||
variant="secondary"
|
||||
data-testid={testIds.clickForHistorical}
|
||||
data-testid={queryAssistanttestIds.clickForHistorical}
|
||||
onClick={() => {
|
||||
const isLoading = true;
|
||||
const suggestionType = SuggestionType.Historical;
|
||||
@ -198,7 +198,7 @@ export const PromQail = (props: PromQailProps) => {
|
||||
<Button
|
||||
fill="solid"
|
||||
variant="primary"
|
||||
data-testid={testIds.clickForAi}
|
||||
data-testid={queryAssistanttestIds.clickForAi}
|
||||
onClick={() => {
|
||||
reportInteraction('grafana_prometheus_promqail_know_what_you_want_to_query', {
|
||||
promVisualQuery: query,
|
||||
@ -293,7 +293,7 @@ export const PromQail = (props: PromQailProps) => {
|
||||
<Button
|
||||
fill="solid"
|
||||
variant="primary"
|
||||
data-testid={testIds.submitPrompt + idx}
|
||||
data-testid={queryAssistanttestIds.submitPrompt + idx}
|
||||
onClick={() => {
|
||||
const newInteraction: Interaction = {
|
||||
...interaction,
|
||||
@ -557,7 +557,7 @@ export const getStyles = (theme: GrafanaTheme2) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const testIds = {
|
||||
export const queryAssistanttestIds = {
|
||||
promQail: 'prom-qail',
|
||||
securityInfoButton: 'security-info-button',
|
||||
clickForHistorical: 'click-for-historical',
|
||||
|
@ -5,7 +5,7 @@ import { Button, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { PromVisualQuery } from '../../types';
|
||||
|
||||
import { getStyles, testIds } from './PromQail';
|
||||
import { getStyles, queryAssistanttestIds } from './PromQail';
|
||||
import { QuerySuggestionItem } from './QuerySuggestionItem';
|
||||
import { QuerySuggestion, SuggestionType } from './types';
|
||||
|
||||
@ -81,7 +81,7 @@ export function QuerySuggestionContainer(props: Props) {
|
||||
updateHasNextInteraction(true);
|
||||
nextInteraction();
|
||||
}}
|
||||
data-testid={testIds.refinePrompt}
|
||||
data-testid={queryAssistanttestIds.refinePrompt}
|
||||
fill="outline"
|
||||
variant="secondary"
|
||||
size="md"
|
||||
|
@ -0,0 +1 @@
|
||||
export * from './PromQail';
|
13
packages/grafana-prometheus/src/querybuilder/index.ts
Normal file
13
packages/grafana-prometheus/src/querybuilder/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export * from './aggregations';
|
||||
export * from './binaryScalarOperations';
|
||||
export * from './operations';
|
||||
export * from './operationUtils';
|
||||
export * from './parsing';
|
||||
export * from './parsingUtils';
|
||||
export * from './PromQueryModeller';
|
||||
export * from './QueryPattern';
|
||||
export * from './QueryPatternsModal';
|
||||
export * from './state';
|
||||
export * from './testUtils';
|
||||
export * from './types';
|
||||
export * from './components';
|
Loading…
Reference in New Issue
Block a user