mirror of
https://github.com/grafana/grafana.git
synced 2025-12-18 22:16:21 -05:00
Internationalisation: Mark up @grafana/prometheus package (#105861)
* scaffolding for grafana-prometheus i18n * markup * extract translations * fix tests * add loadResources + fix object properties * fix typo + prettier * remove useTranslate * update workflow to trigger on changes to grafana-sql/grafana-prometheus packages
This commit is contained in:
parent
d284cceb67
commit
0879479c15
55 changed files with 1195 additions and 287 deletions
2
.github/workflows/i18n-crowdin-upload.yml
vendored
2
.github/workflows/i18n-crowdin-upload.yml
vendored
|
|
@ -7,6 +7,8 @@ on:
|
|||
- 'public/locales/en-US/grafana.json'
|
||||
- 'public/app/plugins/datasource/azuremonitor/locales/en-US/grafana-azure-monitor-datasource.json'
|
||||
- 'public/app/plugins/datasource/mssql/locales/en-US/mssql.json'
|
||||
- 'packages/grafana-sql/src/locales/en-US/grafana-sql.json'
|
||||
- 'packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json'
|
||||
branches:
|
||||
- main
|
||||
|
||||
|
|
|
|||
|
|
@ -27,4 +27,10 @@ files: [
|
|||
"type": "i18next_json",
|
||||
"dest": "packages/grafana-sql/en-US/%original_file_name%"
|
||||
},
|
||||
{
|
||||
"source": "packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json",
|
||||
"translation": "packages/grafana-prometheus/src/locales/%locale%/%original_file_name%",
|
||||
"type": "i18next_json",
|
||||
"dest": "packages/grafana-prometheus/en-US/%original_file_name%"
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -305,6 +305,7 @@ module.exports = [
|
|||
'public/app/!(plugins)/**/*.{ts,tsx,js,jsx}',
|
||||
'packages/grafana-ui/**/*.{ts,tsx,js,jsx}',
|
||||
'packages/grafana-sql/**/*.{ts,tsx,js,jsx}',
|
||||
'packages/grafana-prometheus/**/*.{ts,tsx,js,jsx}',
|
||||
...pluginsToTranslate.map((plugin) => `${plugin}/**/*.{ts,tsx,js,jsx}`),
|
||||
],
|
||||
ignores: [
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
"build": "tsc -p ./tsconfig.build.json && rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"bundle": "rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"clean": "rimraf ./dist ./compiled ./package.tgz",
|
||||
"i18n-extract": "i18next --config src/locales/i18next-parser.config.cjs",
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
|
||||
"prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js",
|
||||
"postpack": "mv package.json.bak package.json"
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
"@floating-ui/react": "0.27.12",
|
||||
"@grafana/data": "12.1.0-pre",
|
||||
"@grafana/e2e-selectors": "12.1.0-pre",
|
||||
"@grafana/i18n": "12.1.0-pre",
|
||||
"@grafana/plugin-ui": "0.10.6",
|
||||
"@grafana/runtime": "12.1.0-pre",
|
||||
"@grafana/schema": "12.1.0-pre",
|
||||
|
|
@ -87,6 +89,7 @@
|
|||
"@types/pluralize": "^0.0.33",
|
||||
"@types/prismjs": "1.26.5",
|
||||
"esbuild": "0.25.0",
|
||||
"i18next-parser": "9.3.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"react": "18.3.1",
|
||||
|
|
|
|||
|
|
@ -83,12 +83,12 @@ describe('AnnotationQueryEditor', () => {
|
|||
|
||||
it('displays an error message when annotation data is missing', () => {
|
||||
render(<AnnotationQueryEditor {...defaultProps} annotation={undefined} />);
|
||||
expect(screen.getByText('annotation data load error!')).toBeInTheDocument();
|
||||
expect(screen.getByText('Annotation data load error!')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays an error message when onAnnotationChange is missing', () => {
|
||||
render(<AnnotationQueryEditor {...defaultProps} onAnnotationChange={undefined} />);
|
||||
expect(screen.getByText('annotation data load error!')).toBeInTheDocument();
|
||||
expect(screen.getByText('Annotation data load error!')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders correctly with an empty annotation object', () => {
|
||||
|
|
@ -96,7 +96,7 @@ describe('AnnotationQueryEditor', () => {
|
|||
// Should render normally with empty values but not show an error
|
||||
expect(screen.getByText('Min step')).toBeInTheDocument();
|
||||
expect(screen.getByText('Title')).toBeInTheDocument();
|
||||
expect(screen.queryByText('annotation data load error!')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Annotation data load error!')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onChange when min step is updated', () => {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { memo } from 'react';
|
|||
|
||||
import { AnnotationQuery } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { EditorField, EditorRow, EditorRows, EditorSwitch } from '@grafana/plugin-ui';
|
||||
import { AutoSizeInput, Input, Space } from '@grafana/ui';
|
||||
|
||||
|
|
@ -30,7 +31,13 @@ export const AnnotationQueryEditor = memo(function AnnotationQueryEditor(props:
|
|||
const { annotation, onAnnotationChange, onChange, onRunQuery, query } = props;
|
||||
|
||||
if (!annotation || !onAnnotationChange) {
|
||||
return <h3>annotation data load error!</h3>;
|
||||
return (
|
||||
<h3>
|
||||
<Trans i18nKey="components.annotation-query-editor.annotation-data-load-error">
|
||||
Annotation data load error!
|
||||
</Trans>
|
||||
</h3>
|
||||
);
|
||||
}
|
||||
|
||||
const handleMinStepChange = (value: string) => {
|
||||
|
|
@ -71,18 +78,24 @@ export const AnnotationQueryEditor = memo(function AnnotationQueryEditor(props:
|
|||
<PromQueryCodeEditor {...props} query={query} showExplain={false} onRunQuery={onRunQuery} onChange={onChange} />
|
||||
<EditorRow>
|
||||
<EditorField
|
||||
label="Min step"
|
||||
label={t('components.annotation-query-editor.label-min-step', 'Min step')}
|
||||
tooltip={
|
||||
<>
|
||||
<Trans
|
||||
i18nKey="components.annotation-query-editor.tooltip-min-step"
|
||||
values={{ intervalVar: '$__interval', rateIntervalVar: '$__rate_interval' }}
|
||||
>
|
||||
An additional lower limit for the step parameter of the Prometheus query and for the{' '}
|
||||
<code>$__interval</code> and <code>$__rate_interval</code> variables.
|
||||
</>
|
||||
<code>{'{{intervalVar}}'}</code> and <code>{'{{rateIntervalVar}}'}</code> variables.
|
||||
</Trans>
|
||||
}
|
||||
>
|
||||
<AutoSizeInput
|
||||
type="text"
|
||||
aria-label="Set lower limit for the step parameter"
|
||||
placeholder={'auto'}
|
||||
aria-label={t(
|
||||
'components.annotation-query-editor.aria-label-lower-limit-parameter',
|
||||
'Set lower limit for the step parameter'
|
||||
)}
|
||||
placeholder={t('components.annotation-query-editor.placeholder-auto', 'auto')}
|
||||
minWidth={10}
|
||||
value={query.interval ?? ''}
|
||||
onChange={(e) => handleMinStepChange(e.currentTarget.value)}
|
||||
|
|
@ -94,10 +107,12 @@ export const AnnotationQueryEditor = memo(function AnnotationQueryEditor(props:
|
|||
<Space v={0.5} />
|
||||
<EditorRow>
|
||||
<EditorField
|
||||
label="Title"
|
||||
tooltip={
|
||||
'Use either the name or a pattern. For example, {{instance}} is replaced with label value for the label instance.'
|
||||
}
|
||||
label={t('components.annotation-query-editor.label-title', 'Title')}
|
||||
tooltip={t(
|
||||
'components.annotation-query-editor.tooltip-either-pattern-example-instance-replaced-label',
|
||||
'Use either the name or a pattern. For example, {{labelTemplate}} is replaced with label value for the label {{labelName}}.',
|
||||
{ labelName: 'instance', labelTemplate: '{{instance}}' }
|
||||
)}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
|
|
@ -107,7 +122,7 @@ export const AnnotationQueryEditor = memo(function AnnotationQueryEditor(props:
|
|||
data-testid={selectors.components.DataSource.Prometheus.annotations.title}
|
||||
/>
|
||||
</EditorField>
|
||||
<EditorField label="Tags">
|
||||
<EditorField label={t('components.annotation-query-editor.label-tags', 'Tags')}>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={PLACEHOLDER_TAGS}
|
||||
|
|
@ -117,10 +132,12 @@ export const AnnotationQueryEditor = memo(function AnnotationQueryEditor(props:
|
|||
/>
|
||||
</EditorField>
|
||||
<EditorField
|
||||
label="Text"
|
||||
tooltip={
|
||||
'Use either the name or a pattern. For example, {{instance}} is replaced with label value for the label instance.'
|
||||
}
|
||||
label={t('components.annotation-query-editor.label-text', 'Text')}
|
||||
tooltip={t(
|
||||
'components.annotation-query-editor.tooltip-either-pattern-example-instance-replaced-label',
|
||||
'Use either the name or a pattern. For example, {{labelTemplate}} is replaced with label value for the label {{labelName}}.',
|
||||
{ labelName: 'instance', labelTemplate: '{{instance}}' }
|
||||
)}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
|
|
@ -131,10 +148,11 @@ export const AnnotationQueryEditor = memo(function AnnotationQueryEditor(props:
|
|||
/>
|
||||
</EditorField>
|
||||
<EditorField
|
||||
label="Series value as timestamp"
|
||||
tooltip={
|
||||
label={t('components.annotation-query-editor.label-series-value-as-timestamp', 'Series value as timestamp')}
|
||||
tooltip={t(
|
||||
'components.annotation-query-editor.tooltip-timestamp-milliseconds-series-value-seconds-multiply',
|
||||
'The unit of timestamp is milliseconds. If the unit of the series value is seconds, multiply its range vector by 1000.'
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EditorSwitch
|
||||
value={annotation.useValueForTime ?? false}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2, QueryEditorHelpProps } from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { PromQuery } from '../types';
|
||||
|
|
@ -35,7 +36,9 @@ export const PromCheatSheet = (props: QueryEditorHelpProps<PromQuery>) => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<h2>PromQL Cheat Sheet</h2>
|
||||
<h2>
|
||||
<Trans i18nKey="components.prom-cheat-sheet.prom-ql-cheat-sheet">PromQL Cheat Sheet</Trans>
|
||||
</h2>
|
||||
{CHEAT_SHEET_ITEMS.map((item, index) => (
|
||||
<div className={styles.cheatSheetItem} key={index}>
|
||||
<div className={styles.cheatSheetItemTitle}>{item.title}</div>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useEffect, useState } from 'react';
|
|||
import { usePrevious } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { IconButton, InlineLabel, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
|
|
@ -48,10 +49,14 @@ export function PromExemplarField({ datasource, onChange, query, ...rest }: Prop
|
|||
<InlineLabel width="auto" data-testid={rest['data-testid']}>
|
||||
<Tooltip content={error ?? ''}>
|
||||
<div className={styles.iconWrapper}>
|
||||
Exemplars
|
||||
<Trans i18nKey="components.prom-exemplar-field.exemplars">Exemplars</Trans>
|
||||
<IconButton
|
||||
name="eye"
|
||||
tooltip={!!query.exemplar ? 'Disable query with exemplars' : 'Enable query with exemplars'}
|
||||
tooltip={
|
||||
!!query.exemplar
|
||||
? t('components.prom-exemplar-field.tooltip-disable-query', 'Disable query with exemplars')
|
||||
: t('components.prom-exemplar-field.tooltip-enable-query', 'Enable query with exemplars')
|
||||
}
|
||||
disabled={!!error}
|
||||
className={iconButtonStyles}
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import * as React from 'react';
|
|||
import { usePrevious } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { InlineFormLabel, RadioButtonGroup, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
|
|
@ -54,7 +55,7 @@ export const PromExploreExtraField = memo(({ query, datasource, onChange, onRunQ
|
|||
|
||||
return (
|
||||
<div
|
||||
aria-label="Prometheus extra field"
|
||||
aria-label={t('components.prom-explore-extra-field.aria-label-prometheus-extra-field', 'Prometheus extra field')}
|
||||
className="gf-form-inline"
|
||||
data-testid={promExploreExtraFieldTestIds.extraFieldEditor}
|
||||
>
|
||||
|
|
@ -68,9 +69,11 @@ export const PromExploreExtraField = memo(({ query, datasource, onChange, onRunQ
|
|||
flexWrap: 'nowrap',
|
||||
})
|
||||
)}
|
||||
aria-label="Query type field"
|
||||
aria-label={t('components.prom-explore-extra-field.aria-label-query-type-field', 'Query type field')}
|
||||
>
|
||||
<InlineFormLabel width="auto">Query type</InlineFormLabel>
|
||||
<InlineFormLabel width="auto">
|
||||
<Trans i18nKey="components.prom-explore-extra-field.query-type">Query type</Trans>
|
||||
</InlineFormLabel>
|
||||
|
||||
<RadioButtonGroup
|
||||
options={rangeOptions}
|
||||
|
|
@ -87,20 +90,32 @@ export const PromExploreExtraField = memo(({ query, datasource, onChange, onRunQ
|
|||
flexWrap: 'nowrap',
|
||||
})
|
||||
)}
|
||||
aria-label="Step field"
|
||||
aria-label={t('components.prom-explore-extra-field.aria-label-step-field', 'Step field')}
|
||||
>
|
||||
<InlineFormLabel
|
||||
width={6}
|
||||
tooltip={
|
||||
'Time units and built-in variables can be used here, for example: $__interval, $__rate_interval, 5s, 1m, 3h, 1d, 1y (Default if no unit is specified: s)'
|
||||
}
|
||||
tooltip={t(
|
||||
'components.prom-explore-extra-field.tooltip-units-builtin-variables-example-interval-rateinterval',
|
||||
'Time units and built-in variables can be used here, for example: {{example1}}, {{example2}}, {{example3}}, {{example4}}, {{example5}}, {{example6}}, {{example7}} (Default if no unit is specified: {{default}})',
|
||||
{
|
||||
example1: '$__interval',
|
||||
example2: '$__rate_interval',
|
||||
example3: '5s',
|
||||
example4: '1m',
|
||||
example5: '3h',
|
||||
example6: '1d',
|
||||
example7: '1y',
|
||||
default: 's',
|
||||
}
|
||||
)}
|
||||
>
|
||||
Min step
|
||||
<Trans i18nKey="components.prom-explore-extra-field.min-step">Min step</Trans>
|
||||
</InlineFormLabel>
|
||||
<input
|
||||
type={'text'}
|
||||
className="gf-form-input width-4"
|
||||
placeholder={'auto'}
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
placeholder="auto"
|
||||
onChange={onStepChange}
|
||||
onKeyDown={onReturnKeyDown}
|
||||
value={query.interval ?? ''}
|
||||
|
|
@ -116,16 +131,30 @@ PromExploreExtraField.displayName = 'PromExploreExtraField';
|
|||
|
||||
export function getQueryTypeOptions(includeBoth: boolean) {
|
||||
const rangeOptions = [
|
||||
{ value: 'range', label: 'Range', description: 'Run query over a range of time' },
|
||||
{
|
||||
value: 'range',
|
||||
label: t('components.get-query-type-options.range-options.label.range', 'Range'),
|
||||
description: t(
|
||||
'components.get-query-type-options.range-options.description.query-range',
|
||||
'Run query over a range of time'
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'instant',
|
||||
label: 'Instant',
|
||||
label: t('components.get-query-type-options.range-options.label.instant', 'Instant'),
|
||||
description: 'Run query against a single point in time. For this query, the "To" time is used',
|
||||
},
|
||||
];
|
||||
|
||||
if (includeBoth) {
|
||||
rangeOptions.push({ value: 'both', label: 'Both', description: 'Run an Instant query and a Range query' });
|
||||
rangeOptions.push({
|
||||
value: 'both',
|
||||
label: t('components.get-query-type-options.label.both', 'Both'),
|
||||
description: t(
|
||||
'components.get-query-type-options.description.instant-query-range',
|
||||
'Run an Instant query and a Range query'
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return rangeOptions;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { MutableRefObject, ReactNode, useCallback, useState } from 'react';
|
|||
|
||||
import { getDefaultTimeRange, isDataFrame, QueryEditorProps, QueryHint, toLegacyResponseData } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { clearButtonStyles, Icon, useTheme2 } from '@grafana/ui';
|
||||
|
||||
|
|
@ -165,7 +166,7 @@ export const PromQueryField = (props: PromQueryFieldProps) => {
|
|||
onChange={onChangeQuery}
|
||||
onRunQuery={onRunQuery}
|
||||
initialValue={query.expr ?? ''}
|
||||
placeholder="Enter a PromQL query…"
|
||||
placeholder={t('components.prom-query-field.placeholder-enter-a-prom-ql-query', 'Enter a PromQL query…')}
|
||||
datasource={datasource}
|
||||
timeRange={range ?? getDefaultTimeRange()}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { render, screen, waitFor } from '@testing-library/react';
|
|||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { dateTime, TimeRange } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
import PrometheusLanguageProvider from '../language_provider';
|
||||
|
|
@ -270,7 +271,9 @@ describe('PromVariableQueryEditor', () => {
|
|||
render(<PromVariableQueryEditor {...props} onChange={onChange} />);
|
||||
|
||||
await selectOptionInTest(screen.getByLabelText('Query type'), 'Label values');
|
||||
const labelSelect = screen.getByLabelText('label-select');
|
||||
const labelSelect = screen.getByTestId(
|
||||
selectors.components.DataSource.Prometheus.variableQueryEditor.labelValues.labelSelect
|
||||
);
|
||||
await userEvent.type(labelSelect, 'this');
|
||||
await selectOptionInTest(labelSelect, 'this');
|
||||
//display label in label select
|
||||
|
|
@ -296,7 +299,9 @@ describe('PromVariableQueryEditor', () => {
|
|||
render(<PromVariableQueryEditor {...props} onChange={onChange} />);
|
||||
|
||||
await selectOptionInTest(screen.getByLabelText('Query type'), 'Label values');
|
||||
const labelSelect = screen.getByLabelText('label-select');
|
||||
const labelSelect = screen.getByTestId(
|
||||
selectors.components.DataSource.Prometheus.variableQueryEditor.labelValues.labelSelect
|
||||
);
|
||||
await userEvent.type(labelSelect, 'this');
|
||||
await selectOptionInTest(labelSelect, 'this');
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { FormEvent, useCallback, useEffect, useState } from 'react';
|
|||
|
||||
import { getDefaultTimeRange, QueryEditorProps, SelectableValue, toOption } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { AsyncSelect, InlineField, InlineFieldRow, Input, Select, TextArea } from '@grafana/ui';
|
||||
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
|
|
@ -124,6 +125,7 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
|
|||
});
|
||||
} else {
|
||||
// fetch the labels filtered by the metric
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
const labelToConsider = [{ label: '__name__', op: '=', value: metric }];
|
||||
const expr = promQueryModeller.renderLabels(labelToConsider);
|
||||
|
||||
|
|
@ -258,15 +260,19 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
|
|||
<>
|
||||
<InlineFieldRow>
|
||||
<InlineField
|
||||
label="Query type"
|
||||
label={t('components.prom-variable-query-editor.label-query-type', 'Query type')}
|
||||
labelWidth={20}
|
||||
tooltip={
|
||||
<div>The Prometheus data source plugin provides the following query types for template variables.</div>
|
||||
<div>
|
||||
<Trans i18nKey="components.prom-variable-query-editor.tooltip-query-type">
|
||||
The Prometheus data source plugin provides the following query types for template variables.
|
||||
</Trans>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Select
|
||||
placeholder="Select query type"
|
||||
aria-label="Query type"
|
||||
placeholder={t('components.prom-variable-query-editor.placeholder-select-query-type', 'Select query type')}
|
||||
aria-label={t('components.prom-variable-query-editor.aria-label-query-type', 'Query type')}
|
||||
onChange={onQueryTypeChange}
|
||||
value={qryType}
|
||||
options={variableOptions}
|
||||
|
|
@ -280,18 +286,19 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
|
|||
<>
|
||||
<InlineFieldRow>
|
||||
<InlineField
|
||||
label="Label"
|
||||
label={t('components.prom-variable-query-editor.label-label', 'Label')}
|
||||
labelWidth={20}
|
||||
required
|
||||
aria-labelledby="label-select"
|
||||
tooltip={
|
||||
<div>
|
||||
Returns a list of label values for the label name in all metrics unless the metric is specified.
|
||||
<Trans i18nKey="components.prom-variable-query-editor.tooltip-label">
|
||||
Returns a list of label values for the label name in all metrics unless the metric is specified.
|
||||
</Trans>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<AsyncSelect
|
||||
aria-label="label-select"
|
||||
onChange={onLabelChange}
|
||||
value={label ? toOption(label) : null}
|
||||
defaultOptions={truncatedLabelOptions}
|
||||
|
|
@ -317,15 +324,21 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
|
|||
{qryType === QueryType.LabelNames && (
|
||||
<InlineFieldRow>
|
||||
<InlineField
|
||||
label="Metric regex"
|
||||
label={t('components.prom-variable-query-editor.label-metric-regex', 'Metric regex')}
|
||||
labelWidth={20}
|
||||
aria-labelledby="Metric regex"
|
||||
tooltip={<div>Returns a list of label names, optionally filtering by specified metric regex.</div>}
|
||||
tooltip={
|
||||
<div>
|
||||
<Trans i18nKey="components.prom-variable-query-editor.tooltip-metric-regex">
|
||||
Returns a list of label names, optionally filtering by specified metric regex.
|
||||
</Trans>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
aria-label="Metric regex"
|
||||
placeholder="Metric regex"
|
||||
aria-label={t('components.prom-variable-query-editor.aria-label-metric-regex', 'Metric regex')}
|
||||
placeholder={t('components.prom-variable-query-editor.placeholder-metric-regex', 'Metric regex')}
|
||||
value={labelNamesMatch}
|
||||
onBlur={(event) => {
|
||||
setLabelNamesMatch(event.currentTarget.value);
|
||||
|
|
@ -344,15 +357,21 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
|
|||
{qryType === QueryType.MetricNames && (
|
||||
<InlineFieldRow>
|
||||
<InlineField
|
||||
label="Metric regex"
|
||||
label={t('components.prom-variable-query-editor.label-metric-regex', 'Metric regex')}
|
||||
labelWidth={20}
|
||||
aria-labelledby="Metric selector"
|
||||
tooltip={<div>Returns a list of metrics matching the specified metric regex.</div>}
|
||||
tooltip={
|
||||
<div>
|
||||
<Trans i18nKey="components.prom-variable-query-editor.returns-metrics-matching-specified-metric-regex">
|
||||
Returns a list of metrics matching the specified metric regex.
|
||||
</Trans>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
aria-label="Metric selector"
|
||||
placeholder="Metric regex"
|
||||
aria-label={t('components.prom-variable-query-editor.aria-label-metric-selector', 'Metric selector')}
|
||||
placeholder={t('components.prom-variable-query-editor.placeholder-metric-regex', 'Metric regex')}
|
||||
value={metric}
|
||||
onChange={(e) => {
|
||||
setMetric(e.currentTarget.value);
|
||||
|
|
@ -371,19 +390,24 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
|
|||
{qryType === QueryType.VarQueryResult && (
|
||||
<InlineFieldRow>
|
||||
<InlineField
|
||||
label="Query"
|
||||
label={t('components.prom-variable-query-editor.label-query', 'Query')}
|
||||
labelWidth={20}
|
||||
tooltip={
|
||||
<div>
|
||||
Returns a list of Prometheus query results for the query. This can include Prometheus functions, i.e.
|
||||
sum(go_goroutines).
|
||||
<Trans
|
||||
i18nKey="components.prom-variable-query-editor.tooltip-query"
|
||||
values={{ exampleQuery: 'sum(go_goroutines)' }}
|
||||
>
|
||||
Returns a list of Prometheus query results for the query. This can include Prometheus functions, i.e.
|
||||
{'{{exampleQuery}}'}.
|
||||
</Trans>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<TextArea
|
||||
type="text"
|
||||
aria-label="Prometheus Query"
|
||||
placeholder="Prometheus Query"
|
||||
aria-label={t('components.prom-variable-query-editor.aria-label-prometheus-query', 'Prometheus Query')}
|
||||
placeholder={t('components.prom-variable-query-editor.placeholder-prometheus-query', 'Prometheus Query')}
|
||||
value={varQuery}
|
||||
onChange={onVarQueryChange}
|
||||
onBlur={() => {
|
||||
|
|
@ -401,21 +425,29 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
|
|||
{qryType === QueryType.SeriesQuery && (
|
||||
<InlineFieldRow>
|
||||
<InlineField
|
||||
label="Series Query"
|
||||
label={t('components.prom-variable-query-editor.label-series-query', 'Series Query')}
|
||||
labelWidth={20}
|
||||
tooltip={
|
||||
<div>
|
||||
Enter a metric with labels, only a metric or only labels, i.e.
|
||||
go_goroutines{instance="localhost:9090"}, go_goroutines, or
|
||||
{instance="localhost:9090"}. Returns a list of time series associated with the
|
||||
entered data.
|
||||
<Trans
|
||||
i18nKey="components.prom-variable-query-editor.tooltip-series-query"
|
||||
values={{
|
||||
example1: 'go_goroutines{instance="localhost:9090"}',
|
||||
example2: 'go_goroutines',
|
||||
example3: '{instance="localhost:9090"}',
|
||||
}}
|
||||
>
|
||||
Enter a metric with labels, only a metric or only labels, i.e.
|
||||
{'{{example1}}'}, {'{{example2}}'}, or {'{{example3}}'}. Returns a list of time series associated with
|
||||
the entered data.
|
||||
</Trans>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
aria-label="Series Query"
|
||||
placeholder="Series Query"
|
||||
aria-label={t('components.prom-variable-query-editor.aria-label-series-query', 'Series Query')}
|
||||
placeholder={t('components.prom-variable-query-editor.placeholder-series-query', 'Series Query')}
|
||||
value={seriesQuery}
|
||||
onChange={onSeriesQueryChange}
|
||||
onBlur={() => {
|
||||
|
|
@ -433,19 +465,26 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
|
|||
{qryType === QueryType.ClassicQuery && (
|
||||
<InlineFieldRow>
|
||||
<InlineField
|
||||
label="Classic Query"
|
||||
label={t('components.prom-variable-query-editor.label-classic-query', 'Classic Query')}
|
||||
labelWidth={20}
|
||||
tooltip={
|
||||
<div>
|
||||
The original implemetation of the Prometheus variable query editor. Enter a string with the correct
|
||||
query type and parameters as described in these docs. For example, label_values(label, metric).
|
||||
<Trans
|
||||
i18nKey="components.prom-variable-query-editor.tooltip-classic-query"
|
||||
values={{
|
||||
exampleQuery: 'label_values(label, metric)',
|
||||
}}
|
||||
>
|
||||
The original implemetation of the Prometheus variable query editor. Enter a string with the correct
|
||||
query type and parameters as described in these docs. For example, {'{{exampleQuery}}'}.
|
||||
</Trans>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
aria-label="Classic Query"
|
||||
placeholder="Classic Query"
|
||||
aria-label={t('components.prom-variable-query-editor.aria-label-classic-query', 'Classic Query')}
|
||||
placeholder={t('components.prom-variable-query-editor.placeholder-classic-query', 'Classic Query')}
|
||||
value={classicQuery}
|
||||
onChange={onClassicQueryChange}
|
||||
onBlur={() => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { BrowserLabel as PromLabel, Input, Label, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { useMetricsBrowser } from './MetricsBrowserContext';
|
||||
|
|
@ -20,13 +21,21 @@ export function LabelSelector() {
|
|||
|
||||
return (
|
||||
<div className={styles.section}>
|
||||
<Label description="Once label values are selected, only possible label combinations are shown.">
|
||||
2. Select labels to search in
|
||||
<Label
|
||||
description={t(
|
||||
'components.label-selector.description-select-labels',
|
||||
'Once label values are selected, only possible label combinations are shown.'
|
||||
)}
|
||||
>
|
||||
<Trans i18nKey="components.label-selector.select-labels-to-search-in">2. Select labels to search in</Trans>
|
||||
</Label>
|
||||
<div>
|
||||
<Input
|
||||
onChange={(e) => setLabelSearchTerm(e.currentTarget.value)}
|
||||
aria-label="Filter expression for label"
|
||||
aria-label={t(
|
||||
'components.label-selector.aria-label-filter-expression-for-label',
|
||||
'Filter expression for label'
|
||||
)}
|
||||
value={labelSearchTerm}
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.labelNamesFilter}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { useMemo, useState } from 'react';
|
|||
import { FixedSizeList } from 'react-window';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { BrowserLabel as PromLabel, Input, Label, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { useMetricsBrowser } from './MetricsBrowserContext';
|
||||
|
|
@ -20,24 +21,40 @@ export function MetricSelector() {
|
|||
return (
|
||||
<div>
|
||||
<div className={styles.section}>
|
||||
<Label description="Once a metric is selected only possible labels are shown. Labels are limited by the series limit below.">
|
||||
1. Select a metric
|
||||
<Label
|
||||
description={t(
|
||||
'components.metric-selector.label-select-metric',
|
||||
'Once a metric is selected only possible labels are shown. Labels are limited by the series limit below.'
|
||||
)}
|
||||
>
|
||||
<Trans i18nKey="components.metric-selector.select-a-metric">1. Select a metric</Trans>
|
||||
</Label>
|
||||
<div>
|
||||
<Input
|
||||
onChange={(e) => setMetricSearchTerm(e.currentTarget.value)}
|
||||
aria-label="Filter expression for metric"
|
||||
aria-label={t(
|
||||
'components.metric-selector.aria-label-filter-expression-for-metric',
|
||||
'Filter expression for metric'
|
||||
)}
|
||||
value={metricSearchTerm}
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.selectMetric}
|
||||
/>
|
||||
</div>
|
||||
<Label description="The limit applies to all metrics, labels, and values. Leave the field empty to use the default limit. Set to 0 to disable the limit and fetch everything — this may cause performance issues.">
|
||||
Series limit
|
||||
<Label
|
||||
description={t(
|
||||
'components.metric-selector.description-series-limit',
|
||||
'The limit applies to all metrics, labels, and values. Leave the field empty to use the default limit. Set to 0 to disable the limit and fetch everything — this may cause performance issues.'
|
||||
)}
|
||||
>
|
||||
<Trans i18nKey="components.metric-selector.series-limit">Series limit</Trans>
|
||||
</Label>
|
||||
<div>
|
||||
<Input
|
||||
onChange={(e) => setSeriesLimit(e.currentTarget.value.trim())}
|
||||
aria-label="Limit results from series endpoint"
|
||||
aria-label={t(
|
||||
'components.metric-selector.aria-label-limit-results-from-series-endpoint',
|
||||
'Limit results from series endpoint'
|
||||
)}
|
||||
value={seriesLimit}
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.seriesLimit}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { cx } from '@emotion/css';
|
|||
import { useMemo } from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { Button, Label, Stack, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { useMetricsBrowser } from './MetricsBrowserContext';
|
||||
|
|
@ -27,45 +28,53 @@ export function SelectorActions() {
|
|||
|
||||
return (
|
||||
<div className={styles.section}>
|
||||
<Label>4. Resulting selector</Label>
|
||||
<div aria-label="selector" className={styles.selector}>
|
||||
<Label>
|
||||
<Trans i18nKey="components.selector-actions.resulting-selector">4. Resulting selector</Trans>
|
||||
</Label>
|
||||
<div aria-label={t('components.selector-actions.aria-label-selector', 'selector')} className={styles.selector}>
|
||||
{selector}
|
||||
</div>
|
||||
{validationStatus && <div className={styles.validationStatus}>{validationStatus}</div>}
|
||||
<Stack>
|
||||
<Button
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.useQuery}
|
||||
aria-label="Use selector for query button"
|
||||
aria-label={t(
|
||||
'components.selector-actions.aria-label-use-selector-for-query-button',
|
||||
'Use selector for query button'
|
||||
)}
|
||||
disabled={empty}
|
||||
onClick={onClickRunQuery}
|
||||
>
|
||||
Use query
|
||||
<Trans i18nKey="components.selector-actions.use-query">Use query</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.useAsRateQuery}
|
||||
aria-label="Use selector as metrics button"
|
||||
aria-label={t(
|
||||
'components.selector-actions.aria-label-use-selector-as-metrics-button',
|
||||
'Use selector as metrics button'
|
||||
)}
|
||||
variant="secondary"
|
||||
disabled={empty}
|
||||
onClick={onClickRunRateQuery}
|
||||
>
|
||||
Use as rate query
|
||||
<Trans i18nKey="components.selector-actions.use-as-rate-query">Use as rate query</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.validateSelector}
|
||||
aria-label="Validate submit button"
|
||||
aria-label={t('components.selector-actions.aria-label-validate-submit-button', 'Validate submit button')}
|
||||
variant="secondary"
|
||||
disabled={empty}
|
||||
onClick={onValidationClick}
|
||||
>
|
||||
Validate selector
|
||||
<Trans i18nKey="components.selector-actions.validate-selector">Validate selector</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.clear}
|
||||
aria-label="Selector clear button"
|
||||
aria-label={t('components.selector-actions.aria-label-selector-clear-button', 'Selector clear button')}
|
||||
variant="secondary"
|
||||
onClick={onClearClick}
|
||||
>
|
||||
Clear
|
||||
<Trans i18nKey="components.selector-actions.clear">Clear</Trans>
|
||||
</Button>
|
||||
<div className={cx(styles.status, (status || err) && styles.statusShowing)}>
|
||||
<span className={err ? styles.error : ''}>{err || status}</span>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { useState } from 'react';
|
|||
import { FixedSizeList } from 'react-window';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { BrowserLabel as PromLabel, Input, Label, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { useMetricsBrowser } from './MetricsBrowserContext';
|
||||
|
|
@ -15,13 +16,23 @@ export function ValueSelector() {
|
|||
|
||||
return (
|
||||
<div className={styles.section}>
|
||||
<Label description="Use the search field to find values across selected labels.">
|
||||
3. Select (multiple) values for your labels
|
||||
<Label
|
||||
description={t(
|
||||
'components.value-selector.description-search-field-values-across-selected-labels',
|
||||
'Use the search field to find values across selected labels.'
|
||||
)}
|
||||
>
|
||||
<Trans i18nKey="components.value-selector.select-multiple-values-for-your-labels">
|
||||
3. Select (multiple) values for your labels
|
||||
</Trans>
|
||||
</Label>
|
||||
<div>
|
||||
<Input
|
||||
onChange={(e) => setValueSearchTerm(e.currentTarget.value)}
|
||||
aria-label="Filter expression for label values"
|
||||
aria-label={t(
|
||||
'components.value-selector.aria-label-filter-expression-for-label-values',
|
||||
'Filter expression for label values'
|
||||
)}
|
||||
value={valueSearchTerm}
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.labelValuesFilter}
|
||||
/>
|
||||
|
|
@ -33,7 +44,14 @@ export function ValueSelector() {
|
|||
return null;
|
||||
}
|
||||
return (
|
||||
<div role="list" key={lk} aria-label={`Values for ${lk}`} className={styles.valueListWrapper}>
|
||||
<div
|
||||
role="list"
|
||||
key={lk}
|
||||
aria-label={t('components.value-selector.aria-label-values-for', 'Values for {{labelKey}}', {
|
||||
labelKey: lk,
|
||||
})}
|
||||
className={styles.valueListWrapper}
|
||||
>
|
||||
<div className={styles.valueTitle}>
|
||||
<PromLabel
|
||||
name={lk}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { cx } from '@emotion/css';
|
|||
|
||||
import { DataSourceJsonData, DataSourcePluginOptionsEditorProps } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { ConfigSubSection } from '@grafana/plugin-ui';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { InlineField, Switch, useTheme2 } from '@grafana/ui';
|
||||
|
|
@ -26,18 +27,27 @@ export function AlertingSettingsOverhaul<T extends AlertingConfig>({
|
|||
const styles = overhaulStyles(theme);
|
||||
|
||||
return (
|
||||
<ConfigSubSection title="Alerting" className={cx(styles.container, styles.alertingTop)}>
|
||||
<ConfigSubSection
|
||||
title={t('configuration.alerting-settings-overhaul.title-alerting', 'Alerting')}
|
||||
className={cx(styles.container, styles.alertingTop)}
|
||||
>
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
labelWidth={30}
|
||||
label="Manage alerts via Alerting UI"
|
||||
label={t(
|
||||
'configuration.alerting-settings-overhaul.label-manage-alerts-via-alerting-ui',
|
||||
'Manage alerts via Alerting UI'
|
||||
)}
|
||||
disabled={options.readOnly}
|
||||
tooltip={
|
||||
<>
|
||||
Manage alert rules for this data source. To manage other alerting resources, add an Alertmanager data
|
||||
source. {docsTip()}
|
||||
<Trans i18nKey="configuration.alerting-settings-overhaul.tooltip-manage-alerts-via-alerting-ui">
|
||||
Manage alert rules for this data source. To manage other alerting resources, add an Alertmanager
|
||||
data source.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
import { css } from '@emotion/css';
|
||||
|
||||
import { DataSourcePluginOptionsEditorProps, GrafanaTheme2 } from '@grafana/data';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { ConfigSection, DataSourceDescription, AdvancedHttpSettings } from '@grafana/plugin-ui';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Alert, FieldValidationMessage, useTheme2 } from '@grafana/ui';
|
||||
import { Alert, FieldValidationMessage, TextLink, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { PromOptions } from '../types';
|
||||
|
||||
|
|
@ -24,8 +25,10 @@ export const ConfigEditor = (props: PrometheusConfigProps) => {
|
|||
return (
|
||||
<>
|
||||
{options.access === 'direct' && (
|
||||
<Alert title="Error" severity="error">
|
||||
Browser access mode in the Prometheus data source is no longer available. Switch to server access mode.
|
||||
<Alert title={t('configuration.config-editor.title-error', 'Error')} severity="error">
|
||||
<Trans i18nKey="configuration.config-editor.browser-access-mode-error">
|
||||
Browser access mode in the Prometheus data source is no longer available. Switch to server access mode.
|
||||
</Trans>
|
||||
</Alert>
|
||||
)}
|
||||
<DataSourceDescription
|
||||
|
|
@ -41,8 +44,11 @@ export const ConfigEditor = (props: PrometheusConfigProps) => {
|
|||
<hr />
|
||||
<ConfigSection
|
||||
className={styles.advancedSettings}
|
||||
title="Advanced settings"
|
||||
description="Additional settings are optional settings that can be configured for more control over your data source."
|
||||
title={t('configuration.config-editor.title-advanced-settings', 'Advanced settings')}
|
||||
description={t(
|
||||
'configuration.config-editor.description-advanced-settings',
|
||||
'Additional settings are optional settings that can be configured for more control over your data source.'
|
||||
)}
|
||||
>
|
||||
<AdvancedHttpSettings
|
||||
className={styles.advancedHTTPSettingsMargin}
|
||||
|
|
@ -65,9 +71,9 @@ export function docsTip(url?: string) {
|
|||
const docsUrl = 'https://grafana.com/docs/grafana/latest/datasources/prometheus/configure-prometheus-data-source/';
|
||||
|
||||
return (
|
||||
<a href={url ? url : docsUrl} target="_blank" rel="noopener noreferrer">
|
||||
Visit docs for more details here.
|
||||
</a>
|
||||
<TextLink href={url ? url : docsUrl} external>
|
||||
<Trans i18nKey="configuration.docs-tip.visit-docs-for-more-details-here">Visit docs for more details here.</Trans>
|
||||
</TextLink>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/configuration/DataSourceHttpSettingsOverhaul.tsx
|
||||
import { DataSourceSettings } from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { Auth, AuthMethod, ConnectionSettings, convertLegacyAuthProps } from '@grafana/plugin-ui';
|
||||
import { SecureSocksProxySettings, useTheme2 } from '@grafana/ui';
|
||||
|
||||
|
|
@ -34,7 +35,9 @@ export const DataSourceHttpSettingsOverhaul = (props: DataSourceHttpSettingsProp
|
|||
case 'direct':
|
||||
urlTooltip = (
|
||||
<>
|
||||
Your access method is <em>Browser</em>, this means the URL needs to be accessible from the browser.
|
||||
<Trans i18nKey="configuration.data-source-http-settings-overhaul.tooltip-browser-access-mode">
|
||||
Your access method is <em>Browser</em>, this means the URL needs to be accessible from the browser.
|
||||
</Trans>
|
||||
{docsTip()}
|
||||
</>
|
||||
);
|
||||
|
|
@ -42,14 +45,26 @@ export const DataSourceHttpSettingsOverhaul = (props: DataSourceHttpSettingsProp
|
|||
case 'proxy':
|
||||
urlTooltip = (
|
||||
<>
|
||||
Your access method is <em>Server</em>, this means the URL needs to be accessible from the grafana
|
||||
backend/server.
|
||||
<Trans i18nKey="configuration.data-source-http-settings-overhaul.tooltip-server-access-mode">
|
||||
Your access method is <em>Server</em>, this means the URL needs to be accessible from the grafana
|
||||
backend/server.
|
||||
</Trans>
|
||||
{docsTip()}
|
||||
</>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
urlTooltip = <>Specify a complete HTTP URL (for example http://your_server:8080) {docsTip()}</>;
|
||||
urlTooltip = (
|
||||
<>
|
||||
<Trans
|
||||
i18nKey="configuration.data-source-http-settings-overhaul.tooltip-http-url"
|
||||
values={{ exampleURL: 'http://your_server:8080' }}
|
||||
>
|
||||
Specify a complete HTTP URL (for example {'{{exampleURL}}'})
|
||||
</Trans>
|
||||
{docsTip()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useState } from 'react';
|
|||
|
||||
import { DataSourceInstanceSettings } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { config, DataSourcePicker } from '@grafana/runtime';
|
||||
import { Button, InlineField, Input, Switch, useTheme2 } from '@grafana/ui';
|
||||
|
||||
|
|
@ -26,13 +27,16 @@ export function ExemplarSetting({ value, onChange, onDelete, disabled }: Props)
|
|||
return (
|
||||
<div className="gf-form-group">
|
||||
<InlineField
|
||||
label="Internal link"
|
||||
label={t('configuration.exemplar-setting.label-internal-link', 'Internal link')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
disabled={disabled}
|
||||
tooltip={
|
||||
<>
|
||||
Enable this option if you have an internal link. When enabled, this reveals the data source selector. Select
|
||||
the backend tracing data store for your exemplar data. {docsTip()}
|
||||
<Trans i18nKey="configuration.exemplar-setting.tooltip-internal-link">
|
||||
Enable this option if you have an internal link. When enabled, this reveals the data source selector.
|
||||
Select the backend tracing data store for your exemplar data.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
|
|
@ -49,9 +53,16 @@ export function ExemplarSetting({ value, onChange, onDelete, disabled }: Props)
|
|||
|
||||
{isInternalLink ? (
|
||||
<InlineField
|
||||
label="Data source"
|
||||
label={t('configuration.exemplar-setting.label-data-source', 'Data source')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={<>The data source the exemplar is going to navigate to. {docsTip()}</>}
|
||||
tooltip={
|
||||
<>
|
||||
<Trans i18nKey="configuration.exemplar-setting.tooltip-data-source">
|
||||
The data source the exemplar is going to navigate to.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
disabled={disabled}
|
||||
interactive={true}
|
||||
>
|
||||
|
|
@ -76,14 +87,24 @@ export function ExemplarSetting({ value, onChange, onDelete, disabled }: Props)
|
|||
</InlineField>
|
||||
) : (
|
||||
<InlineField
|
||||
label="URL"
|
||||
label={t('configuration.exemplar-setting.label-url', 'URL')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={<>The URL of the trace backend the user would go to see its trace. {docsTip()}</>}
|
||||
tooltip={
|
||||
<>
|
||||
<Trans i18nKey="configuration.exemplar-setting.tooltip-url">
|
||||
The URL of the trace backend the user would go to see its trace
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
disabled={disabled}
|
||||
interactive={true}
|
||||
>
|
||||
<Input
|
||||
placeholder="https://example.com/${__value.raw}"
|
||||
placeholder={t(
|
||||
'configuration.exemplar-setting.placeholder-httpsexamplecomvalueraw',
|
||||
'https://example.com/${__value.raw}'
|
||||
)}
|
||||
spellCheck={false}
|
||||
width={40}
|
||||
value={value.url}
|
||||
|
|
@ -99,14 +120,21 @@ export function ExemplarSetting({ value, onChange, onDelete, disabled }: Props)
|
|||
)}
|
||||
|
||||
<InlineField
|
||||
label="URL Label"
|
||||
label={t('configuration.exemplar-setting.label-url-label', 'URL Label')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={<>Use to override the button label on the exemplar traceID field. {docsTip()}</>}
|
||||
tooltip={
|
||||
<>
|
||||
<Trans i18nKey="configuration.exemplar-setting.tooltip-url-label">
|
||||
Use to override the button label on the exemplar traceID field.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
disabled={disabled}
|
||||
interactive={true}
|
||||
>
|
||||
<Input
|
||||
placeholder="Go to example.com"
|
||||
placeholder={t('configuration.exemplar-setting.placeholder-go-to-examplecom', 'Go to example.com')}
|
||||
spellCheck={false}
|
||||
width={40}
|
||||
value={value.urlDisplayLabel}
|
||||
|
|
@ -119,14 +147,21 @@ export function ExemplarSetting({ value, onChange, onDelete, disabled }: Props)
|
|||
/>
|
||||
</InlineField>
|
||||
<InlineField
|
||||
label="Label name"
|
||||
label={t('configuration.exemplar-setting.label-label-name', 'Label name')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={<>The name of the field in the labels object that should be used to get the traceID. {docsTip()}</>}
|
||||
tooltip={
|
||||
<>
|
||||
<Trans i18nKey="configuration.exemplar-setting.tooltip-label-name">
|
||||
The name of the field in the labels object that should be used to get the traceID.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
disabled={disabled}
|
||||
interactive={true}
|
||||
>
|
||||
<Input
|
||||
placeholder="traceID"
|
||||
placeholder={t('configuration.exemplar-setting.placeholder-trace-id', 'traceID')}
|
||||
spellCheck={false}
|
||||
width={40}
|
||||
value={value.name}
|
||||
|
|
@ -139,10 +174,14 @@ export function ExemplarSetting({ value, onChange, onDelete, disabled }: Props)
|
|||
/>
|
||||
</InlineField>
|
||||
{!disabled && (
|
||||
<InlineField label="Remove exemplar link" labelWidth={PROM_CONFIG_LABEL_WIDTH} disabled={disabled}>
|
||||
<InlineField
|
||||
label={t('configuration.exemplar-setting.label-remove-exemplar-link', 'Remove exemplar link')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Button
|
||||
variant="destructive"
|
||||
title="Remove exemplar link"
|
||||
title={t('configuration.exemplar-setting.title-remove-exemplar-link', 'Remove exemplar link')}
|
||||
icon="times"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { css } from '@emotion/css';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { ConfigSubSection } from '@grafana/plugin-ui';
|
||||
import { Button, useTheme2 } from '@grafana/ui';
|
||||
|
||||
|
|
@ -21,7 +22,10 @@ export function ExemplarsSettings({ options, onChange, disabled }: Props) {
|
|||
const styles = overhaulStyles(theme);
|
||||
return (
|
||||
<div className={styles.sectionBottomPadding}>
|
||||
<ConfigSubSection title="Exemplars" className={styles.container}>
|
||||
<ConfigSubSection
|
||||
title={t('configuration.exemplars-settings.title-exemplars', 'Exemplars')}
|
||||
className={styles.container}
|
||||
>
|
||||
{options &&
|
||||
options.map((option, index) => {
|
||||
return (
|
||||
|
|
@ -57,10 +61,16 @@ export function ExemplarsSettings({ options, onChange, disabled }: Props) {
|
|||
onChange(newOptions);
|
||||
}}
|
||||
>
|
||||
Add
|
||||
<Trans i18nKey="configuration.exemplars-settings.add">Add</Trans>
|
||||
</Button>
|
||||
)}
|
||||
{disabled && !options && <i>No exemplars configurations</i>}
|
||||
{disabled && !options && (
|
||||
<i>
|
||||
<Trans i18nKey="configuration.exemplars-settings.no-exemplars-configurations">
|
||||
No exemplars configurations
|
||||
</Trans>
|
||||
</i>
|
||||
)}
|
||||
</ConfigSubSection>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ import {
|
|||
updateDatasourcePluginJsonDataOption,
|
||||
} from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { ConfigSubSection } from '@grafana/plugin-ui';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { InlineField, Input, Select, Switch, useTheme2 } from '@grafana/ui';
|
||||
import { InlineField, Input, Select, Switch, TextLink, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { SUGGESTIONS_LIMIT } from '../language_provider';
|
||||
import { QueryEditorMode } from '../querybuilder/shared/types';
|
||||
|
|
@ -100,20 +101,26 @@ export const PromSettings = (props: Props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<ConfigSubSection title="Interval behaviour" className={styles.container}>
|
||||
<ConfigSubSection
|
||||
title={t('configuration.prom-settings.title-interval-behaviour', 'Interval behaviour')}
|
||||
className={styles.container}
|
||||
>
|
||||
<div className="gf-form-group">
|
||||
{/* Scrape interval */}
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label="Scrape interval"
|
||||
label={t('configuration.prom-settings.label-scrape-interval', 'Scrape interval')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
This interval is how frequently Prometheus scrapes targets. Set this to the typical scrape and
|
||||
evaluation interval configured in your Prometheus config file. If you set this to a greater value
|
||||
than your Prometheus config file interval, Grafana will evaluate the data according to this interval
|
||||
and you will see less data points. Defaults to 15s. {docsTip()}
|
||||
<Trans i18nKey="configuration.prom-settings.tooltip-scrape-interval" values={{ default: '15s' }}>
|
||||
This interval is how frequently Prometheus scrapes targets. Set this to the typical scrape and
|
||||
evaluation interval configured in your Prometheus config file. If you set this to a greater value
|
||||
than your Prometheus config file interval, Grafana will evaluate the data according to this
|
||||
interval and you will see less data points. Defaults to {'{{default}}'}.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
|
|
@ -124,6 +131,7 @@ export const PromSettings = (props: Props) => {
|
|||
className="width-20"
|
||||
value={optionsWithDefaults.jsonData.timeInterval}
|
||||
spellCheck={false}
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
placeholder="15s"
|
||||
onChange={onChangeHandler('timeInterval', optionsWithDefaults, onOptionsChange)}
|
||||
onBlur={(e) =>
|
||||
|
|
@ -143,9 +151,16 @@ export const PromSettings = (props: Props) => {
|
|||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label="Query timeout"
|
||||
label={t('configuration.prom-settings.label-query-timeout', 'Query timeout')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={<>Set the Prometheus query timeout. {docsTip()}</>}
|
||||
tooltip={
|
||||
<>
|
||||
<Trans i18nKey="configuration.prom-settings.tooltip-query-timeout">
|
||||
Set the Prometheus query timeout.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={optionsWithDefaults.readOnly}
|
||||
>
|
||||
|
|
@ -155,6 +170,7 @@ export const PromSettings = (props: Props) => {
|
|||
value={optionsWithDefaults.jsonData.queryTimeout}
|
||||
onChange={onChangeHandler('queryTimeout', optionsWithDefaults, onOptionsChange)}
|
||||
spellCheck={false}
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
placeholder="60s"
|
||||
onBlur={(e) =>
|
||||
updateValidDuration({
|
||||
|
|
@ -172,18 +188,31 @@ export const PromSettings = (props: Props) => {
|
|||
</div>
|
||||
</ConfigSubSection>
|
||||
|
||||
<ConfigSubSection title="Query editor" className={styles.container}>
|
||||
<ConfigSubSection
|
||||
title={t('configuration.prom-settings.title-query-editor', 'Query editor')}
|
||||
className={styles.container}
|
||||
>
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label="Default editor"
|
||||
label={t('configuration.prom-settings.label-default-editor', 'Default editor')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={<>Set default editor option for all users of this data source. {docsTip()}</>}
|
||||
tooltip={
|
||||
<>
|
||||
<Trans i18nKey="configuration.prom-settings.tooltip-default-editor">
|
||||
Set default editor option for all users of this data source.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={optionsWithDefaults.readOnly}
|
||||
>
|
||||
<Select
|
||||
aria-label={`Default Editor (Code or Builder)`}
|
||||
aria-label={t(
|
||||
'configuration.prom-settings.aria-label-default-editor',
|
||||
'Default Editor (Code or Builder)'
|
||||
)}
|
||||
options={editorOptions}
|
||||
value={
|
||||
editorOptions.find((o) => o.value === optionsWithDefaults.jsonData.defaultEditor) ??
|
||||
|
|
@ -198,11 +227,14 @@ export const PromSettings = (props: Props) => {
|
|||
<div className="gf-form">
|
||||
<InlineField
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
label="Disable metrics lookup"
|
||||
label={t('configuration.prom-settings.label-disable-metrics-lookup', 'Disable metrics lookup')}
|
||||
tooltip={
|
||||
<>
|
||||
Checking this option will disable the metrics chooser and metric/label support in the query
|
||||
field's autocomplete. This helps if you have performance issues with bigger Prometheus instances.{' '}
|
||||
<Trans i18nKey="configuration.prom-settings.tooltip-disable-metrics-lookup">
|
||||
Checking this option will disable the metrics chooser and metric/label support in the query
|
||||
field's autocomplete. This helps if you have performance issues with bigger Prometheus
|
||||
instances.{' '}
|
||||
</Trans>
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
|
|
@ -220,43 +252,48 @@ export const PromSettings = (props: Props) => {
|
|||
</div>
|
||||
</ConfigSubSection>
|
||||
|
||||
<ConfigSubSection title="Performance" className={styles.container}>
|
||||
<ConfigSubSection
|
||||
title={t('configuration.prom-settings.title-performance', 'Performance')}
|
||||
className={styles.container}
|
||||
>
|
||||
{!optionsWithDefaults.jsonData.prometheusType &&
|
||||
!optionsWithDefaults.jsonData.prometheusVersion &&
|
||||
optionsWithDefaults.readOnly && (
|
||||
<div className={styles.versionMargin}>
|
||||
For more information on configuring prometheus type and version in data sources, see the{' '}
|
||||
<a
|
||||
className={styles.textUnderline}
|
||||
href="https://grafana.com/docs/grafana/latest/administration/provisioning/"
|
||||
>
|
||||
provisioning documentation
|
||||
</a>
|
||||
.
|
||||
<Trans i18nKey="configuration.prom-settings.more-info">
|
||||
For more information on configuring prometheus type and version in data sources, see the{' '}
|
||||
<TextLink external href="https://grafana.com/docs/grafana/latest/administration/provisioning/">
|
||||
provisioning documentation
|
||||
</TextLink>
|
||||
.
|
||||
</Trans>
|
||||
</div>
|
||||
)}
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label="Prometheus type"
|
||||
label={t('configuration.prom-settings.label-prometheus-type', 'Prometheus type')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
{/* , and attempt to detect the version */}
|
||||
Set this to the type of your prometheus database, e.g. Prometheus, Cortex, Mimir or Thanos. Changing
|
||||
this field will save your current settings. Certain types of Prometheus supports or does not support
|
||||
various APIs. For example, some types support regex matching for label queries to improve
|
||||
performance. Some types have an API for metadata. If you set this incorrectly you may experience odd
|
||||
behavior when querying metrics and labels. Please check your Prometheus documentation to ensure you
|
||||
enter the correct type. {docsTip()}
|
||||
<Trans i18nKey="configuration.prom-settings.tooltip-prometheus-type">
|
||||
Set this to the type of your prometheus database, e.g. Prometheus, Cortex, Mimir or Thanos.
|
||||
Changing this field will save your current settings. Certain types of Prometheus supports or does
|
||||
not support various APIs. For example, some types support regex matching for label queries to
|
||||
improve performance. Some types have an API for metadata. If you set this incorrectly you may
|
||||
experience odd behavior when querying metrics and labels. Please check your Prometheus
|
||||
documentation to ensure you enter the correct type.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={optionsWithDefaults.readOnly}
|
||||
>
|
||||
<Select
|
||||
aria-label="Prometheus type"
|
||||
aria-label={t('configuration.prom-settings.aria-label-prometheus-type', 'Prometheus type')}
|
||||
options={prometheusFlavorSelectItems}
|
||||
value={prometheusFlavorSelectItems.find(
|
||||
(o) => o.value === optionsWithDefaults.jsonData.prometheusType
|
||||
|
|
@ -272,19 +309,29 @@ export const PromSettings = (props: Props) => {
|
|||
{optionsWithDefaults.jsonData.prometheusType && (
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label={`${optionsWithDefaults.jsonData.prometheusType} version`}
|
||||
label={t('configuration.prom-settings.label-prom-type-version', '{{promType}} version', {
|
||||
promType: optionsWithDefaults.jsonData.prometheusType,
|
||||
})}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Use this to set the version of your {optionsWithDefaults.jsonData.prometheusType} instance if it
|
||||
is not automatically configured. {docsTip()}
|
||||
<Trans
|
||||
i18nKey="configuration.prom-settings.tooltip-prom-type-version"
|
||||
values={{ promType: optionsWithDefaults.jsonData.prometheusType }}
|
||||
>
|
||||
Use this to set the version of your {'{{promType}}'} instance if it is not automatically
|
||||
configured.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
disabled={optionsWithDefaults.readOnly}
|
||||
>
|
||||
<Select
|
||||
aria-label={`${optionsWithDefaults.jsonData.prometheusType} type`}
|
||||
aria-label={t('configuration.prom-settings.aria-label-prom-type-type', '{{promType}} type', {
|
||||
promType: optionsWithDefaults.jsonData.prometheusType,
|
||||
})}
|
||||
options={PromFlavorVersions[optionsWithDefaults.jsonData.prometheusType]}
|
||||
value={PromFlavorVersions[optionsWithDefaults.jsonData.prometheusType]?.find(
|
||||
(o) => o.value === optionsWithDefaults.jsonData.prometheusVersion
|
||||
|
|
@ -301,12 +348,14 @@ export const PromSettings = (props: Props) => {
|
|||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30">
|
||||
<InlineField
|
||||
label="Cache level"
|
||||
label={t('configuration.prom-settings.label-cache-level', 'Cache level')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Sets the browser caching level for editor queries. Higher cache settings are recommended for high
|
||||
cardinality data sources.
|
||||
<Trans i18nKey="configuration.prom-settings.tooltip-cache-level">
|
||||
Sets the browser caching level for editor queries. Higher cache settings are recommended for high
|
||||
cardinality data sources.
|
||||
</Trans>
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
|
|
@ -330,12 +379,17 @@ export const PromSettings = (props: Props) => {
|
|||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<InlineField
|
||||
label="Metric names suggestion limit"
|
||||
label={t(
|
||||
'configuration.prom-settings.label-metric-names-suggestion-limit',
|
||||
'Metric names suggestion limit'
|
||||
)}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
The maximum number of metric names that may appear as autocomplete suggestions in the query
|
||||
editor's Code mode.
|
||||
<Trans i18nKey="configuration.prom-settings.tooltip-metric-names-suggestion-limit">
|
||||
The maximum number of metric names that may appear as autocomplete suggestions in the query
|
||||
editor's Code mode.
|
||||
</Trans>
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
|
|
@ -376,13 +430,15 @@ export const PromSettings = (props: Props) => {
|
|||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30">
|
||||
<InlineField
|
||||
label="Incremental querying (beta)"
|
||||
label={t('configuration.prom-settings.label-incremental-querying-beta', 'Incremental querying (beta)')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
This feature will change the default behavior of relative queries to always request fresh data from
|
||||
the prometheus instance, instead query results will be cached, and only new records are requested.
|
||||
Turn this on to decrease database and network load.
|
||||
<Trans i18nKey="configuration.prom-settings.tooltip-incremental-querying-beta">
|
||||
This feature will change the default behavior of relative queries to always request fresh data
|
||||
from the prometheus instance, instead query results will be cached, and only new records are
|
||||
requested. Turn this on to decrease database and network load.
|
||||
</Trans>
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
|
|
@ -401,12 +457,22 @@ export const PromSettings = (props: Props) => {
|
|||
<div className="gf-form-inline">
|
||||
{optionsWithDefaults.jsonData.incrementalQuerying && (
|
||||
<InlineField
|
||||
label="Query overlap window"
|
||||
label={t('configuration.prom-settings.label-query-overlap-window', 'Query overlap window')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Set a duration like 10m or 120s or 0s. Default of 10 minutes. This duration will be added to the
|
||||
duration of each incremental request.
|
||||
<Trans
|
||||
i18nKey="configuration.prom-settings.tooltip-query-overlap-window"
|
||||
values={{
|
||||
example1: '10m',
|
||||
example2: '120s',
|
||||
example3: '0s',
|
||||
default: '10m',
|
||||
}}
|
||||
>
|
||||
Set a duration like {'{{example1}}'} or {'{{example2}}'} or {'{{example3}}'}. Default of{' '}
|
||||
{'{{default}}'}. This duration will be added to the duration of each incremental request.
|
||||
</Trans>
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
|
|
@ -437,9 +503,18 @@ export const PromSettings = (props: Props) => {
|
|||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30">
|
||||
<InlineField
|
||||
label="Disable recording rules (beta)"
|
||||
label={t(
|
||||
'configuration.prom-settings.label-disable-recording-rules-beta',
|
||||
'Disable recording rules (beta)'
|
||||
)}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={<>This feature will disable recording rules Turn this on to improve dashboard performance</>}
|
||||
tooltip={
|
||||
<>
|
||||
<Trans i18nKey="configuration.prom-settings.tooltip-disable-recording-rules-beta">
|
||||
This feature will disable recording rules. Turn this on to improve dashboard performance
|
||||
</Trans>
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
className={styles.switchField}
|
||||
disabled={optionsWithDefaults.readOnly}
|
||||
|
|
@ -455,17 +530,31 @@ export const PromSettings = (props: Props) => {
|
|||
</div>
|
||||
</ConfigSubSection>
|
||||
|
||||
<ConfigSubSection title="Other" className={styles.container}>
|
||||
<ConfigSubSection title={t('configuration.prom-settings.title-other', 'Other')} className={styles.container}>
|
||||
<div className="gf-form-group">
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form max-width-30">
|
||||
<InlineField
|
||||
label="Custom query parameters"
|
||||
label={t('configuration.prom-settings.label-custom-query-parameters', 'Custom query parameters')}
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
Add custom parameters to the Prometheus query URL. For example timeout, partial_response, dedup, or
|
||||
max_source_resolution. Multiple parameters should be concatenated together with an ‘&’. {docsTip()}
|
||||
<Trans
|
||||
i18nKey="configuration.prom-settings.tooltip-custom-query-parameters"
|
||||
values={{
|
||||
example1: 'timeout',
|
||||
example2: 'partial_response',
|
||||
example3: 'dedup',
|
||||
example4: 'max_source_resolution',
|
||||
concatenationChar: '‘&’',
|
||||
}}
|
||||
>
|
||||
Add custom parameters to the Prometheus query URL. For example {'{{example1}}'}, {'{{example2}}'},{' '}
|
||||
{'{{example3}}'}, or
|
||||
{'{{example4}}'}. Multiple parameters should be concatenated together with{' '}
|
||||
{'{{concatenationChar}}'}.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
|
|
@ -476,7 +565,11 @@ export const PromSettings = (props: Props) => {
|
|||
value={optionsWithDefaults.jsonData.customQueryParameters}
|
||||
onChange={onChangeHandler('customQueryParameters', optionsWithDefaults, onOptionsChange)}
|
||||
spellCheck={false}
|
||||
placeholder="Example: max_source_resolution=5m&timeout=10"
|
||||
placeholder={t(
|
||||
'configuration.prom-settings.placeholder-example-maxsourceresolutionmtimeout',
|
||||
'Example: {{example}}',
|
||||
{ example: 'max_source_resolution=5m&timeout=10' }
|
||||
)}
|
||||
data-testid={selectors.components.DataSource.Prometheus.configPage.customQueryParameters}
|
||||
/>
|
||||
</InlineField>
|
||||
|
|
@ -489,18 +582,21 @@ export const PromSettings = (props: Props) => {
|
|||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
tooltip={
|
||||
<>
|
||||
You can use either POST or GET HTTP method to query your Prometheus data source. POST is the
|
||||
recommended method as it allows bigger queries. Change this to GET if you have a Prometheus version
|
||||
older than 2.1 or if POST requests are restricted in your network. {docsTip()}
|
||||
<Trans i18nKey="configuration.prom-settings.tooltip-http-method">
|
||||
You can use either POST or GET HTTP method to query your Prometheus data source. POST is the
|
||||
recommended method as it allows bigger queries. Change this to GET if you have a Prometheus
|
||||
version older than 2.1 or if POST requests are restricted in your network.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
label="HTTP method"
|
||||
label={t('configuration.prom-settings.label-http-method', 'HTTP method')}
|
||||
disabled={optionsWithDefaults.readOnly}
|
||||
>
|
||||
<Select
|
||||
width={40}
|
||||
aria-label="Select HTTP method"
|
||||
aria-label={t('configuration.prom-settings.aria-label-select-http-method', 'Select HTTP method')}
|
||||
options={httpOptions}
|
||||
value={httpOptions.find((o) => o.value === optionsWithDefaults.jsonData.httpMethod)}
|
||||
onChange={onChangeHandler('httpMethod', optionsWithDefaults, onOptionsChange)}
|
||||
|
|
@ -511,13 +607,19 @@ export const PromSettings = (props: Props) => {
|
|||
</div>
|
||||
<InlineField
|
||||
labelWidth={PROM_CONFIG_LABEL_WIDTH}
|
||||
label="Use series endpoint"
|
||||
label={t('configuration.prom-settings.label-use-series-endpoint', 'Use series endpoint')}
|
||||
tooltip={
|
||||
<>
|
||||
Checking this option will favor the series endpoint with match[] parameter over the label values
|
||||
endpoint with match[] parameter. While the label values endpoint is considered more performant, some
|
||||
users may prefer the series because it has a POST method while the label values endpoint only has a GET
|
||||
method. {docsTip()}
|
||||
<Trans
|
||||
i18nKey="configuration.prom-settings.tooltip-use-series-endpoint"
|
||||
values={{ exampleParameter: 'match[]' }}
|
||||
>
|
||||
Checking this option will favor the series endpoint with {'{{exampleParameter}}'} parameter over the
|
||||
label values endpoint with {'{{exampleParameter}}'} parameter. While the label values endpoint is
|
||||
considered more performant, some users may prefer the series because it has a POST method while the
|
||||
label values endpoint only has a GET method.
|
||||
</Trans>{' '}
|
||||
{docsTip()}
|
||||
</>
|
||||
}
|
||||
interactive={true}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ export { PrometheusDatasource, InstantQueryRefIdIndex } from './datasource';
|
|||
// The parts
|
||||
export { addLabelToQuery } from './add_label_to_query';
|
||||
export { type QueryEditorMode, type PromQueryFormat, type Prometheus } from './dataquery';
|
||||
export { loadResources } from './loadResources';
|
||||
export { PrometheusMetricFindQuery } from './metric_find_query';
|
||||
export { promqlGrammar } from './promql';
|
||||
export { getQueryHints, getInitHints } from './query_hints';
|
||||
|
|
|
|||
11
packages/grafana-prometheus/src/loadResources.ts
Normal file
11
packages/grafana-prometheus/src/loadResources.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { LANGUAGES, ResourceLoader, Resources } from '@grafana/i18n';
|
||||
|
||||
const resources = LANGUAGES.reduce<Record<string, () => Promise<{ default: Resources }>>>((acc, lang) => {
|
||||
acc[lang.code] = async () => await import(`./locales/${lang.code}/grafana-prometheus.json`);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
export const loadResources: ResourceLoader = async (resolvedLanguage: string) => {
|
||||
const translation = await resources[resolvedLanguage]();
|
||||
return translation.default;
|
||||
};
|
||||
|
|
@ -0,0 +1,338 @@
|
|||
{
|
||||
"components": {
|
||||
"annotation-query-editor": {
|
||||
"annotation-data-load-error": "Annotation data load error!",
|
||||
"aria-label-lower-limit-parameter": "Set lower limit for the step parameter",
|
||||
"label-min-step": "Min step",
|
||||
"label-series-value-as-timestamp": "Series value as timestamp",
|
||||
"label-tags": "Tags",
|
||||
"label-text": "Text",
|
||||
"label-title": "Title",
|
||||
"placeholder-auto": "auto",
|
||||
"tooltip-either-pattern-example-instance-replaced-label": "Use either the name or a pattern. For example, {{labelTemplate}} is replaced with label value for the label {{labelName}}.",
|
||||
"tooltip-min-step": "An additional lower limit for the step parameter of the Prometheus query and for the <2>{{intervalVar}}</2> and <4>{{rateIntervalVar}}</4> variables.",
|
||||
"tooltip-timestamp-milliseconds-series-value-seconds-multiply": "The unit of timestamp is milliseconds. If the unit of the series value is seconds, multiply its range vector by 1000."
|
||||
},
|
||||
"get-query-type-options": {
|
||||
"description": {
|
||||
"instant-query-range": "Run an Instant query and a Range query"
|
||||
},
|
||||
"label": {
|
||||
"both": "Both"
|
||||
},
|
||||
"range-options": {
|
||||
"description": {
|
||||
"query-range": "Run query over a range of time"
|
||||
},
|
||||
"label": {
|
||||
"instant": "Instant",
|
||||
"range": "Range"
|
||||
}
|
||||
}
|
||||
},
|
||||
"label-selector": {
|
||||
"aria-label-filter-expression-for-label": "Filter expression for label",
|
||||
"description-select-labels": "Once label values are selected, only possible label combinations are shown.",
|
||||
"select-labels-to-search-in": "2. Select labels to search in"
|
||||
},
|
||||
"metric-selector": {
|
||||
"aria-label-filter-expression-for-metric": "Filter expression for metric",
|
||||
"aria-label-limit-results-from-series-endpoint": "Limit results from series endpoint",
|
||||
"description-series-limit": "The limit applies to all metrics, labels, and values. Leave the field empty to use the default limit. Set to 0 to disable the limit and fetch everything — this may cause performance issues.",
|
||||
"label-select-metric": "Once a metric is selected only possible labels are shown. Labels are limited by the series limit below.",
|
||||
"select-a-metric": "1. Select a metric",
|
||||
"series-limit": "Series limit"
|
||||
},
|
||||
"prom-cheat-sheet": {
|
||||
"prom-ql-cheat-sheet": "PromQL Cheat Sheet"
|
||||
},
|
||||
"prom-exemplar-field": {
|
||||
"exemplars": "Exemplars",
|
||||
"tooltip-disable-query": "Disable query with exemplars",
|
||||
"tooltip-enable-query": "Enable query with exemplars"
|
||||
},
|
||||
"prom-explore-extra-field": {
|
||||
"aria-label-prometheus-extra-field": "Prometheus extra field",
|
||||
"aria-label-query-type-field": "Query type field",
|
||||
"aria-label-step-field": "Step field",
|
||||
"min-step": "Min step",
|
||||
"query-type": "Query type",
|
||||
"tooltip-units-builtin-variables-example-interval-rateinterval": "Time units and built-in variables can be used here, for example: {{example1}}, {{example2}}, {{example3}}, {{example4}}, {{example5}}, {{example6}}, {{example7}} (Default if no unit is specified: {{default}})"
|
||||
},
|
||||
"prom-query-field": {
|
||||
"placeholder-enter-a-prom-ql-query": "Enter a PromQL query…"
|
||||
},
|
||||
"prom-variable-query-editor": {
|
||||
"aria-label-classic-query": "Classic Query",
|
||||
"aria-label-metric-regex": "Metric regex",
|
||||
"aria-label-metric-selector": "Metric selector",
|
||||
"aria-label-prometheus-query": "Prometheus Query",
|
||||
"aria-label-query-type": "Query type",
|
||||
"aria-label-series-query": "Series Query",
|
||||
"label-classic-query": "Classic Query",
|
||||
"label-label": "Label",
|
||||
"label-metric-regex": "Metric regex",
|
||||
"label-query": "Query",
|
||||
"label-query-type": "Query type",
|
||||
"label-series-query": "Series Query",
|
||||
"placeholder-classic-query": "Classic Query",
|
||||
"placeholder-metric-regex": "Metric regex",
|
||||
"placeholder-prometheus-query": "Prometheus Query",
|
||||
"placeholder-select-query-type": "Select query type",
|
||||
"placeholder-series-query": "Series Query",
|
||||
"returns-metrics-matching-specified-metric-regex": "Returns a list of metrics matching the specified metric regex.",
|
||||
"tooltip-classic-query": "The original implemetation of the Prometheus variable query editor. Enter a string with the correct query type and parameters as described in these docs. For example, {{exampleQuery}}.",
|
||||
"tooltip-label": "Returns a list of label values for the label name in all metrics unless the metric is specified.",
|
||||
"tooltip-metric-regex": "Returns a list of label names, optionally filtering by specified metric regex.",
|
||||
"tooltip-query": "Returns a list of Prometheus query results for the query. This can include Prometheus functions, i.e.{{exampleQuery}}.",
|
||||
"tooltip-query-type": "The Prometheus data source plugin provides the following query types for template variables.",
|
||||
"tooltip-series-query": "Enter a metric with labels, only a metric or only labels, i.e.{{example1}}, {{example2}}, or {{example3}}. Returns a list of time series associated with the entered data."
|
||||
},
|
||||
"selector-actions": {
|
||||
"aria-label-selector": "selector",
|
||||
"aria-label-selector-clear-button": "Selector clear button",
|
||||
"aria-label-use-selector-as-metrics-button": "Use selector as metrics button",
|
||||
"aria-label-use-selector-for-query-button": "Use selector for query button",
|
||||
"aria-label-validate-submit-button": "Validate submit button",
|
||||
"clear": "Clear",
|
||||
"resulting-selector": "4. Resulting selector",
|
||||
"use-as-rate-query": "Use as rate query",
|
||||
"use-query": "Use query",
|
||||
"validate-selector": "Validate selector"
|
||||
},
|
||||
"value-selector": {
|
||||
"aria-label-filter-expression-for-label-values": "Filter expression for label values",
|
||||
"aria-label-values-for": "Values for {{labelKey}}",
|
||||
"description-search-field-values-across-selected-labels": "Use the search field to find values across selected labels.",
|
||||
"select-multiple-values-for-your-labels": "3. Select (multiple) values for your labels"
|
||||
}
|
||||
},
|
||||
"configuration": {
|
||||
"alerting-settings-overhaul": {
|
||||
"label-manage-alerts-via-alerting-ui": "Manage alerts via Alerting UI",
|
||||
"title-alerting": "Alerting",
|
||||
"tooltip-manage-alerts-via-alerting-ui": "Manage alert rules for this data source. To manage other alerting resources, add an Alertmanager data source."
|
||||
},
|
||||
"config-editor": {
|
||||
"browser-access-mode-error": "Browser access mode in the Prometheus data source is no longer available. Switch to server access mode.",
|
||||
"description-advanced-settings": "Additional settings are optional settings that can be configured for more control over your data source.",
|
||||
"title-advanced-settings": "Advanced settings",
|
||||
"title-error": "Error"
|
||||
},
|
||||
"data-source-http-settings-overhaul": {
|
||||
"tooltip-browser-access-mode": "Your access method is <1>Browser</1>, this means the URL needs to be accessible from the browser.",
|
||||
"tooltip-http-url": "Specify a complete HTTP URL (for example {{exampleURL}})",
|
||||
"tooltip-server-access-mode": "Your access method is <1>Server</1>, this means the URL needs to be accessible from the grafana backend/server."
|
||||
},
|
||||
"docs-tip": {
|
||||
"visit-docs-for-more-details-here": "Visit docs for more details here."
|
||||
},
|
||||
"exemplar-setting": {
|
||||
"label-data-source": "Data source",
|
||||
"label-internal-link": "Internal link",
|
||||
"label-label-name": "Label name",
|
||||
"label-remove-exemplar-link": "Remove exemplar link",
|
||||
"label-url": "URL",
|
||||
"label-url-label": "URL Label",
|
||||
"placeholder-go-to-examplecom": "Go to example.com",
|
||||
"placeholder-httpsexamplecomvalueraw": "https://example.com/${__value.raw}",
|
||||
"placeholder-trace-id": "traceID",
|
||||
"title-remove-exemplar-link": "Remove exemplar link",
|
||||
"tooltip-data-source": "The data source the exemplar is going to navigate to.",
|
||||
"tooltip-internal-link": "Enable this option if you have an internal link. When enabled, this reveals the data source selector. Select the backend tracing data store for your exemplar data.",
|
||||
"tooltip-label-name": "The name of the field in the labels object that should be used to get the traceID.",
|
||||
"tooltip-url": "The URL of the trace backend the user would go to see its trace",
|
||||
"tooltip-url-label": "Use to override the button label on the exemplar traceID field."
|
||||
},
|
||||
"exemplars-settings": {
|
||||
"add": "Add",
|
||||
"no-exemplars-configurations": "No exemplars configurations",
|
||||
"title-exemplars": "Exemplars"
|
||||
},
|
||||
"prom-settings": {
|
||||
"aria-label-default-editor": "Default Editor (Code or Builder)",
|
||||
"aria-label-prom-type-type": "{{promType}} type",
|
||||
"aria-label-prometheus-type": "Prometheus type",
|
||||
"aria-label-select-http-method": "Select HTTP method",
|
||||
"label-cache-level": "Cache level",
|
||||
"label-custom-query-parameters": "Custom query parameters",
|
||||
"label-default-editor": "Default editor",
|
||||
"label-disable-metrics-lookup": "Disable metrics lookup",
|
||||
"label-disable-recording-rules-beta": "Disable recording rules (beta)",
|
||||
"label-http-method": "HTTP method",
|
||||
"label-incremental-querying-beta": "Incremental querying (beta)",
|
||||
"label-metric-names-suggestion-limit": "Metric names suggestion limit",
|
||||
"label-prom-type-version": "{{promType}} version",
|
||||
"label-prometheus-type": "Prometheus type",
|
||||
"label-query-overlap-window": "Query overlap window",
|
||||
"label-query-timeout": "Query timeout",
|
||||
"label-scrape-interval": "Scrape interval",
|
||||
"label-use-series-endpoint": "Use series endpoint",
|
||||
"more-info": "For more information on configuring prometheus type and version in data sources, see the <2>provisioning documentation</2>.",
|
||||
"placeholder-example-maxsourceresolutionmtimeout": "Example: {{example}}",
|
||||
"title-interval-behaviour": "Interval behaviour",
|
||||
"title-other": "Other",
|
||||
"title-performance": "Performance",
|
||||
"title-query-editor": "Query editor",
|
||||
"tooltip-cache-level": "Sets the browser caching level for editor queries. Higher cache settings are recommended for high cardinality data sources.",
|
||||
"tooltip-custom-query-parameters": "Add custom parameters to the Prometheus query URL. For example {{example1}}, {{example2}}, {{example3}}, or{{example4}}. Multiple parameters should be concatenated together with {{concatenationChar}}.",
|
||||
"tooltip-default-editor": "Set default editor option for all users of this data source.",
|
||||
"tooltip-disable-metrics-lookup": "Checking this option will disable the metrics chooser and metric/label support in the query field's autocomplete. This helps if you have performance issues with bigger Prometheus instances. ",
|
||||
"tooltip-disable-recording-rules-beta": "This feature will disable recording rules. Turn this on to improve dashboard performance",
|
||||
"tooltip-http-method": "You can use either POST or GET HTTP method to query your Prometheus data source. POST is the recommended method as it allows bigger queries. Change this to GET if you have a Prometheus version older than 2.1 or if POST requests are restricted in your network.",
|
||||
"tooltip-incremental-querying-beta": "This feature will change the default behavior of relative queries to always request fresh data from the prometheus instance, instead query results will be cached, and only new records are requested. Turn this on to decrease database and network load.",
|
||||
"tooltip-metric-names-suggestion-limit": "The maximum number of metric names that may appear as autocomplete suggestions in the query editor's Code mode.",
|
||||
"tooltip-prom-type-version": "Use this to set the version of your {{promType}} instance if it is not automatically configured.",
|
||||
"tooltip-prometheus-type": "Set this to the type of your prometheus database, e.g. Prometheus, Cortex, Mimir or Thanos. Changing this field will save your current settings. Certain types of Prometheus supports or does not support various APIs. For example, some types support regex matching for label queries to improve performance. Some types have an API for metadata. If you set this incorrectly you may experience odd behavior when querying metrics and labels. Please check your Prometheus documentation to ensure you enter the correct type.",
|
||||
"tooltip-query-overlap-window": "Set a duration like {{example1}} or {{example2}} or {{example3}}. Default of {{default}}. This duration will be added to the duration of each incremental request.",
|
||||
"tooltip-query-timeout": "Set the Prometheus query timeout.",
|
||||
"tooltip-scrape-interval": "This interval is how frequently Prometheus scrapes targets. Set this to the typical scrape and evaluation interval configured in your Prometheus config file. If you set this to a greater value than your Prometheus config file interval, Grafana will evaluate the data according to this interval and you will see less data points. Defaults to {{default}}.",
|
||||
"tooltip-use-series-endpoint": "Checking this option will favor the series endpoint with {{exampleParameter}} parameter over the label values endpoint with {{exampleParameter}} parameter. While the label values endpoint is considered more performant, some users may prefer the series because it has a POST method while the label values endpoint only has a GET method."
|
||||
}
|
||||
},
|
||||
"querybuilder": {
|
||||
"additional-settings": {
|
||||
"content-filter-metric-names-regex-search-using": "Filter metric names by regex search, using an additional call on the Prometheus API.",
|
||||
"disable-text-wrap": "Disable text wrap"
|
||||
},
|
||||
"feedback-link": {
|
||||
"give-feedback": "Give feedback",
|
||||
"title-give-feedback": "The metrics explorer is new, please let us know how we can improve it"
|
||||
},
|
||||
"handle-function": {
|
||||
"text": {
|
||||
"query-parsing-is-ambiguous": "Query parsing is ambiguous."
|
||||
}
|
||||
},
|
||||
"label-filter-item": {
|
||||
"aria-label-remove": "Remove {{name}}",
|
||||
"placeholder-select-label": "Select label",
|
||||
"placeholder-select-value": "Select value"
|
||||
},
|
||||
"label-filters": {
|
||||
"label-filters": "Label filters",
|
||||
"label-label-filters": "Label filters",
|
||||
"tooltip-label-filters": "Optional: used to filter the metric select for this query type."
|
||||
},
|
||||
"metric-combobox": {
|
||||
"async-select": {
|
||||
"aria-label-open-metrics-explorer": "Open metrics explorer",
|
||||
"placeholder-select-metric": "Select metric",
|
||||
"tooltip-open-metrics-explorer": "Open metrics explorer"
|
||||
},
|
||||
"label-metric": "Metric",
|
||||
"tooltip-metric": "Optional: returns a list of label values for the label name in the specified metric."
|
||||
},
|
||||
"metrics-modal": {
|
||||
"additional-settings": "Additional Settings",
|
||||
"aria-label-additional-settings": "Additional settings",
|
||||
"aria-label-browse-metrics": "Browse metrics",
|
||||
"currently-selected": "Currently selected: {{selected}}",
|
||||
"metrics-pre-filtered": "These metrics have been pre-filtered by labels chosen in the label filters.",
|
||||
"placeholder-results-per-page": "results per page",
|
||||
"results-amount_one": "Showing {{num}} of {{count}} result",
|
||||
"results-amount_other": "Showing {{num}} of {{count}} results",
|
||||
"results-per-page": "Results per page",
|
||||
"title-metrics-explorer": "Metrics explorer"
|
||||
},
|
||||
"nested-query": {
|
||||
"label": {
|
||||
"ignoring": "Ignoring",
|
||||
"on": "On"
|
||||
},
|
||||
"operator": "Operator",
|
||||
"tooltip-remove-match": "Remove match",
|
||||
"vector-matches": "Vector matches"
|
||||
},
|
||||
"operation-editor": {
|
||||
"not-found": "Operation {{id}} not found",
|
||||
"title-remove": "Remove {{name}}"
|
||||
},
|
||||
"operation-header": {
|
||||
"placeholder-replace-with": "Replace with",
|
||||
"title-click-to-view-alternative-operations": "Click to view alternative operations",
|
||||
"title-remove-operation": "Remove operation"
|
||||
},
|
||||
"operation-info-button": {
|
||||
"title-click-to-show-description": "Click to show description",
|
||||
"title-remove-operation": "Remove operation"
|
||||
},
|
||||
"operation-list": {
|
||||
"operations": "Operations",
|
||||
"placeholder-search": "Search",
|
||||
"title-add-operation": "Add operation"
|
||||
},
|
||||
"operation-param-editor": {
|
||||
"title-add": "Add {{name}}",
|
||||
"title-remove": "Remove {{name}}"
|
||||
},
|
||||
"prom-query-builder-options": {
|
||||
"aria-label-lower-limit-parameter": "Set lower limit for the step parameter",
|
||||
"aria-label-select-resolution": "Select resolution",
|
||||
"label-exemplars": "Exemplars",
|
||||
"label-format": "Format",
|
||||
"label-min-step": "Min step",
|
||||
"label-resolution": "Resolution",
|
||||
"label-type": "Type",
|
||||
"placeholder-auto": "auto",
|
||||
"title-options": "Options",
|
||||
"tooltip-min-step": "An additional lower limit for the step parameter of the Prometheus query and for the <2>{{interval}}</2> and <4>{{rateInterval}}</4> variables."
|
||||
},
|
||||
"prom-query-code-editor-autocomplete-info": {
|
||||
"autocomplete-suggestions-limited": "Autocomplete suggestions limited",
|
||||
"tooltip-autocomplete-suggestions-limited": "The number of metric names exceeds the autocomplete limit. Only the {{autocompleteLimit}}-most relevant metrics are displayed. You can adjust the threshold in the data source settings."
|
||||
},
|
||||
"prom-query-editor-selector": {
|
||||
"kick-start-your-query": "Kick start your query",
|
||||
"label-explain": "Explain",
|
||||
"run-queries": "Run queries",
|
||||
"title-parsing-error-switch-builder": "Parsing error: Switch to the builder mode?"
|
||||
},
|
||||
"prom-query-legend-editor": {
|
||||
"label-legend": "Legend",
|
||||
"placeholder-select-legend-mode": "Select legend mode",
|
||||
"tooltip-legend": "Series name override or template. Ex. {{templateExample}} will be replaced with label value for {{labelName}}."
|
||||
},
|
||||
"query-builder-hints": {
|
||||
"hint-details": "hint: {{hintDetails}}"
|
||||
},
|
||||
"query-editor-hints": {
|
||||
"hint-details": "hint: {{hintDetails}}"
|
||||
},
|
||||
"query-pattern": {
|
||||
"apply-query": "Apply query",
|
||||
"aria-label-apply-query-starter-button": "apply query starter button",
|
||||
"aria-label-back-button": "back button",
|
||||
"aria-label-create-new-query-button": "create new query button",
|
||||
"aria-label-raw-query": "{{patternName}} raw query",
|
||||
"aria-label-use-this-query-button": "use this query button",
|
||||
"back": "Back",
|
||||
"create-new-query": "Create new query",
|
||||
"use-this-query": "Use this query"
|
||||
},
|
||||
"query-patterns-modal": {
|
||||
"aria-label-close-kick-start-your-query-modal": "close kick start your query modal",
|
||||
"aria-label-kick-start-your-query-modal": "Kick start your query modal",
|
||||
"aria-label-toggle-query-starter": "open and close {{patternType}} query starter card",
|
||||
"close": "Close",
|
||||
"description-kick-start-your-query": "Kick start your query by selecting one of these queries. You can then continue to complete your query.",
|
||||
"label-toggle-query-starter": "{{patternType}} query starters",
|
||||
"title-kick-start-your-query": "Kick start your query"
|
||||
},
|
||||
"raw-query": {
|
||||
"aria-label-selector": "selector"
|
||||
},
|
||||
"results-table": {
|
||||
"content-descriptive-type": "When creating a {{descriptiveType}}, Prometheus exposes multiple series with the type counter. ",
|
||||
"description": "Description",
|
||||
"name": "Name",
|
||||
"select": "Select",
|
||||
"type": "Type"
|
||||
},
|
||||
"update-function-args": {
|
||||
"text": {
|
||||
"query-parsing-is-ambiguous": "Query parsing is ambiguous."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
module.exports = {
|
||||
locales: ['en-US'], // Only en-US is updated - Crowdin will PR with other languages
|
||||
sort: true,
|
||||
createOldCatalogs: false,
|
||||
failOnWarnings: true,
|
||||
verbose: false,
|
||||
resetDefaultValueLocale: 'en-US', // Updates extracted values when they change in code
|
||||
|
||||
defaultNamespace: 'grafana-prometheus',
|
||||
input: ['../**/*.{tsx,ts}'],
|
||||
output: './src/locales/$LOCALE/$NAMESPACE.json',
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { Button, Card, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import promqlGrammar from '../promql';
|
||||
|
|
@ -31,7 +32,9 @@ export const QueryPattern = (props: Props) => {
|
|||
<Card.Heading>{pattern.name}</Card.Heading>
|
||||
<div className={styles.rawQueryContainer}>
|
||||
<RawQuery
|
||||
aria-label={`${pattern.name} raw query`}
|
||||
aria-label={t('querybuilder.query-pattern.aria-label-raw-query', '{{patternName}} raw query', {
|
||||
patternName: pattern.name,
|
||||
})}
|
||||
query={promQueryModeller.renderQuery({
|
||||
labels: [],
|
||||
operations: pattern.operations,
|
||||
|
|
@ -45,7 +48,7 @@ export const QueryPattern = (props: Props) => {
|
|||
{selectedPatternName !== pattern.name ? (
|
||||
<Button
|
||||
size="sm"
|
||||
aria-label="use this query button"
|
||||
aria-label={t('querybuilder.query-pattern.aria-label-use-this-query-button', 'use this query button')}
|
||||
onClick={() => {
|
||||
if (hasPreviousQuery) {
|
||||
// If user has previous query, we need to confirm that they want to apply this query pattern
|
||||
|
|
@ -55,7 +58,7 @@ export const QueryPattern = (props: Props) => {
|
|||
}
|
||||
}}
|
||||
>
|
||||
Use this query
|
||||
<Trans i18nKey="querybuilder.query-pattern.use-this-query">Use this query</Trans>
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
|
|
@ -66,27 +69,38 @@ export const QueryPattern = (props: Props) => {
|
|||
: 'this query pattern will be applied to your current query'
|
||||
}.`}
|
||||
</div>
|
||||
<Button size="sm" aria-label="back button" fill="outline" onClick={() => setSelectedPatternName(null)}>
|
||||
Back
|
||||
<Button
|
||||
size="sm"
|
||||
aria-label={t('querybuilder.query-pattern.aria-label-back-button', 'back button')}
|
||||
fill="outline"
|
||||
onClick={() => setSelectedPatternName(null)}
|
||||
>
|
||||
<Trans i18nKey="querybuilder.query-pattern.back">Back</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
aria-label="apply query starter button"
|
||||
aria-label={t(
|
||||
'querybuilder.query-pattern.aria-label-apply-query-starter-button',
|
||||
'apply query starter button'
|
||||
)}
|
||||
onClick={() => {
|
||||
onPatternSelect(pattern);
|
||||
}}
|
||||
>
|
||||
Apply query
|
||||
<Trans i18nKey="querybuilder.query-pattern.apply-query">Apply query</Trans>
|
||||
</Button>
|
||||
{hasNewQueryOption && (
|
||||
<Button
|
||||
size="sm"
|
||||
aria-label="create new query button"
|
||||
aria-label={t(
|
||||
'querybuilder.query-pattern.aria-label-create-new-query-button',
|
||||
'create new query button'
|
||||
)}
|
||||
onClick={() => {
|
||||
onPatternSelect(pattern, true);
|
||||
}}
|
||||
>
|
||||
Create new query
|
||||
<Trans i18nKey="querybuilder.query-pattern.create-new-query">Create new query</Trans>
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { capitalize } from 'lodash';
|
|||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { CoreApp, DataQuery, getNextRefId, GrafanaTheme2 } from '@grafana/data';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { Button, Collapse, Modal, useStyles2 } from '@grafana/ui';
|
||||
|
||||
|
|
@ -72,17 +73,33 @@ export const QueryPatternsModal = (props: Props) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Modal aria-label="Kick start your query modal" isOpen={isOpen} title="Kick start your query" onDismiss={onClose}>
|
||||
<Modal
|
||||
aria-label={t(
|
||||
'querybuilder.query-patterns-modal.aria-label-kick-start-your-query-modal',
|
||||
'Kick start your query modal'
|
||||
)}
|
||||
isOpen={isOpen}
|
||||
title={t('querybuilder.query-patterns-modal.title-kick-start-your-query', 'Kick start your query')}
|
||||
onDismiss={onClose}
|
||||
>
|
||||
<div className={styles.spacing}>
|
||||
Kick start your query by selecting one of these queries. You can then continue to complete your query.
|
||||
<Trans i18nKey="querybuilder.query-patterns-modal.description-kick-start-your-query">
|
||||
Kick start your query by selecting one of these queries. You can then continue to complete your query.
|
||||
</Trans>
|
||||
</div>
|
||||
{Object.values(PromQueryPatternType).map((patternType) => {
|
||||
const isOpen = openTabs.includes(patternType);
|
||||
return (
|
||||
<Collapse
|
||||
aria-label={`open and close ${patternType} query starter card`}
|
||||
aria-label={t(
|
||||
'querybuilder.query-patterns-modal.aria-label-toggle-query-starter',
|
||||
'open and close {{patternType}} query starter card',
|
||||
{ patternType }
|
||||
)}
|
||||
key={patternType}
|
||||
label={`${capitalize(patternType)} query starters`}
|
||||
label={t('querybuilder.query-patterns-modal.label-toggle-query-starter', '{{patternType}} query starters', {
|
||||
patternType: capitalize(patternType),
|
||||
})}
|
||||
isOpen={isOpen}
|
||||
collapsible={true}
|
||||
onToggle={() => {
|
||||
|
|
@ -117,8 +134,15 @@ export const QueryPatternsModal = (props: Props) => {
|
|||
</Collapse>
|
||||
);
|
||||
})}
|
||||
<Button aria-label="close kick start your query modal" variant="secondary" onClick={onClose}>
|
||||
Close
|
||||
<Button
|
||||
aria-label={t(
|
||||
'querybuilder.query-patterns-modal.aria-label-close-kick-start-your-query-modal',
|
||||
'close kick start your query modal'
|
||||
)}
|
||||
variant="secondary"
|
||||
onClick={onClose}
|
||||
>
|
||||
<Trans i18nKey="querybuilder.query-patterns-modal.close">Close</Trans>
|
||||
</Button>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useState } from 'react';
|
|||
|
||||
import { SelectableValue, toOption } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { AccessoryButton, InputGroup } from '@grafana/plugin-ui';
|
||||
import { AsyncSelect, Select } from '@grafana/ui';
|
||||
|
||||
|
|
@ -93,7 +94,7 @@ export function LabelFilterItem({
|
|||
<InputGroup>
|
||||
{/* Label name select, loads all values at once */}
|
||||
<AsyncSelect
|
||||
placeholder="Select label"
|
||||
placeholder={t('querybuilder.label-filter-item.placeholder-select-label', 'Select label')}
|
||||
data-testid={selectors.components.QueryBuilder.labelSelect}
|
||||
inputId="prometheus-dimensions-filter-item-key"
|
||||
width="auto"
|
||||
|
|
@ -150,7 +151,7 @@ export function LabelFilterItem({
|
|||
|
||||
{/* Label value async select: autocomplete calls prometheus API */}
|
||||
<AsyncSelect
|
||||
placeholder="Select value"
|
||||
placeholder={t('querybuilder.label-filter-item.placeholder-select-value', 'Select value')}
|
||||
data-testid={selectors.components.QueryBuilder.valueSelect}
|
||||
inputId="prometheus-dimensions-filter-item-value"
|
||||
width="auto"
|
||||
|
|
@ -201,7 +202,12 @@ export function LabelFilterItem({
|
|||
}}
|
||||
invalid={invalidValue}
|
||||
/>
|
||||
<AccessoryButton aria-label={`remove-${item.label}`} icon="times" variant="secondary" onClick={onDelete} />
|
||||
<AccessoryButton
|
||||
aria-label={t('querybuilder.label-filter-item.aria-label-remove', 'Remove {{name}}', { name: item.label })}
|
||||
icon="times"
|
||||
variant="secondary"
|
||||
onClick={onDelete}
|
||||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ describe('LabelFilters', () => {
|
|||
|
||||
it('removes label', async () => {
|
||||
const { onChange } = setup({ labelsFilters: [{ label: 'foo', op: '=', value: 'bar' }] });
|
||||
await userEvent.click(screen.getByLabelText(/remove-foo/));
|
||||
await userEvent.click(screen.getByLabelText(/Remove foo/));
|
||||
expect(onChange).toHaveBeenCalledWith([]);
|
||||
});
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ describe('LabelFilters', () => {
|
|||
{ label: 'le', op: '=', value: '' },
|
||||
],
|
||||
});
|
||||
await userEvent.click(screen.getByLabelText(/remove-foo/));
|
||||
await userEvent.click(screen.getByLabelText(/Remove foo/));
|
||||
expect(onChange).toHaveBeenCalledWith([
|
||||
{ label: 'lab', op: '=', value: 'bel' },
|
||||
{ label: 'le', op: '=', value: '' },
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { isEqual } from 'lodash';
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { EditorField, EditorFieldGroup, EditorList } from '@grafana/plugin-ui';
|
||||
import { InlineFieldRow, InlineLabel } from '@grafana/ui';
|
||||
|
||||
|
|
@ -94,9 +95,15 @@ export function LabelFilters({
|
|||
>
|
||||
<InlineLabel
|
||||
width={20}
|
||||
tooltip={<div>Optional: used to filter the metric select for this query type.</div>}
|
||||
tooltip={
|
||||
<div>
|
||||
<Trans i18nKey="querybuilder.label-filters.tooltip-label-filters">
|
||||
Optional: used to filter the metric select for this query type.
|
||||
</Trans>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
Label filters
|
||||
<Trans i18nKey="querybuilder.label-filters.label-filters">Label filters</Trans>
|
||||
</InlineLabel>
|
||||
{editorList()}
|
||||
</div>
|
||||
|
|
@ -104,7 +111,7 @@ export function LabelFilters({
|
|||
) : (
|
||||
<EditorFieldGroup>
|
||||
<EditorField
|
||||
label="Label filters"
|
||||
label={t('querybuilder.label-filters.label-label-filters', 'Label filters')}
|
||||
error={MISSING_LABEL_FILTER_ERROR_MESSAGE}
|
||||
invalid={labelFilterRequired && !hasLabelFilter}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ async function loadGroupByLabels(
|
|||
|
||||
// This function is used by both Prometheus and Loki and this the only difference.
|
||||
if (datasource.type === 'prometheus') {
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
labels = [{ label: '__name__', op: '=', value: query.metric }, ...query.labels];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { useCallback, useState } from 'react';
|
|||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { EditorField, EditorFieldGroup, InputGroup } from '@grafana/plugin-ui';
|
||||
import { Button, InlineField, InlineFieldRow, Combobox, ComboboxOption } from '@grafana/ui';
|
||||
|
||||
|
|
@ -87,7 +88,7 @@ export function MetricCombobox({
|
|||
return (
|
||||
<InputGroup>
|
||||
<Combobox
|
||||
placeholder="Select metric"
|
||||
placeholder={t('querybuilder.metric-combobox.async-select.placeholder-select-metric', 'Select metric')}
|
||||
width="auto"
|
||||
minWidth={25}
|
||||
options={loadOptions}
|
||||
|
|
@ -97,8 +98,14 @@ export function MetricCombobox({
|
|||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.builder.metricSelect}
|
||||
/>
|
||||
<Button
|
||||
tooltip="Open metrics explorer"
|
||||
aria-label="Open metrics explorer"
|
||||
tooltip={t(
|
||||
'querybuilder.metric-combobox.async-select.tooltip-open-metrics-explorer',
|
||||
'Open metrics explorer'
|
||||
)}
|
||||
aria-label={t(
|
||||
'querybuilder.metric-combobox.async-select.aria-label-open-metrics-explorer',
|
||||
'Open metrics explorer'
|
||||
)}
|
||||
variant="secondary"
|
||||
icon="book-open"
|
||||
onClick={() => {
|
||||
|
|
@ -125,16 +132,22 @@ export function MetricCombobox({
|
|||
{variableEditor ? (
|
||||
<InlineFieldRow>
|
||||
<InlineField
|
||||
label="Metric"
|
||||
label={t('querybuilder.metric-combobox.label-metric', 'Metric')}
|
||||
labelWidth={20}
|
||||
tooltip={<div>Optional: returns a list of label values for the label name in the specified metric.</div>}
|
||||
tooltip={
|
||||
<div>
|
||||
<Trans i18nKey="querybuilder.metric-combobox.tooltip-metric">
|
||||
Optional: returns a list of label values for the label name in the specified metric.
|
||||
</Trans>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{asyncSelect()}
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
) : (
|
||||
<EditorFieldGroup>
|
||||
<EditorField label="Metric">{asyncSelect()}</EditorField>
|
||||
<EditorField label={t('querybuilder.metric-combobox.label-metric', 'Metric')}>{asyncSelect()}</EditorField>
|
||||
</EditorFieldGroup>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ export function MetricsLabelsSection({
|
|||
}
|
||||
|
||||
const labelsToConsider = query.labels.filter((x) => x !== forLabel);
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
labelsToConsider.push({ label: '__name__', op: '=', value: query.metric });
|
||||
const expr = promQueryModeller.renderLabels(labelsToConsider);
|
||||
|
||||
|
|
@ -91,6 +92,7 @@ export function MetricsLabelsSection({
|
|||
const labelsToConsider = query.labels.filter((x) => x.label !== forLabel.label);
|
||||
labelsToConsider.push(forLabel);
|
||||
if (query.metric) {
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
labelsToConsider.push({ label: '__name__', op: '=', value: query.metric });
|
||||
}
|
||||
const interpolatedLabelsToConsider = labelsToConsider.map((labelObject) => ({
|
||||
|
|
@ -173,6 +175,7 @@ export function MetricsLabelsSection({
|
|||
}
|
||||
|
||||
const labelsToConsider = query.labels.filter((x) => x !== forLabel);
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
labelsToConsider.push({ label: '__name__', op: '=', value: query.metric });
|
||||
|
||||
const interpolatedLabelsToConsider = labelsToConsider.map((labelObject) => ({
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { css } from '@emotion/css';
|
|||
import { memo } from 'react';
|
||||
|
||||
import { GrafanaTheme2, toOption } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { EditorRows, FlexItem } from '@grafana/plugin-ui';
|
||||
import { AutoSizeInput, IconButton, Select, useStyles2 } from '@grafana/ui';
|
||||
|
||||
|
|
@ -29,7 +30,9 @@ export const NestedQuery = memo<NestedQueryProps>((props) => {
|
|||
return (
|
||||
<div className={styles.card}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.name}>Operator</div>
|
||||
<div className={styles.name}>
|
||||
<Trans i18nKey="querybuilder.nested-query.operator">Operator</Trans>
|
||||
</div>
|
||||
<Select
|
||||
width="auto"
|
||||
options={operators}
|
||||
|
|
@ -41,15 +44,17 @@ export const NestedQuery = memo<NestedQueryProps>((props) => {
|
|||
});
|
||||
}}
|
||||
/>
|
||||
<div className={styles.name}>Vector matches</div>
|
||||
<div className={styles.name}>
|
||||
<Trans i18nKey="querybuilder.nested-query.vector-matches">Vector matches</Trans>
|
||||
</div>
|
||||
<div className={styles.vectorMatchWrapper}>
|
||||
<Select<PromVisualQueryBinary['vectorMatchesType']>
|
||||
width="auto"
|
||||
value={nestedQuery.vectorMatchesType || 'on'}
|
||||
allowCustomValue
|
||||
options={[
|
||||
{ value: 'on', label: 'on' },
|
||||
{ value: 'ignoring', label: 'ignoring' },
|
||||
{ value: 'on', label: t('querybuilder.nested-query.label.on', 'On') },
|
||||
{ value: 'ignoring', label: t('querybuilder.nested-query.label.ignoring', 'Ignoring') },
|
||||
]}
|
||||
onChange={(val) => {
|
||||
onChange(index, {
|
||||
|
|
@ -72,7 +77,12 @@ export const NestedQuery = memo<NestedQueryProps>((props) => {
|
|||
/>
|
||||
</div>
|
||||
<FlexItem grow={1} />
|
||||
<IconButton name="times" size="sm" onClick={() => onRemove(index)} tooltip="Remove match" />
|
||||
<IconButton
|
||||
name="times"
|
||||
size="sm"
|
||||
onClick={() => onRemove(index)}
|
||||
tooltip={t('querybuilder.nested-query.tooltip-remove-match', 'Remove match')}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.body}>
|
||||
<EditorRows>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import * as React from 'react';
|
|||
|
||||
import { CoreApp, SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { EditorField, EditorRow, EditorSwitch } from '@grafana/plugin-ui';
|
||||
import { AutoSizeInput, RadioButtonGroup, Select } from '@grafana/ui';
|
||||
|
||||
|
|
@ -68,7 +69,7 @@ export const PromQueryBuilderOptions = React.memo<PromQueryBuilderOptionsProps>(
|
|||
<EditorRow>
|
||||
<div data-testid={selectors.components.DataSource.Prometheus.queryEditor.options}>
|
||||
<QueryOptionGroup
|
||||
title="Options"
|
||||
title={t('querybuilder.prom-query-builder-options.title-options', 'Options')}
|
||||
collapsedInfo={getCollapsedInfo(query, formatOption.label!, queryTypeLabel, app)}
|
||||
>
|
||||
<PromQueryLegendEditor
|
||||
|
|
@ -77,25 +78,36 @@ export const PromQueryBuilderOptions = React.memo<PromQueryBuilderOptionsProps>(
|
|||
onRunQuery={onRunQuery}
|
||||
/>
|
||||
<EditorField
|
||||
label="Min step"
|
||||
label={t('querybuilder.prom-query-builder-options.label-min-step', '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.
|
||||
<Trans
|
||||
i18nKey="querybuilder.prom-query-builder-options.tooltip-min-step"
|
||||
values={{
|
||||
interval: '$__interval',
|
||||
rateInterval: '$__rate_interval',
|
||||
}}
|
||||
>
|
||||
An additional lower limit for the step parameter of the Prometheus query and for the{' '}
|
||||
<code>{'{{interval}}'}</code> and <code>{'{{rateInterval}}'}</code> variables.
|
||||
</Trans>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<AutoSizeInput
|
||||
type="text"
|
||||
aria-label="Set lower limit for the step parameter"
|
||||
placeholder={'auto'}
|
||||
aria-label={t(
|
||||
'querybuilder.prom-query-builder-options.aria-label-lower-limit-parameter',
|
||||
'Set lower limit for the step parameter'
|
||||
)}
|
||||
placeholder={t('querybuilder.prom-query-builder-options.placeholder-auto', 'auto')}
|
||||
minWidth={10}
|
||||
onCommitChange={onChangeStep}
|
||||
defaultValue={query.interval}
|
||||
data-test-id="prometheus-step"
|
||||
/>
|
||||
</EditorField>
|
||||
<EditorField label="Format">
|
||||
<EditorField label={t('querybuilder.prom-query-builder-options.label-format', 'Format')}>
|
||||
<Select
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.format}
|
||||
value={formatOption}
|
||||
|
|
@ -104,11 +116,14 @@ export const PromQueryBuilderOptions = React.memo<PromQueryBuilderOptionsProps>(
|
|||
options={FORMAT_OPTIONS}
|
||||
/>
|
||||
</EditorField>
|
||||
<EditorField label="Type" data-testid={selectors.components.DataSource.Prometheus.queryEditor.type}>
|
||||
<EditorField
|
||||
label={t('querybuilder.prom-query-builder-options.label-type', 'Type')}
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.type}
|
||||
>
|
||||
<RadioButtonGroup options={queryTypeOptions} value={queryTypeValue} onChange={onQueryTypeChange} />
|
||||
</EditorField>
|
||||
{shouldShowExemplarSwitch(query, app) && (
|
||||
<EditorField label="Exemplars">
|
||||
<EditorField label={t('querybuilder.prom-query-builder-options.label-exemplars', 'Exemplars')}>
|
||||
<EditorSwitch
|
||||
value={query.exemplar || false}
|
||||
onChange={onExemplarChange}
|
||||
|
|
@ -117,9 +132,12 @@ export const PromQueryBuilderOptions = React.memo<PromQueryBuilderOptionsProps>(
|
|||
</EditorField>
|
||||
)}
|
||||
{query.intervalFactor && query.intervalFactor > 1 && (
|
||||
<EditorField label="Resolution">
|
||||
<EditorField label={t('querybuilder.prom-query-builder-options.label-resolution', 'Resolution')}>
|
||||
<Select
|
||||
aria-label="Select resolution"
|
||||
aria-label={t(
|
||||
'querybuilder.prom-query-builder-options.aria-label-select-resolution',
|
||||
'Select resolution'
|
||||
)}
|
||||
isSearchable={false}
|
||||
options={INTERVAL_FACTOR_OPTIONS}
|
||||
onChange={onIntervalFactorChange}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { IconButton, Text, Stack } from '@grafana/ui';
|
||||
|
||||
|
|
@ -57,11 +58,17 @@ export function PromQueryCodeEditorAutocompleteInfo(props: Readonly<Props>) {
|
|||
<div data-testid={selectors.components.DataSource.Prometheus.queryEditor.code.metricsCountInfo}>
|
||||
<Stack direction="row" gap={1}>
|
||||
<Text color="secondary" element="p" italic={true}>
|
||||
Autocomplete suggestions limited
|
||||
<Trans i18nKey="querybuilder.prom-query-code-editor-autocomplete-info.autocomplete-suggestions-limited">
|
||||
Autocomplete suggestions limited
|
||||
</Trans>
|
||||
</Text>
|
||||
<IconButton
|
||||
name="info-circle"
|
||||
tooltip={`The number of metric names exceeds the autocomplete limit. Only the ${autocompleteLimit}-most relevant metrics are displayed. You can adjust the threshold in the data source settings.`}
|
||||
tooltip={t(
|
||||
'querybuilder.prom-query-code-editor-autocomplete-info.tooltip-autocomplete-suggestions-limited',
|
||||
'The number of metric names exceeds the autocomplete limit. Only the {{autocompleteLimit}}-most relevant metrics are displayed. You can adjust the threshold in the data source settings.',
|
||||
{ autocompleteLimit }
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { memo, SyntheticEvent, useCallback, useEffect, useState } from 'react';
|
|||
|
||||
import { CoreApp, LoadingState, SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { EditorHeader, EditorRows, FlexItem } from '@grafana/plugin-ui';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { Button, ConfirmModal, Space } from '@grafana/ui';
|
||||
|
|
@ -105,7 +106,10 @@ export const PromQueryEditorSelector = memo<Props>((props) => {
|
|||
<>
|
||||
<ConfirmModal
|
||||
isOpen={parseModalOpen}
|
||||
title="Parsing error: Switch to the builder mode?"
|
||||
title={t(
|
||||
'querybuilder.prom-query-editor-selector.title-parsing-error-switch-builder',
|
||||
'Parsing error: Switch to the builder mode?'
|
||||
)}
|
||||
body="There is a syntax error, or the query structure cannot be visualized when switching to the builder mode. Parts of the query may be lost. "
|
||||
confirmText="Continue"
|
||||
onConfirm={() => {
|
||||
|
|
@ -130,10 +134,14 @@ export const PromQueryEditorSelector = memo<Props>((props) => {
|
|||
size="sm"
|
||||
onClick={handleOpenQueryPatternsModal}
|
||||
>
|
||||
Kick start your query
|
||||
<Trans i18nKey="querybuilder.prom-query-editor-selector.kick-start-your-query">Kick start your query</Trans>
|
||||
</Button>
|
||||
<div data-testid={selectors.components.DataSource.Prometheus.queryEditor.explain}>
|
||||
<QueryHeaderSwitch label="Explain" value={explain} onChange={onShowExplainChange} />
|
||||
<QueryHeaderSwitch
|
||||
label={t('querybuilder.prom-query-editor-selector.label-explain', 'Explain')}
|
||||
value={explain}
|
||||
onChange={onShowExplainChange}
|
||||
/>
|
||||
</div>
|
||||
<FlexItem grow={1} />
|
||||
{app !== CoreApp.Explore && app !== CoreApp.Correlations && (
|
||||
|
|
@ -144,7 +152,7 @@ export const PromQueryEditorSelector = memo<Props>((props) => {
|
|||
icon={data?.state === LoadingState.Loading ? 'spinner' : undefined}
|
||||
disabled={data?.state === LoadingState.Loading}
|
||||
>
|
||||
Run queries
|
||||
<Trans i18nKey="querybuilder.prom-query-editor-selector.run-queries">Run queries</Trans>
|
||||
</Button>
|
||||
)}
|
||||
<PromQueryCodeEditorAutocompleteInfo datasourceUid={props.datasource.uid} editorMode={editorMode} />
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import * as React from 'react';
|
|||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { EditorField } from '@grafana/plugin-ui';
|
||||
import { AutoSizeInput, Select } from '@grafana/ui';
|
||||
|
||||
|
|
@ -66,8 +67,12 @@ export const PromQueryLegendEditor = React.memo<PromQueryLegendEditorProps>(
|
|||
|
||||
return (
|
||||
<EditorField
|
||||
label="Legend"
|
||||
tooltip="Series name override or template. Ex. {{hostname}} will be replaced with label value for hostname."
|
||||
label={t('querybuilder.prom-query-legend-editor.label-legend', 'Legend')}
|
||||
tooltip={t(
|
||||
'querybuilder.prom-query-legend-editor.tooltip-legend',
|
||||
'Series name override or template. Ex. {{templateExample}} will be replaced with label value for {{labelName}}.',
|
||||
{ templateExample: '{{hostname}}', labelName: 'hostname' }
|
||||
)}
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.legend}
|
||||
>
|
||||
<>
|
||||
|
|
@ -75,6 +80,7 @@ export const PromQueryLegendEditor = React.memo<PromQueryLegendEditorProps>(
|
|||
<AutoSizeInput
|
||||
id="legendFormat"
|
||||
minWidth={22}
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
placeholder="auto"
|
||||
defaultValue={legendFormat}
|
||||
onCommitChange={onLegendFormatChanged}
|
||||
|
|
@ -85,7 +91,10 @@ export const PromQueryLegendEditor = React.memo<PromQueryLegendEditorProps>(
|
|||
<Select
|
||||
inputId="legend.mode"
|
||||
isSearchable={false}
|
||||
placeholder="Select legend mode"
|
||||
placeholder={t(
|
||||
'querybuilder.prom-query-legend-editor.placeholder-select-legend-mode',
|
||||
'Select legend mode'
|
||||
)}
|
||||
options={legendModeOptions}
|
||||
width={22}
|
||||
onChange={onLegendModeChanged}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { Icon, Switch, Tooltip, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { metricsModaltestIds } from './MetricsModal';
|
||||
|
|
@ -44,7 +45,9 @@ export function AdditionalSettings(props: AdditionalSettingsProps) {
|
|||
</div>
|
||||
<div className={styles.selectItem}>
|
||||
<Switch value={state.disableTextWrap} onChange={() => onChangeDisableTextWrap()} />
|
||||
<div className={styles.selectItemLabel}>Disable text wrap</div>
|
||||
<div className={styles.selectItemLabel}>
|
||||
<Trans i18nKey="querybuilder.additional-settings.disable-text-wrap">Disable text wrap</Trans>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.selectItem}>
|
||||
<Switch
|
||||
|
|
@ -54,7 +57,10 @@ export function AdditionalSettings(props: AdditionalSettingsProps) {
|
|||
/>
|
||||
<div className={styles.selectItemLabel}>{placeholders.setUseBackend} </div>
|
||||
<Tooltip
|
||||
content={'Filter metric names by regex search, using an additional call on the Prometheus API.'}
|
||||
content={t(
|
||||
'querybuilder.additional-settings.content-filter-metric-names-regex-search-using',
|
||||
'Filter metric names by regex search, using an additional call on the Prometheus API.'
|
||||
)}
|
||||
placement="bottom-end"
|
||||
>
|
||||
<Icon name="info-circle" size="xs" className={styles.settingsIcon} />
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { css } from '@emotion/css';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { Icon, useStyles2, Stack } from '@grafana/ui';
|
||||
|
||||
export interface Props {
|
||||
|
|
@ -16,11 +17,15 @@ export function FeedbackLink({ feedbackUrl }: Props) {
|
|||
<a
|
||||
href={feedbackUrl}
|
||||
className={styles.link}
|
||||
title="The metrics explorer is new, please let us know how we can improve it"
|
||||
title={t(
|
||||
'querybuilder.feedback-link.title-give-feedback',
|
||||
'The metrics explorer is new, please let us know how we can improve it'
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<Icon name="comment-alt-message" /> Give feedback
|
||||
<Icon name="comment-alt-message" />{' '}
|
||||
<Trans i18nKey="querybuilder.feedback-link.give-feedback">Give feedback</Trans>
|
||||
</a>
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { useCallback, useEffect, useMemo, useReducer } from 'react';
|
|||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
|
|
@ -187,9 +188,9 @@ export const MetricsModal = (props: MetricsModalProps) => {
|
|||
<Modal
|
||||
data-testid={metricsModaltestIds.metricModal}
|
||||
isOpen={isOpen}
|
||||
title="Metrics explorer"
|
||||
title={t('querybuilder.metrics-modal.title-metrics-explorer', 'Metrics explorer')}
|
||||
onDismiss={onClose}
|
||||
aria-label="Browse metrics"
|
||||
aria-label={t('querybuilder.metrics-modal.aria-label-browse-metrics', 'Browse metrics')}
|
||||
className={styles.modal}
|
||||
>
|
||||
<FeedbackLink feedbackUrl="https://forms.gle/DEMAJHoAMpe3e54CA" />
|
||||
|
|
@ -227,7 +228,7 @@ export const MetricsModal = (props: MetricsModalProps) => {
|
|||
</div>
|
||||
<div className={styles.inputItem}>
|
||||
<Toggletip
|
||||
aria-label="Additional settings"
|
||||
aria-label={t('querybuilder.metrics-modal.aria-label-additional-settings', 'Additional settings')}
|
||||
content={additionalSettings}
|
||||
placement="bottom-end"
|
||||
closeButton={false}
|
||||
|
|
@ -240,7 +241,7 @@ export const MetricsModal = (props: MetricsModalProps) => {
|
|||
data-testid={metricsModaltestIds.showAdditionalSettings}
|
||||
className={styles.noBorder}
|
||||
>
|
||||
Additional Settings
|
||||
<Trans i18nKey="querybuilder.metrics-modal.additional-settings">Additional Settings</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
className={styles.noBorder}
|
||||
|
|
@ -252,12 +253,21 @@ export const MetricsModal = (props: MetricsModalProps) => {
|
|||
</div>
|
||||
</div>
|
||||
<div className={styles.resultsData}>
|
||||
{query.metric && <i className={styles.currentlySelected}>Currently selected: {query.metric}</i>}
|
||||
{query.metric && (
|
||||
<i className={styles.currentlySelected}>
|
||||
<Trans i18nKey="querybuilder.metrics-modal.currently-selected" values={{ selected: query.metric }}>
|
||||
Currently selected: {'{{selected}}'}
|
||||
</Trans>
|
||||
</i>
|
||||
)}
|
||||
{query.labels.length > 0 && (
|
||||
<div className={styles.resultsDataFiltered}>
|
||||
<Icon name="info-circle" size="sm" />
|
||||
<div className={styles.resultsDataFilteredText}>
|
||||
These metrics have been pre-filtered by labels chosen in the label filters.
|
||||
|
||||
<Trans i18nKey="querybuilder.metrics-modal.metrics-pre-filtered">
|
||||
These metrics have been pre-filtered by labels chosen in the label filters.
|
||||
</Trans>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -276,7 +286,13 @@ export const MetricsModal = (props: MetricsModalProps) => {
|
|||
</div>
|
||||
<div className={styles.resultsFooter}>
|
||||
<div className={styles.resultsAmount}>
|
||||
Showing {state.filteredMetricCount} of {state.totalMetricCount} results
|
||||
<Trans
|
||||
i18nKey="querybuilder.metrics-modal.results-amount"
|
||||
values={{ num: state.filteredMetricCount }}
|
||||
count={state.totalMetricCount}
|
||||
>
|
||||
Showing {'{{num}}'} of {'{{count}}'} results
|
||||
</Trans>
|
||||
</div>
|
||||
<Pagination
|
||||
currentPage={state.pageNum ?? 1}
|
||||
|
|
@ -287,11 +303,13 @@ export const MetricsModal = (props: MetricsModalProps) => {
|
|||
}}
|
||||
/>
|
||||
<div className={styles.resultsPerPageWrapper}>
|
||||
<p className={styles.resultsPerPageLabel}># Results per page </p>
|
||||
<p className={styles.resultsPerPageLabel}>
|
||||
<Trans i18nKey="querybuilder.metrics-modal.results-per-page">Results per page</Trans>
|
||||
</p>
|
||||
<Input
|
||||
data-testid={metricsModaltestIds.resultsPerPage}
|
||||
value={calculateResultsPerPage(state.resultsPerPage, DEFAULT_RESULTS_PER_PAGE, MAXIMUM_RESULTS_PER_PAGE)}
|
||||
placeholder="results per page"
|
||||
placeholder={t('querybuilder.metrics-modal.placeholder-results-per-page', 'results per page')}
|
||||
width={10}
|
||||
title={'The maximum results per page is ' + MAXIMUM_RESULTS_PER_PAGE}
|
||||
type="number"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { ReactElement } from 'react';
|
|||
import Highlighter from 'react-highlight-words';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { Button, Icon, Tooltip, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { docsTip } from '../../../configuration/ConfigEditor';
|
||||
|
|
@ -69,7 +70,9 @@ export function ResultsTable(props: ResultsTableProps) {
|
|||
<Tooltip
|
||||
content={
|
||||
<>
|
||||
When creating a {descriptiveType}, Prometheus exposes multiple series with the type counter.{' '}
|
||||
<Trans i18nKey="querybuilder.results-table.content-descriptive-type">
|
||||
When creating a {{ descriptiveType }}, Prometheus exposes multiple series with the type counter.{' '}
|
||||
</Trans>
|
||||
{docsTip(link)}
|
||||
</>
|
||||
}
|
||||
|
|
@ -140,11 +143,17 @@ export function ResultsTable(props: ResultsTableProps) {
|
|||
<table className={styles.table}>
|
||||
<thead className={styles.stickyHeader}>
|
||||
<tr>
|
||||
<th className={`${styles.nameWidth} ${styles.tableHeaderPadding}`}>Name</th>
|
||||
<th className={`${styles.nameWidth} ${styles.tableHeaderPadding}`}>
|
||||
<Trans i18nKey="querybuilder.results-table.name">Name</Trans>
|
||||
</th>
|
||||
{state.hasMetadata && (
|
||||
<>
|
||||
<th className={`${styles.typeWidth} ${styles.tableHeaderPadding}`}>Type</th>
|
||||
<th className={`${styles.descriptionWidth} ${styles.tableHeaderPadding}`}>Description</th>
|
||||
<th className={`${styles.typeWidth} ${styles.tableHeaderPadding}`}>
|
||||
<Trans i18nKey="querybuilder.results-table.type">Type</Trans>
|
||||
</th>
|
||||
<th className={`${styles.descriptionWidth} ${styles.tableHeaderPadding}`}>
|
||||
<Trans i18nKey="querybuilder.results-table.description">Description</Trans>
|
||||
</th>
|
||||
</>
|
||||
)}
|
||||
<th className={styles.selectButtonWidth}> </th>
|
||||
|
|
@ -172,7 +181,7 @@ export function ResultsTable(props: ResultsTableProps) {
|
|||
onClick={() => selectMetric(metric)}
|
||||
className={styles.centerButton}
|
||||
>
|
||||
Select
|
||||
<Trans i18nKey="querybuilder.results-table.select">Select</Trans>
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ export function getOperationParamId(operationId: string, paramIndex: number) {
|
|||
}
|
||||
|
||||
export function getRangeVectorParamDef(withRateInterval = false): QueryBuilderOperationParamDef {
|
||||
/* eslint-disable @grafana/i18n/no-untranslated-strings */
|
||||
const options: Array<SelectableValue<string>> = [
|
||||
{
|
||||
label: '$__interval',
|
||||
|
|
@ -145,6 +146,7 @@ export function getRangeVectorParamDef(withRateInterval = false): QueryBuilderOp
|
|||
// tooltip: 'Always above 4x scrape interval',
|
||||
});
|
||||
}
|
||||
/* eslint-enable @grafana/i18n/no-untranslated-strings */
|
||||
|
||||
const param: QueryBuilderOperationParamDef = {
|
||||
name: 'Range',
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import {
|
|||
Without,
|
||||
} from '@prometheus-io/lezer-promql';
|
||||
|
||||
import { t } from '@grafana/i18n';
|
||||
|
||||
import { binaryScalarOperatorToOperatorName } from './binaryScalarOperations';
|
||||
import {
|
||||
ErrorId,
|
||||
|
|
@ -225,7 +227,7 @@ function handleFunction(expr: string, node: SyntaxNode, context: Context) {
|
|||
// Visual query builder doesn't support nested queries and so info function.
|
||||
if (funcName === 'info') {
|
||||
context.errors.push({
|
||||
text: 'Query parsing is ambiguous.',
|
||||
text: t('querybuilder.handle-function.text.query-parsing-is-ambiguous', 'Query parsing is ambiguous.'),
|
||||
from: node.from,
|
||||
to: node.to,
|
||||
});
|
||||
|
|
@ -329,7 +331,7 @@ function updateFunctionArgs(expr: string, node: SyntaxNode | null, context: Cont
|
|||
|
||||
if (binaryExpressionWithinFunctionArgs) {
|
||||
context.errors.push({
|
||||
text: 'Query parsing is ambiguous.',
|
||||
text: t('querybuilder.update-function-args.text.query-parsing-is-ambiguous', 'Query parsing is ambiguous.'),
|
||||
from: binaryExpressionWithinFunctionArgs.from,
|
||||
to: binaryExpressionWithinFunctionArgs.to,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { useEffect, useId, useState } from 'react';
|
|||
import * as React from 'react';
|
||||
|
||||
import { DataSourceApi, GrafanaTheme2, TimeRange } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { Button, Icon, Stack, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { getOperationParamId } from '../operationUtils';
|
||||
|
|
@ -52,7 +53,13 @@ export function OperationEditor({
|
|||
const id = useId();
|
||||
|
||||
if (!def) {
|
||||
return <span>Operation {operation.id} not found</span>;
|
||||
return (
|
||||
<span>
|
||||
<Trans i18nKey="querybuilder.operation-editor.not-found" values={{ id: operation.id }}>
|
||||
Operation {'{{id}}'} not found
|
||||
</Trans>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const onParamValueChanged = (paramIdx: number, value: QueryBuilderOperationParamValue) => {
|
||||
|
|
@ -113,7 +120,7 @@ export function OperationEditor({
|
|||
fill="text"
|
||||
icon="times"
|
||||
variant="secondary"
|
||||
title={`Remove ${paramDef.name}`}
|
||||
title={t('querybuilder.operation-editor.title-remove', 'Remove {{name}}', { name: paramDef.name })}
|
||||
onClick={() => onRemoveRestParam(paramIndex)}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { DraggableProvided } from '@hello-pangea/dnd';
|
|||
import { memo, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { FlexItem } from '@grafana/plugin-ui';
|
||||
import { Button, Select, useStyles2 } from '@grafana/ui';
|
||||
|
||||
|
|
@ -54,7 +55,10 @@ export const OperationHeader = memo<Props>(
|
|||
onClick={onToggleSwitcher}
|
||||
fill="text"
|
||||
variant="secondary"
|
||||
title="Click to view alternative operations"
|
||||
title={t(
|
||||
'querybuilder.operation-header.title-click-to-view-alternative-operations',
|
||||
'Click to view alternative operations'
|
||||
)}
|
||||
/>
|
||||
<OperationInfoButton def={def} operation={operation} />
|
||||
<Button
|
||||
|
|
@ -63,7 +67,7 @@ export const OperationHeader = memo<Props>(
|
|||
onClick={() => onRemove(index)}
|
||||
fill="text"
|
||||
variant="secondary"
|
||||
title="Remove operation"
|
||||
title={t('querybuilder.operation-header.title-remove-operation', 'Remove operation')}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
|
@ -73,7 +77,7 @@ export const OperationHeader = memo<Props>(
|
|||
<Select
|
||||
autoFocus
|
||||
openMenuOnFocus
|
||||
placeholder="Replace with"
|
||||
placeholder={t('querybuilder.operation-header.placeholder-replace-with', 'Replace with')}
|
||||
options={state.alternatives}
|
||||
isOpen={true}
|
||||
onCloseMenu={onToggleSwitcher}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
import { memo, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2, renderMarkdown } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { FlexItem } from '@grafana/plugin-ui';
|
||||
import { Button, Portal, useStyles2 } from '@grafana/ui';
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ export const OperationInfoButton = memo<Props>(({ def, operation }) => {
|
|||
return (
|
||||
<>
|
||||
<Button
|
||||
title="Click to show description"
|
||||
title={t('querybuilder.operation-info-button.title-click-to-show-description', 'Click to show description')}
|
||||
ref={refs.setReference}
|
||||
icon="info-circle"
|
||||
size="sm"
|
||||
|
|
@ -74,7 +75,7 @@ export const OperationInfoButton = memo<Props>(({ def, operation }) => {
|
|||
onClick={() => setShow(false)}
|
||||
fill="text"
|
||||
variant="secondary"
|
||||
title="Remove operation"
|
||||
title={t('querybuilder.operation-info-button.title-remove-operation', 'Remove operation')}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { useState } from 'react';
|
|||
import { useMountedState, usePrevious } from 'react-use';
|
||||
|
||||
import { DataSourceApi, GrafanaTheme2, TimeRange } from '@grafana/data';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { Button, Cascader, CascaderOption, useStyles2, Stack } from '@grafana/ui';
|
||||
|
||||
import { OperationEditor } from './OperationEditor';
|
||||
|
|
@ -126,11 +127,16 @@ export function OperationList<T extends QueryWithOperations>({
|
|||
autoFocus={true}
|
||||
alwaysOpen={true}
|
||||
hideActiveLevelLabel={true}
|
||||
placeholder={'Search'}
|
||||
placeholder={t('querybuilder.operation-list.placeholder-search', 'Search')}
|
||||
/>
|
||||
) : (
|
||||
<Button icon={'plus'} variant={'secondary'} onClick={() => setCascaderOpen(true)} title={'Add operation'}>
|
||||
Operations
|
||||
<Button
|
||||
icon={'plus'}
|
||||
variant={'secondary'}
|
||||
onClick={() => setCascaderOpen(true)}
|
||||
title={t('querybuilder.operation-list.title-add-operation', 'Add operation')}
|
||||
>
|
||||
<Trans i18nKey="querybuilder.operation-list.operations">Operations</Trans>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { css } from '@emotion/css';
|
|||
import { ComponentType } from 'react';
|
||||
|
||||
import { GrafanaTheme2, SelectableValue, toOption } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { AutoSizeInput, Button, Checkbox, Select, useStyles2, Stack } from '@grafana/ui';
|
||||
|
||||
import { getOperationParamId } from '../operationUtils';
|
||||
|
|
@ -86,7 +87,7 @@ function SelectInputParamEditor({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
title={`Add ${paramDef.name}`}
|
||||
title={t('querybuilder.operation-param-editor.title-add', 'Add {{name}}', { name: paramDef.name })}
|
||||
icon="plus"
|
||||
onClick={() => onChange(index, selectOptions[0].value)}
|
||||
>
|
||||
|
|
@ -114,7 +115,7 @@ function SelectInputParamEditor({
|
|||
fill="text"
|
||||
icon="times"
|
||||
variant="secondary"
|
||||
title={`Remove ${paramDef.name}`}
|
||||
title={t('querybuilder.operation-param-editor.title-remove', 'Remove {{name}}', { name: paramDef.name })}
|
||||
onClick={() => onChange(index, '')}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { css } from '@emotion/css';
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2, PanelData, QueryHint } from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { Button, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ export const QueryBuilderHints = <T extends PromLokiVisualQuery>({
|
|||
<div className={styles.container}>
|
||||
{hints.map((hint) => {
|
||||
return (
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
<Tooltip content={`${hint.label} ${hint.fix?.label}`} key={hint.type}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
|
|
@ -62,7 +64,12 @@ export const QueryBuilderHints = <T extends PromLokiVisualQuery>({
|
|||
size="sm"
|
||||
className={styles.hint}
|
||||
>
|
||||
hint: {hint.fix?.title || hint.fix?.action?.type.toLowerCase().replace('_', ' ')}
|
||||
<Trans
|
||||
i18nKey="querybuilder.query-builder-hints.hint-details"
|
||||
values={{ hintDetails: hint.fix?.title || hint.fix?.action?.type.toLowerCase().replace('_', ' ') }}
|
||||
>
|
||||
hint: {'{{hintDetails}}'}
|
||||
</Trans>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { css } from '@emotion/css';
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2, QueryHint } from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { Button, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
|
||||
|
|
@ -24,9 +25,15 @@ export function QueryEditorHints(props: PromQueryEditorProps) {
|
|||
<div className={styles.container}>
|
||||
{hints.map((hint) => {
|
||||
return (
|
||||
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
||||
<Tooltip content={`${hint.label} ${hint.fix?.label}`} key={hint.type}>
|
||||
<Button onClick={() => onHintButtonClick(hint, props)} fill="outline" size="sm" className={styles.hint}>
|
||||
hint: {hint.fix?.title || hint.fix?.action?.type.toLowerCase().replace('_', ' ')}
|
||||
<Trans
|
||||
i18nKey="querybuilder.query-editor-hints.hint-details"
|
||||
values={{ hintDetails: hint.fix?.title || hint.fix?.action?.type.toLowerCase().replace('_', ' ') }}
|
||||
>
|
||||
hint: {'{{hintDetails}}'}
|
||||
</Trans>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { css, cx } from '@emotion/css';
|
|||
import Prism, { Grammar } from 'prismjs';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { useTheme2 } from '@grafana/ui';
|
||||
|
||||
export interface Props {
|
||||
|
|
@ -22,7 +23,7 @@ export function RawQuery({ query, lang, className }: Props) {
|
|||
return (
|
||||
<div
|
||||
className={cx(styles.editorField, 'prism-syntax-highlight', className)}
|
||||
aria-label="selector"
|
||||
aria-label={t('querybuilder.raw-query.aria-label-selector', 'selector')}
|
||||
dangerouslySetInnerHTML={{ __html: highlighted }}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
"isolatedModules": true,
|
||||
"allowJs": true,
|
||||
"rootDirs": ["."],
|
||||
"resolveJsonModule": true
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "bundler"
|
||||
},
|
||||
"exclude": ["dist/**/*"],
|
||||
"extends": "@grafana/tsconfig",
|
||||
|
|
|
|||
|
|
@ -3400,6 +3400,7 @@ __metadata:
|
|||
"@floating-ui/react": "npm:0.27.12"
|
||||
"@grafana/data": "npm:12.1.0-pre"
|
||||
"@grafana/e2e-selectors": "npm:12.1.0-pre"
|
||||
"@grafana/i18n": "npm:12.1.0-pre"
|
||||
"@grafana/plugin-ui": "npm:0.10.6"
|
||||
"@grafana/runtime": "npm:12.1.0-pre"
|
||||
"@grafana/schema": "npm:12.1.0-pre"
|
||||
|
|
@ -3431,6 +3432,7 @@ __metadata:
|
|||
"@types/uuid": "npm:10.0.0"
|
||||
debounce-promise: "npm:3.1.2"
|
||||
esbuild: "npm:0.25.0"
|
||||
i18next-parser: "npm:9.3.0"
|
||||
jest: "npm:29.7.0"
|
||||
jest-environment-jsdom: "npm:29.7.0"
|
||||
lodash: "npm:4.17.21"
|
||||
|
|
|
|||
Loading…
Reference in a new issue