mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
VariableEditor: Use new form styles (#56326)
This commit is contained in:
@@ -5301,16 +5301,11 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
],
|
||||
"public/app/features/variables/editor/VariableEditorEditor.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/features/variables/editor/VariableSelectField.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/features/variables/editor/VariableTextAreaField.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/features/variables/editor/getVariableQueryEditor.test.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
|
@@ -10,7 +10,9 @@ describe('Variables - Constant', () => {
|
||||
// Create a new "Constant" variable
|
||||
e2e.components.CallToActionCard.buttonV2('Add variable').click();
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().type('Constant{enter}');
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().within(() => {
|
||||
e2e().get('input').type('Constant{enter}');
|
||||
});
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type('VariableUnderTest').blur();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2().type('Variable under test').blur();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.ConstantVariable.constantOptionsQueryInputV2().type('pesto').blur();
|
||||
|
@@ -3,7 +3,9 @@ import { e2e } from '@grafana/e2e';
|
||||
const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output';
|
||||
|
||||
function fillInCustomVariable(name: string, label: string, value: string) {
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().type('Custom{enter}');
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().within(() => {
|
||||
e2e().get('input').type('Custom{enter}');
|
||||
});
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type(name).blur();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2().type(label).blur();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput().type(value).blur();
|
||||
@@ -42,7 +44,9 @@ describe('Variables - Custom', () => {
|
||||
|
||||
// Create a new "Custom" variable
|
||||
e2e.components.CallToActionCard.buttonV2('Add variable').click();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().type('Custom{enter}');
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().within(() => {
|
||||
e2e().get('input').type('Custom{enter}');
|
||||
});
|
||||
|
||||
// Set its name, label, and content
|
||||
fillInCustomVariable('VariableUnderTest', 'Variable under test', 'One : 1,Two : 2, Three : 3');
|
||||
|
@@ -9,13 +9,17 @@ describe('Variables - Datasource', () => {
|
||||
|
||||
// Create a new "Datasource" variable
|
||||
e2e.components.CallToActionCard.buttonV2('Add variable').click();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().type('Data source{enter}');
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().within(() => {
|
||||
e2e().get('input').type('Data source{enter}');
|
||||
});
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type('VariableUnderTest').blur();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2().type('Variable under test').blur();
|
||||
|
||||
// If this is failing, but sure to check there are Prometheus datasources named "gdev-prometheus" and "gdev-slow-prometheus"
|
||||
// Or, just update is to match some gdev datasources to test with :)
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.DatasourceVariable.datasourceSelect().type('Prometheus{enter}');
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.DatasourceVariable.datasourceSelect().within(() => {
|
||||
e2e().get('input').type('Prometheus{enter}');
|
||||
});
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption()
|
||||
.eq(0)
|
||||
.should('have.text', 'gdev-prometheus');
|
||||
|
@@ -16,7 +16,9 @@ describe('Variables - Interval', () => {
|
||||
|
||||
// Create a new "Interval" variable
|
||||
e2e.components.CallToActionCard.buttonV2('Add variable').click();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().type('Interval{enter}');
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().within(() => {
|
||||
e2e().get('input').type('Interval{enter}');
|
||||
});
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type('VariableUnderTest').blur();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2().type('Variable under test').blur();
|
||||
|
||||
|
@@ -12,7 +12,7 @@ describe('Variables - Query - Add variable', () => {
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2()
|
||||
.should('be.visible')
|
||||
.within((input) => {
|
||||
expect(input.attr('placeholder')).equals('name');
|
||||
expect(input.attr('placeholder')).equals('Variable name');
|
||||
expect(input.val()).equals('query0');
|
||||
});
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2()
|
||||
@@ -23,21 +23,17 @@ describe('Variables - Query - Add variable', () => {
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2()
|
||||
.should('be.visible')
|
||||
.within((input) => {
|
||||
expect(input.attr('placeholder')).equals('optional display name');
|
||||
expect(input.attr('placeholder')).equals('Label name');
|
||||
expect(input.val()).equals('');
|
||||
});
|
||||
e2e()
|
||||
.get('#Description')
|
||||
.get('[placeholder="Descriptive text"]')
|
||||
.should('be.visible')
|
||||
.within((input) => {
|
||||
expect(input.attr('placeholder')).equals('descriptive text');
|
||||
expect(input.attr('placeholder')).equals('Descriptive text');
|
||||
expect(input.val()).equals('');
|
||||
});
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalHideSelectV2()
|
||||
.should('be.visible')
|
||||
.within((select) => {
|
||||
e2e.components.Select.singleValue().should('have.text', '');
|
||||
});
|
||||
e2e().get('label').contains('Show on dashboard').should('be.visible');
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsDataSourceSelect()
|
||||
.should('be.visible')
|
||||
@@ -45,11 +41,9 @@ describe('Variables - Query - Add variable', () => {
|
||||
e2e.components.Select.singleValue().should('have.text', 'gdev-testdata');
|
||||
});
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRefreshSelectV2()
|
||||
.should('be.visible')
|
||||
.within((select) => {
|
||||
e2e.components.Select.singleValue().should('have.text', 'On dashboard load');
|
||||
});
|
||||
e2e().get('label').contains('Refresh').scrollIntoView().should('be.visible');
|
||||
e2e().get('label').contains('On dashboard load').scrollIntoView().should('be.visible');
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2()
|
||||
.should('be.visible')
|
||||
.within((input) => {
|
||||
@@ -62,8 +56,18 @@ describe('Variables - Query - Add variable', () => {
|
||||
.within((select) => {
|
||||
e2e.components.Select.singleValue().should('have.text', 'Disabled');
|
||||
});
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsMultiSwitch().should('not.be.checked');
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch().should('not.be.checked');
|
||||
|
||||
e2e()
|
||||
.contains('label', 'Multi-value')
|
||||
.within(() => {
|
||||
e2e().get('input[type="checkbox"]').should('not.be.checked');
|
||||
});
|
||||
|
||||
e2e()
|
||||
.contains('label', 'Include All option')
|
||||
.within(() => {
|
||||
e2e().get('input[type="checkbox"]').should('not.be.checked');
|
||||
});
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should('not.exist');
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInputV2().should('not.exist');
|
||||
@@ -80,9 +84,9 @@ describe('Variables - Query - Add variable', () => {
|
||||
.clear()
|
||||
.type('a label');
|
||||
|
||||
e2e().get('#Description').should('be.visible').clear().type('a description');
|
||||
e2e().get('[placeholder="Descriptive text"]').should('be.visible').clear().type('a description');
|
||||
|
||||
e2e.components.DataSourcePicker.container().should('be.visible').type('gdev-testdata{enter}');
|
||||
e2e.components.DataSourcePicker.inputV2().should('be.visible').type('gdev-testdata{enter}');
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput()
|
||||
.should('be.visible')
|
||||
@@ -96,7 +100,7 @@ describe('Variables - Query - Add variable', () => {
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should('exist');
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.submitButton().should('be.visible').click();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.submitButton().scrollIntoView().should('be.visible').click();
|
||||
|
||||
e2e().wait(1500);
|
||||
|
||||
@@ -130,9 +134,9 @@ describe('Variables - Query - Add variable', () => {
|
||||
.clear()
|
||||
.type('a label');
|
||||
|
||||
e2e().get('#Description').should('be.visible').clear().type('a description');
|
||||
e2e().get('[placeholder="Descriptive text"]').should('be.visible').clear().type('a description');
|
||||
|
||||
e2e.components.DataSourcePicker.container().type('gdev-testdata{enter}');
|
||||
e2e.components.DataSourcePicker.inputV2().type('gdev-testdata{enter}');
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput()
|
||||
.should('be.visible')
|
||||
@@ -144,13 +148,17 @@ describe('Variables - Query - Add variable', () => {
|
||||
.type('/.*C.*/')
|
||||
.blur();
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsMultiSwitch()
|
||||
.click({ force: true })
|
||||
.should('be.checked');
|
||||
e2e()
|
||||
.contains('label', 'Multi-value')
|
||||
.within(() => {
|
||||
e2e().get('input[type="checkbox"]').click({ force: true }).should('be.checked');
|
||||
});
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch()
|
||||
.click({ force: true })
|
||||
.should('be.checked');
|
||||
e2e()
|
||||
.contains('label', 'Include All option')
|
||||
.within(() => {
|
||||
e2e().get('input[type="checkbox"]').click({ force: true }).should('be.checked');
|
||||
});
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInputV2().within((input) => {
|
||||
expect(input.attr('placeholder')).equals('blank = auto');
|
||||
@@ -159,7 +167,7 @@ describe('Variables - Query - Add variable', () => {
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should('exist');
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.submitButton().should('be.visible').click();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.submitButton().scrollIntoView().should('be.visible').click();
|
||||
|
||||
e2e().wait(500);
|
||||
|
||||
|
@@ -10,7 +10,9 @@ describe('Variables - Text box', () => {
|
||||
// Create a new "text box" variable
|
||||
e2e.components.CallToActionCard.buttonV2('Add variable').click();
|
||||
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().type('Text box{enter}');
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().within(() => {
|
||||
e2e().get('input').type('Text box{enter}');
|
||||
});
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type('VariableUnderTest').blur();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2().type('Variable under test').blur();
|
||||
e2e.pages.Dashboard.Settings.Variables.Edit.TextBoxVariable.textBoxOptionsQueryInputV2().type('cat-dog').blur();
|
||||
|
@@ -3,10 +3,10 @@ import { connect, ConnectedProps } from 'react-redux';
|
||||
|
||||
import { DataSourceInstanceSettings, getDataSourceRef } from '@grafana/data';
|
||||
import { DataSourcePicker } from '@grafana/runtime';
|
||||
import { Alert, InlineField, InlineFieldRow, VerticalGroup } from '@grafana/ui';
|
||||
import { Alert, Field } from '@grafana/ui';
|
||||
import { StoreState } from 'app/types';
|
||||
|
||||
import { VariableSectionHeader } from '../editor/VariableSectionHeader';
|
||||
import { VariableLegend } from '../editor/VariableLegend';
|
||||
import { initialVariableEditorState } from '../editor/reducer';
|
||||
import { getAdhocVariableEditorState } from '../editor/selectors';
|
||||
import { VariableEditorProps } from '../editor/types';
|
||||
@@ -61,18 +61,14 @@ export class AdHocVariableEditorUnConnected extends PureComponent<Props> {
|
||||
const infoText = extended?.infoText ?? null;
|
||||
|
||||
return (
|
||||
<VerticalGroup spacing="xs">
|
||||
<VariableSectionHeader name="Options" />
|
||||
<VerticalGroup spacing="sm">
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Data source" labelWidth={20} htmlFor="data-source-picker">
|
||||
<DataSourcePicker current={variable.datasource} onChange={this.onDatasourceChanged} noDefault />
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
<>
|
||||
<VariableLegend>Ad-hoc options</VariableLegend>
|
||||
<Field label="Data source" htmlFor="data-source-picker">
|
||||
<DataSourcePicker current={variable.datasource} onChange={this.onDatasourceChanged} width={30} noDefault />
|
||||
</Field>
|
||||
|
||||
{infoText ? <Alert title={infoText} severity="info" /> : null}
|
||||
</VerticalGroup>
|
||||
</VerticalGroup>
|
||||
{infoText ? <Alert title={infoText} severity="info" /> : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import React, { FormEvent, PureComponent } from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { VerticalGroup } from '@grafana/ui';
|
||||
|
||||
import { VariableSectionHeader } from '../editor/VariableSectionHeader';
|
||||
import { VariableLegend } from '../editor/VariableLegend';
|
||||
import { VariableTextField } from '../editor/VariableTextField';
|
||||
import { VariableEditorProps } from '../editor/types';
|
||||
import { ConstantVariableModel } from '../types';
|
||||
@@ -28,19 +27,18 @@ export class ConstantVariableEditor extends PureComponent<Props> {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<VerticalGroup spacing="xs">
|
||||
<VariableSectionHeader name="Constant options" />
|
||||
<>
|
||||
<VariableLegend>Constant options</VariableLegend>
|
||||
<VariableTextField
|
||||
value={this.props.variable.query}
|
||||
name="Value"
|
||||
placeholder="your metric prefix"
|
||||
onChange={this.onChange}
|
||||
onBlur={this.onBlur}
|
||||
labelWidth={20}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.ConstantVariable.constantOptionsQueryInputV2}
|
||||
grow
|
||||
width={30}
|
||||
/>
|
||||
</VerticalGroup>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -2,12 +2,11 @@ import React, { FormEvent, PureComponent } from 'react';
|
||||
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { VerticalGroup } from '@grafana/ui';
|
||||
import { connectWithStore } from 'app/core/utils/connectWithReduxStore';
|
||||
import { StoreState } from 'app/types';
|
||||
|
||||
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
|
||||
import { VariableSectionHeader } from '../editor/VariableSectionHeader';
|
||||
import { VariableLegend } from '../editor/VariableLegend';
|
||||
import { VariableTextAreaField } from '../editor/VariableTextAreaField';
|
||||
import { OnPropChangeArguments, VariableEditorProps } from '../editor/types';
|
||||
import { changeVariableMultiValue } from '../state/actions';
|
||||
@@ -45,29 +44,26 @@ class CustomVariableEditorUnconnected extends PureComponent<Props> {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<VerticalGroup spacing="xs">
|
||||
<VariableSectionHeader name="Custom options" />
|
||||
<VerticalGroup spacing="md">
|
||||
<VerticalGroup spacing="none">
|
||||
<VariableTextAreaField
|
||||
name="Values separated by comma"
|
||||
value={this.props.variable.query}
|
||||
placeholder="1, 10, mykey : myvalue, myvalue, escaped\,value"
|
||||
onChange={this.onChange}
|
||||
onBlur={this.onBlur}
|
||||
required
|
||||
width={50}
|
||||
labelWidth={27}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput}
|
||||
/>
|
||||
</VerticalGroup>
|
||||
<SelectionOptionsEditor
|
||||
variable={this.props.variable}
|
||||
onPropChange={this.onSelectionOptionsChange}
|
||||
onMultiChanged={this.props.changeVariableMultiValue}
|
||||
/>{' '}
|
||||
</VerticalGroup>
|
||||
</VerticalGroup>
|
||||
<>
|
||||
<VariableLegend>Custom options</VariableLegend>
|
||||
|
||||
<VariableTextAreaField
|
||||
name="Values separated by comma"
|
||||
value={this.props.variable.query}
|
||||
placeholder="1, 10, mykey : myvalue, myvalue, escaped\,value"
|
||||
onChange={this.onChange}
|
||||
onBlur={this.onBlur}
|
||||
required
|
||||
width={52}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput}
|
||||
/>
|
||||
<VariableLegend>Selection options</VariableLegend>
|
||||
<SelectionOptionsEditor
|
||||
variable={this.props.variable}
|
||||
onPropChange={this.onSelectionOptionsChange}
|
||||
onMultiChanged={this.props.changeVariableMultiValue}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -41,13 +41,13 @@ describe('DataSourceVariableEditor', () => {
|
||||
|
||||
it('has a regex filter field', () => {
|
||||
render(<DataSourceVariableEditor {...props} />);
|
||||
const field = screen.getByLabelText('Instance name filter');
|
||||
const field = screen.getByLabelText(/Instance name filter/);
|
||||
expect(field).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls the handler when the regex filter is changed', () => {
|
||||
render(<DataSourceVariableEditor {...props} />);
|
||||
const field = screen.getByLabelText('Instance name filter');
|
||||
const field = screen.getByLabelText(/Instance name filter/);
|
||||
fireEvent.change(field, { target: { value: '/prod/' } });
|
||||
expect(props.onPropChange).toBeCalledWith({ propName: 'regex', propValue: '/prod/' });
|
||||
});
|
||||
|
@@ -3,11 +3,10 @@ import { connect, ConnectedProps } from 'react-redux';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { InlineFieldRow, VerticalGroup } from '@grafana/ui';
|
||||
|
||||
import { StoreState } from '../../../types';
|
||||
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
|
||||
import { VariableSectionHeader } from '../editor/VariableSectionHeader';
|
||||
import { VariableLegend } from '../editor/VariableLegend';
|
||||
import { VariableSelectField } from '../editor/VariableSelectField';
|
||||
import { VariableTextField } from '../editor/VariableTextField';
|
||||
import { initialVariableEditorState } from '../editor/reducer';
|
||||
@@ -103,48 +102,40 @@ export class DataSourceVariableEditorUnConnected extends PureComponent<Props> {
|
||||
const typeValue = typeOptions.find((o) => o.value === variable.query) ?? typeOptions[0];
|
||||
|
||||
return (
|
||||
<VerticalGroup spacing="xs">
|
||||
<VariableSectionHeader name="Data source options" />
|
||||
<VerticalGroup spacing="md">
|
||||
<VerticalGroup spacing="xs">
|
||||
<InlineFieldRow>
|
||||
<VariableSelectField
|
||||
name="Type"
|
||||
value={typeValue}
|
||||
options={typeOptions}
|
||||
onChange={this.onDataSourceTypeChanged}
|
||||
labelWidth={10}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.DatasourceVariable.datasourceSelect}
|
||||
/>
|
||||
</InlineFieldRow>
|
||||
<InlineFieldRow>
|
||||
<VariableTextField
|
||||
value={this.props.variable.regex}
|
||||
name="Instance name filter"
|
||||
placeholder="/.*-(.*)-.*/"
|
||||
onChange={this.onRegExChange}
|
||||
onBlur={this.onRegExBlur}
|
||||
labelWidth={20}
|
||||
tooltip={
|
||||
<div>
|
||||
Regex filter for which data source instances to choose from in the variable value list. Leave empty
|
||||
for all.
|
||||
<br />
|
||||
<br />
|
||||
Example: <code>/^prod/</code>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</InlineFieldRow>
|
||||
</VerticalGroup>
|
||||
<>
|
||||
<VariableLegend>Data source options</VariableLegend>
|
||||
<VariableSelectField
|
||||
name="Type"
|
||||
value={typeValue}
|
||||
options={typeOptions}
|
||||
onChange={this.onDataSourceTypeChanged}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.DatasourceVariable.datasourceSelect}
|
||||
/>
|
||||
|
||||
<SelectionOptionsEditor
|
||||
variable={variable}
|
||||
onPropChange={this.onSelectionOptionsChange}
|
||||
onMultiChanged={changeVariableMultiValue}
|
||||
/>
|
||||
</VerticalGroup>
|
||||
</VerticalGroup>
|
||||
<VariableTextField
|
||||
value={this.props.variable.regex}
|
||||
name="Instance name filter"
|
||||
placeholder="/.*-(.*)-.*/"
|
||||
onChange={this.onRegExChange}
|
||||
onBlur={this.onRegExBlur}
|
||||
description={
|
||||
<div>
|
||||
Regex filter for which data source instances to choose from in the variable value list. Leave empty for
|
||||
all.
|
||||
<br />
|
||||
<br />
|
||||
Example: <code>/^prod/</code>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<VariableLegend>Selection options</VariableLegend>
|
||||
<SelectionOptionsEditor
|
||||
variable={variable}
|
||||
onPropChange={this.onSelectionOptionsChange}
|
||||
onMultiChanged={changeVariableMultiValue}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@ import { dataSourceVariableReducer, initialDataSourceVariableModelState } from '
|
||||
export const createDataSourceVariableAdapter = (): VariableAdapter<DataSourceVariableModel> => {
|
||||
return {
|
||||
id: 'datasource',
|
||||
description: 'Enabled you to dynamically switch the data source for multiple panels.',
|
||||
description: 'Enables you to dynamically switch the data source for multiple panels.',
|
||||
name: 'Data source',
|
||||
initialState: initialDataSourceVariableModelState,
|
||||
reducer: dataSourceVariableReducer,
|
||||
|
@@ -1,18 +1,17 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useId } from '@react-aria/utils';
|
||||
import React, { FC, useCallback, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { useStyles } from '@grafana/ui';
|
||||
import { TextArea, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { VariableQueryEditorProps } from '../types';
|
||||
|
||||
import { VariableTextAreaField } from './VariableTextAreaField';
|
||||
import { getStyles } from './VariableTextAreaField';
|
||||
|
||||
export const LEGACY_VARIABLE_QUERY_EDITOR_NAME = 'Grafana-LegacyVariableQueryEditor';
|
||||
|
||||
export const LegacyVariableQueryEditor: FC<VariableQueryEditorProps> = ({ onChange, query }) => {
|
||||
const styles = useStyles(getStyles);
|
||||
const styles = useStyles2(getStyles);
|
||||
const [value, setValue] = useState(query);
|
||||
const onValueChange = (event: React.FormEvent<HTMLTextAreaElement>) => {
|
||||
setValue(event.currentTarget.value);
|
||||
@@ -25,29 +24,22 @@ export const LegacyVariableQueryEditor: FC<VariableQueryEditorProps> = ({ onChan
|
||||
[onChange]
|
||||
);
|
||||
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<VariableTextAreaField
|
||||
name="Query"
|
||||
value={value}
|
||||
placeholder="metric name or tags query"
|
||||
width={100}
|
||||
onChange={onValueChange}
|
||||
onBlur={onBlur}
|
||||
required
|
||||
labelWidth={20}
|
||||
ariaLabel={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput}
|
||||
/>
|
||||
</div>
|
||||
<TextArea
|
||||
id={id}
|
||||
rows={2}
|
||||
value={value}
|
||||
onChange={onValueChange}
|
||||
onBlur={onBlur}
|
||||
placeholder="Metric name or tags query"
|
||||
required
|
||||
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput}
|
||||
cols={52}
|
||||
className={styles.textarea}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
function getStyles(theme: GrafanaTheme) {
|
||||
return {
|
||||
container: css`
|
||||
margin-bottom: ${theme.spacing.xs};
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
LegacyVariableQueryEditor.displayName = LEGACY_VARIABLE_QUERY_EDITOR_NAME;
|
||||
|
@@ -1,14 +1,13 @@
|
||||
import React, { ChangeEvent, FormEvent, FunctionComponent, useCallback } from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { InlineFieldRow, VerticalGroup } from '@grafana/ui';
|
||||
import { VerticalGroup } from '@grafana/ui';
|
||||
|
||||
import { KeyedVariableIdentifier } from '../state/types';
|
||||
import { VariableWithMultiSupport } from '../types';
|
||||
import { toKeyedVariableIdentifier } from '../utils';
|
||||
|
||||
import { VariableSectionHeader } from './VariableSectionHeader';
|
||||
import { VariableSwitchField } from './VariableSwitchField';
|
||||
import { VariableCheckboxField } from './VariableCheckboxField';
|
||||
import { VariableTextField } from './VariableTextField';
|
||||
import { VariableEditorProps } from './types';
|
||||
|
||||
@@ -44,37 +43,27 @@ export const SelectionOptionsEditor: FunctionComponent<SelectionOptionsEditorPro
|
||||
);
|
||||
|
||||
return (
|
||||
<VerticalGroup spacing="none">
|
||||
<VariableSectionHeader name="Selection options" />
|
||||
<InlineFieldRow>
|
||||
<VariableSwitchField
|
||||
value={variable.multi}
|
||||
name="Multi-value"
|
||||
tooltip="Enables multiple values to be selected at the same time"
|
||||
onChange={onMultiChanged}
|
||||
ariaLabel={selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsMultiSwitch}
|
||||
/>
|
||||
</InlineFieldRow>
|
||||
<InlineFieldRow>
|
||||
<VariableSwitchField
|
||||
value={variable.includeAll}
|
||||
name="Include All option"
|
||||
tooltip="Enables an option to include all variables"
|
||||
onChange={onIncludeAllChanged}
|
||||
ariaLabel={selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsIncludeAllSwitch}
|
||||
/>
|
||||
</InlineFieldRow>
|
||||
<VerticalGroup spacing="md" height="inherit">
|
||||
<VariableCheckboxField
|
||||
value={variable.multi}
|
||||
name="Multi-value"
|
||||
description="Enables multiple values to be selected at the same time"
|
||||
onChange={onMultiChanged}
|
||||
/>
|
||||
<VariableCheckboxField
|
||||
value={variable.includeAll}
|
||||
name="Include All option"
|
||||
description="Enables an option to include all variables"
|
||||
onChange={onIncludeAllChanged}
|
||||
/>
|
||||
{variable.includeAll && (
|
||||
<InlineFieldRow>
|
||||
<VariableTextField
|
||||
value={variable.allValue ?? ''}
|
||||
onChange={onAllValueChanged}
|
||||
name="Custom all value"
|
||||
placeholder="blank = auto"
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInputV2}
|
||||
labelWidth={20}
|
||||
/>
|
||||
</InlineFieldRow>
|
||||
<VariableTextField
|
||||
value={variable.allValue ?? ''}
|
||||
onChange={onAllValueChanged}
|
||||
name="Custom all value"
|
||||
placeholder="blank = auto"
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInputV2}
|
||||
/>
|
||||
)}
|
||||
</VerticalGroup>
|
||||
);
|
||||
|
@@ -0,0 +1,33 @@
|
||||
import { useId } from '@react-aria/utils';
|
||||
import React, { ChangeEvent, PropsWithChildren, ReactElement } from 'react';
|
||||
|
||||
import { Checkbox } from '@grafana/ui';
|
||||
|
||||
interface VariableCheckboxFieldProps {
|
||||
value: boolean;
|
||||
name: string;
|
||||
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
description?: string;
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
export function VariableCheckboxField({
|
||||
value,
|
||||
name,
|
||||
description,
|
||||
onChange,
|
||||
ariaLabel,
|
||||
}: PropsWithChildren<VariableCheckboxFieldProps>): ReactElement {
|
||||
const uniqueId = useId();
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
id={uniqueId}
|
||||
label={name}
|
||||
description={description}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
aria-label={ariaLabel}
|
||||
/>
|
||||
);
|
||||
}
|
@@ -1,14 +1,12 @@
|
||||
import { isEqual } from 'lodash';
|
||||
import React, { FormEvent, PureComponent } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import { AppEvents, LoadingState, SelectableValue, VariableType } from '@grafana/data';
|
||||
import { LoadingState, SelectableValue, VariableType } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { Button, HorizontalGroup, Icon, InlineFieldRow, VerticalGroup } from '@grafana/ui';
|
||||
import { Button, HorizontalGroup, Icon } from '@grafana/ui';
|
||||
|
||||
import { appEvents } from '../../../core/core';
|
||||
import { StoreState, ThunkDispatch } from '../../../types';
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { hasOptions } from '../guard';
|
||||
@@ -22,7 +20,8 @@ import { toKeyedVariableIdentifier, toVariablePayload } from '../utils';
|
||||
|
||||
import { ConfirmDeleteModal } from './ConfirmDeleteModal';
|
||||
import { VariableHideSelect } from './VariableHideSelect';
|
||||
import { VariableSectionHeader } from './VariableSectionHeader';
|
||||
import { VariableLegend } from './VariableLegend';
|
||||
import { VariableTextAreaField } from './VariableTextAreaField';
|
||||
import { VariableTextField } from './VariableTextField';
|
||||
import { VariableTypeSelect } from './VariableTypeSelect';
|
||||
import { VariableValuesPreview } from './VariableValuesPreview';
|
||||
@@ -75,14 +74,6 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props, State>
|
||||
this.props.variableEditorMount(this.props.identifier);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<{}>, snapshot?: any): void {
|
||||
if (!isEqual(prevProps.editor.errors, this.props.editor.errors)) {
|
||||
Object.values(this.props.editor.errors).forEach((error) => {
|
||||
appEvents.emit(AppEvents.alertWarning, ['Validation', error]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this.props.variableEditorUnMount(this.props.identifier);
|
||||
}
|
||||
@@ -104,12 +95,12 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props, State>
|
||||
this.props.changeVariableProp(this.props.identifier, 'label', event.currentTarget.value);
|
||||
};
|
||||
|
||||
onDescriptionChange = (event: FormEvent<HTMLInputElement>) => {
|
||||
onDescriptionChange = (event: FormEvent<HTMLTextAreaElement>) => {
|
||||
this.props.changeVariableProp(this.props.identifier, 'description', event.currentTarget.value);
|
||||
};
|
||||
|
||||
onHideChange = (option: SelectableValue<VariableHide>) => {
|
||||
this.props.changeVariableProp(this.props.identifier, 'hide', option.value);
|
||||
onHideChange = (option: VariableHide) => {
|
||||
this.props.changeVariableProp(this.props.identifier, 'hide', option);
|
||||
};
|
||||
|
||||
onPropChanged = ({ propName, propValue, updateOptions = false }: OnPropChangeArguments) => {
|
||||
@@ -156,79 +147,68 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props, State>
|
||||
const loading = variable.state === LoadingState.Loading;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<form aria-label="Variable editor Form" onSubmit={this.onHandleSubmit}>
|
||||
<VerticalGroup spacing="lg">
|
||||
<VerticalGroup spacing="none">
|
||||
<VariableSectionHeader name="General" />
|
||||
<InlineFieldRow>
|
||||
<VariableTextField
|
||||
value={this.props.editor.name}
|
||||
onChange={this.onNameChange}
|
||||
name="Name"
|
||||
placeholder="name"
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2}
|
||||
maxLength={VariableNameConstraints.MaxSize}
|
||||
required
|
||||
tooltip="Variable name cannot be longer than 50 characters"
|
||||
/>
|
||||
<VariableTypeSelect onChange={this.onTypeChange} type={this.props.variable.type} />
|
||||
</InlineFieldRow>
|
||||
<VariableTypeSelect onChange={this.onTypeChange} type={this.props.variable.type} />
|
||||
|
||||
{this.props.editor.errors.name && (
|
||||
<div className="gf-form">
|
||||
<span className="gf-form-label gf-form-label--error">{this.props.editor.errors.name}</span>
|
||||
</div>
|
||||
)}
|
||||
<VariableLegend>General</VariableLegend>
|
||||
<VariableTextField
|
||||
value={this.props.editor.name}
|
||||
onChange={this.onNameChange}
|
||||
name="Name"
|
||||
placeholder="Variable name"
|
||||
description="The name of the template variable. (Max. 50 characters)"
|
||||
invalid={!!this.props.editor.errors.name}
|
||||
error={this.props.editor.errors.name}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2}
|
||||
maxLength={VariableNameConstraints.MaxSize}
|
||||
required
|
||||
/>
|
||||
|
||||
<InlineFieldRow>
|
||||
<VariableTextField
|
||||
value={this.props.variable.label ?? ''}
|
||||
onChange={this.onLabelChange}
|
||||
name="Label"
|
||||
placeholder="optional display name"
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2}
|
||||
/>
|
||||
<VariableHideSelect
|
||||
onChange={this.onHideChange}
|
||||
hide={this.props.variable.hide}
|
||||
type={this.props.variable.type}
|
||||
/>
|
||||
</InlineFieldRow>
|
||||
<VariableTextField
|
||||
name="Label"
|
||||
description="Optional display name"
|
||||
value={this.props.variable.label ?? ''}
|
||||
placeholder="Label name"
|
||||
onChange={this.onLabelChange}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2}
|
||||
/>
|
||||
<VariableTextAreaField
|
||||
name="Description"
|
||||
value={variable.description ?? ''}
|
||||
placeholder="Descriptive text"
|
||||
onChange={this.onDescriptionChange}
|
||||
width={52}
|
||||
/>
|
||||
<VariableHideSelect
|
||||
onChange={this.onHideChange}
|
||||
hide={this.props.variable.hide}
|
||||
type={this.props.variable.type}
|
||||
/>
|
||||
|
||||
<VariableTextField
|
||||
name="Description"
|
||||
value={variable.description ?? ''}
|
||||
placeholder="descriptive text"
|
||||
onChange={this.onDescriptionChange}
|
||||
grow
|
||||
/>
|
||||
</VerticalGroup>
|
||||
{EditorToRender && <EditorToRender variable={this.props.variable} onPropChange={this.onPropChanged} />}
|
||||
|
||||
{EditorToRender && <EditorToRender variable={this.props.variable} onPropChange={this.onPropChanged} />}
|
||||
{hasOptions(this.props.variable) ? <VariableValuesPreview variable={this.props.variable} /> : null}
|
||||
|
||||
{hasOptions(this.props.variable) ? <VariableValuesPreview variable={this.props.variable} /> : null}
|
||||
|
||||
<HorizontalGroup spacing="md">
|
||||
<Button variant="destructive" onClick={this.onModalOpen}>
|
||||
<div style={{ marginTop: '16px' }}>
|
||||
<HorizontalGroup spacing="md" height="inherit">
|
||||
<Button variant="destructive" fill="outline" onClick={this.onModalOpen}>
|
||||
Delete
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
aria-label={selectors.pages.Dashboard.Settings.Variables.Edit.General.submitButton}
|
||||
disabled={loading}
|
||||
variant={'secondary'}
|
||||
variant="secondary"
|
||||
>
|
||||
Run query
|
||||
{loading ? (
|
||||
<Icon className="spin-clockwise" name="sync" size="sm" style={{ marginLeft: '2px' }} />
|
||||
) : null}
|
||||
{loading && <Icon className="spin-clockwise" name="sync" size="sm" style={{ marginLeft: '2px' }} />}
|
||||
</Button>
|
||||
<Button variant="primary" onClick={this.onApply}>
|
||||
Apply
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</VerticalGroup>
|
||||
</div>
|
||||
</form>
|
||||
<ConfirmDeleteModal
|
||||
isOpen={this.state.showDeleteModal}
|
||||
@@ -236,7 +216,7 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props, State>
|
||||
onConfirm={this.onDelete}
|
||||
onDismiss={this.onModalClose}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,37 +1,30 @@
|
||||
import React, { PropsWithChildren, useMemo } from 'react';
|
||||
|
||||
import { SelectableValue, VariableType } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { VariableSelectField } from '../editor/VariableSelectField';
|
||||
import { VariableHide } from '../types';
|
||||
import { VariableType, VariableHide } from '@grafana/data';
|
||||
import { Field, RadioButtonGroup } from '@grafana/ui';
|
||||
|
||||
interface Props {
|
||||
onChange: (option: SelectableValue<VariableHide>) => void;
|
||||
onChange: (option: VariableHide) => void;
|
||||
hide: VariableHide;
|
||||
type: VariableType;
|
||||
}
|
||||
|
||||
const HIDE_OPTIONS = [
|
||||
{ label: '', value: VariableHide.dontHide },
|
||||
{ label: 'Label', value: VariableHide.hideLabel },
|
||||
{ label: 'Variable', value: VariableHide.hideVariable },
|
||||
{ label: 'Label and value', value: VariableHide.dontHide },
|
||||
{ label: 'Value', value: VariableHide.hideLabel },
|
||||
{ label: 'Nothing', value: VariableHide.hideVariable },
|
||||
];
|
||||
|
||||
export function VariableHideSelect({ onChange, hide, type }: PropsWithChildren<Props>) {
|
||||
const value = useMemo(() => HIDE_OPTIONS.find((o) => o.value === hide) ?? HIDE_OPTIONS[0], [hide]);
|
||||
const value = useMemo(() => HIDE_OPTIONS.find((o) => o.value === hide)?.value ?? HIDE_OPTIONS[0].value, [hide]);
|
||||
|
||||
if (type === 'constant') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<VariableSelectField
|
||||
name="Hide"
|
||||
value={value}
|
||||
options={HIDE_OPTIONS}
|
||||
onChange={onChange}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalHideSelectV2}
|
||||
/>
|
||||
<Field label="Show on dashboard">
|
||||
<RadioButtonGroup options={HIDE_OPTIONS} onChange={onChange} value={value} />
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
19
public/app/features/variables/editor/VariableLegend.tsx
Normal file
19
public/app/features/variables/editor/VariableLegend.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Legend, useStyles2 } from '@grafana/ui';
|
||||
|
||||
export function VariableLegend({ className, ...rest }: Parameters<typeof Legend>['0']) {
|
||||
const styles = useStyles2(getStyles);
|
||||
return <Legend {...rest} className={cx(styles.legend, className)} />;
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
legend: css({
|
||||
marginTop: theme.spacing(3),
|
||||
marginBottom: theme.spacing(1),
|
||||
}),
|
||||
};
|
||||
}
|
@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
|
||||
import React, { PropsWithChildren, ReactElement } from 'react';
|
||||
|
||||
import { GrafanaTheme, SelectableValue } from '@grafana/data';
|
||||
import { InlineFormLabel, Select, useStyles } from '@grafana/ui';
|
||||
import { Field, Select, useStyles } from '@grafana/ui';
|
||||
import { useUniqueId } from 'app/plugins/datasource/influxdb/components/useUniqueId';
|
||||
|
||||
interface VariableSelectFieldProps<T> {
|
||||
@@ -10,42 +10,37 @@ interface VariableSelectFieldProps<T> {
|
||||
value: SelectableValue<T>;
|
||||
options: Array<SelectableValue<T>>;
|
||||
onChange: (option: SelectableValue<T>) => void;
|
||||
tooltip?: string;
|
||||
testId?: string;
|
||||
width?: number;
|
||||
labelWidth?: number;
|
||||
description?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function VariableSelectField({
|
||||
name,
|
||||
description,
|
||||
value,
|
||||
options,
|
||||
tooltip,
|
||||
onChange,
|
||||
testId,
|
||||
width,
|
||||
labelWidth,
|
||||
}: PropsWithChildren<VariableSelectFieldProps<any>>): ReactElement {
|
||||
const styles = useStyles(getStyles);
|
||||
const uniqueId = useUniqueId();
|
||||
const inputId = `variable-select-input-${name}-${uniqueId}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<InlineFormLabel width={labelWidth ?? 6} tooltip={tooltip} htmlFor={inputId}>
|
||||
{name}
|
||||
</InlineFormLabel>
|
||||
<Field label={name} description={description} htmlFor={inputId}>
|
||||
<div data-testid={testId}>
|
||||
<Select
|
||||
inputId={inputId}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
width={width ?? 25}
|
||||
width={width ?? 30}
|
||||
options={options}
|
||||
className={styles.selectContainer}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,32 +0,0 @@
|
||||
import React, { ChangeEvent, PropsWithChildren, ReactElement } from 'react';
|
||||
|
||||
import { InlineField, InlineSwitch } from '@grafana/ui';
|
||||
import { useUniqueId } from 'app/plugins/datasource/influxdb/components/useUniqueId';
|
||||
interface VariableSwitchFieldProps {
|
||||
value: boolean;
|
||||
name: string;
|
||||
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
tooltip?: string;
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
export function VariableSwitchField({
|
||||
value,
|
||||
name,
|
||||
tooltip,
|
||||
onChange,
|
||||
ariaLabel,
|
||||
}: PropsWithChildren<VariableSwitchFieldProps>): ReactElement {
|
||||
const uniqueId = useUniqueId();
|
||||
return (
|
||||
<InlineField label={name} labelWidth={20} tooltip={tooltip}>
|
||||
<InlineSwitch
|
||||
id={`var-switch-${uniqueId}`}
|
||||
label={name}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
aria-label={ariaLabel}
|
||||
/>
|
||||
</InlineField>
|
||||
);
|
||||
}
|
@@ -1,49 +1,43 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { FormEvent, PropsWithChildren, ReactElement, useCallback } from 'react';
|
||||
import { useId } from '@react-aria/utils';
|
||||
import React, { FormEvent, PropsWithChildren, ReactElement } from 'react';
|
||||
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { InlineField, TextArea, useStyles } from '@grafana/ui';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Field, TextArea, useStyles2 } from '@grafana/ui';
|
||||
|
||||
interface VariableTextAreaFieldProps<T> {
|
||||
interface VariableTextAreaFieldProps {
|
||||
name: string;
|
||||
value: string;
|
||||
placeholder: string;
|
||||
onChange: (event: FormEvent<HTMLTextAreaElement>) => void;
|
||||
width: number;
|
||||
tooltip?: string;
|
||||
ariaLabel?: string;
|
||||
required?: boolean;
|
||||
labelWidth?: number;
|
||||
testId?: string;
|
||||
onBlur?: (event: FormEvent<HTMLTextAreaElement>) => void;
|
||||
description?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function VariableTextAreaField({
|
||||
name,
|
||||
value,
|
||||
name,
|
||||
description,
|
||||
placeholder,
|
||||
tooltip,
|
||||
onChange,
|
||||
onBlur,
|
||||
ariaLabel,
|
||||
required,
|
||||
width,
|
||||
labelWidth,
|
||||
testId,
|
||||
}: PropsWithChildren<VariableTextAreaFieldProps<any>>): ReactElement {
|
||||
const styles = useStyles(getStyles);
|
||||
const getLineCount = useCallback((value: any) => {
|
||||
if (value && typeof value === 'string') {
|
||||
return value.split('\n').length;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}, []);
|
||||
}: PropsWithChildren<VariableTextAreaFieldProps>): ReactElement {
|
||||
const styles = useStyles2(getStyles);
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<InlineField label={name} labelWidth={labelWidth ?? 12} tooltip={tooltip}>
|
||||
<Field label={name} description={description} htmlFor={id}>
|
||||
<TextArea
|
||||
rows={getLineCount(value)}
|
||||
id={id}
|
||||
rows={2}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
@@ -54,18 +48,19 @@ export function VariableTextAreaField({
|
||||
className={styles.textarea}
|
||||
data-testid={testId}
|
||||
/>
|
||||
</InlineField>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaTheme) {
|
||||
export function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
textarea: css`
|
||||
white-space: pre-wrap;
|
||||
min-height: 32px;
|
||||
min-height: ${theme.spacing(4)};
|
||||
height: auto;
|
||||
overflow: auto;
|
||||
padding: 6px 8px;
|
||||
padding: ${theme.spacing(0.75, 1)};
|
||||
width: inherit;
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
@@ -1,53 +1,55 @@
|
||||
import React, { FormEvent, PropsWithChildren, ReactElement } from 'react';
|
||||
import { useId } from '@react-aria/utils';
|
||||
import React, { FormEvent, PropsWithChildren } from 'react';
|
||||
|
||||
import { InlineField, Input, PopoverContent } from '@grafana/ui';
|
||||
import { Field, Input } from '@grafana/ui';
|
||||
|
||||
interface VariableTextFieldProps {
|
||||
value: string;
|
||||
name: string;
|
||||
placeholder: string;
|
||||
placeholder?: string;
|
||||
onChange: (event: FormEvent<HTMLInputElement>) => void;
|
||||
testId?: string;
|
||||
tooltip?: PopoverContent;
|
||||
required?: boolean;
|
||||
width?: number;
|
||||
labelWidth?: number;
|
||||
grow?: boolean;
|
||||
onBlur?: (event: FormEvent<HTMLInputElement>) => void;
|
||||
interactive?: boolean;
|
||||
maxLength?: number;
|
||||
description?: React.ReactNode;
|
||||
invalid?: boolean;
|
||||
error?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function VariableTextField({
|
||||
value,
|
||||
name,
|
||||
placeholder,
|
||||
placeholder = '',
|
||||
onChange,
|
||||
testId,
|
||||
width,
|
||||
labelWidth,
|
||||
required,
|
||||
onBlur,
|
||||
tooltip,
|
||||
grow,
|
||||
interactive,
|
||||
description,
|
||||
invalid,
|
||||
error,
|
||||
maxLength,
|
||||
}: PropsWithChildren<VariableTextFieldProps>): ReactElement {
|
||||
}: PropsWithChildren<VariableTextFieldProps>) {
|
||||
const id = useId(name);
|
||||
|
||||
return (
|
||||
<InlineField interactive={interactive} label={name} labelWidth={labelWidth ?? 12} tooltip={tooltip} grow={grow}>
|
||||
<Field label={name} description={description} invalid={invalid} error={error} htmlFor={id}>
|
||||
<Input
|
||||
type="text"
|
||||
id={name}
|
||||
name={name}
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
width={grow ? undefined : width ?? 25}
|
||||
width={grow ? undefined : width ?? 30}
|
||||
data-testid={testId}
|
||||
maxLength={maxLength}
|
||||
required={required}
|
||||
/>
|
||||
</InlineField>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ import React, { PropsWithChildren, useMemo } from 'react';
|
||||
import { SelectableValue, VariableType } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { variableAdapters } from '../adapters';
|
||||
import { VariableSelectField } from '../editor/VariableSelectField';
|
||||
import { getVariableTypes } from '../utils';
|
||||
|
||||
@@ -18,11 +17,10 @@ export function VariableTypeSelect({ onChange, type }: PropsWithChildren<Props>)
|
||||
|
||||
return (
|
||||
<VariableSelectField
|
||||
name="Type"
|
||||
name="Select variable type"
|
||||
value={value}
|
||||
options={options}
|
||||
onChange={onChange}
|
||||
tooltip={variableAdapters.get(type).description}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2}
|
||||
/>
|
||||
);
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { MouseEvent, useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Button, InlineFieldRow, InlineLabel, useStyles, VerticalGroup } from '@grafana/ui';
|
||||
import { Button, InlineFieldRow, InlineLabel, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { VariableOption, VariableWithOptions } from '../types';
|
||||
|
||||
@@ -23,7 +23,7 @@ export const VariableValuesPreview: React.FunctionComponent<VariableValuesPrevie
|
||||
},
|
||||
[previewLimit, setPreviewLimit]
|
||||
);
|
||||
const styles = useStyles(getStyles);
|
||||
const styles = useStyles2(getStyles);
|
||||
useEffect(() => setPreviewOptions(options.slice(0, previewLimit)), [previewLimit, options]);
|
||||
|
||||
if (!previewOptions.length) {
|
||||
@@ -31,7 +31,7 @@ export const VariableValuesPreview: React.FunctionComponent<VariableValuesPrevie
|
||||
}
|
||||
|
||||
return (
|
||||
<VerticalGroup spacing="none">
|
||||
<div style={{ display: 'flex', flexDirection: 'column', marginTop: '16px' }}>
|
||||
<h5>Preview of values</h5>
|
||||
<InlineFieldRow>
|
||||
{previewOptions.map((o, index) => (
|
||||
@@ -54,22 +54,27 @@ export const VariableValuesPreview: React.FunctionComponent<VariableValuesPrevie
|
||||
</Button>
|
||||
</InlineFieldRow>
|
||||
)}
|
||||
</VerticalGroup>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
VariableValuesPreview.displayName = 'VariableValuesPreview';
|
||||
|
||||
function getStyles(theme: GrafanaTheme) {
|
||||
function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
optionContainer: css`
|
||||
margin-left: ${theme.spacing.xs};
|
||||
margin-bottom: ${theme.spacing.xs};
|
||||
`,
|
||||
label: css`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 50vw;
|
||||
`,
|
||||
wrapper: css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
marginTop: theme.spacing(2),
|
||||
}),
|
||||
optionContainer: css({
|
||||
marginLeft: theme.spacing(0.5),
|
||||
marginBottom: theme.spacing(0.5),
|
||||
}),
|
||||
label: css({
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
maxWidth: '50vw',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
@@ -1,113 +1,119 @@
|
||||
import React, { ChangeEvent, FormEvent, PureComponent } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import React, { ChangeEvent, FormEvent } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { GrafanaTheme2, IntervalVariableModel, SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { InlineFieldRow, VerticalGroup } from '@grafana/ui';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { VariableSectionHeader } from '../editor/VariableSectionHeader';
|
||||
import { VariableCheckboxField } from '../editor/VariableCheckboxField';
|
||||
import { VariableLegend } from '../editor/VariableLegend';
|
||||
import { VariableSelectField } from '../editor/VariableSelectField';
|
||||
import { VariableSwitchField } from '../editor/VariableSwitchField';
|
||||
import { VariableTextField } from '../editor/VariableTextField';
|
||||
import { VariableEditorProps } from '../editor/types';
|
||||
import { IntervalVariableModel } from '../types';
|
||||
|
||||
const STEP_OPTIONS = [1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 200, 300, 400, 500].map((count) => ({
|
||||
label: `${count}`,
|
||||
value: count,
|
||||
}));
|
||||
|
||||
export interface Props extends VariableEditorProps<IntervalVariableModel> {}
|
||||
|
||||
export class IntervalVariableEditor extends PureComponent<Props> {
|
||||
onAutoChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.props.onPropChange({
|
||||
export const IntervalVariableEditor = React.memo(({ onPropChange, variable }: Props) => {
|
||||
const onAutoChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
onPropChange({
|
||||
propName: 'auto',
|
||||
propValue: event.target.checked,
|
||||
updateOptions: true,
|
||||
});
|
||||
};
|
||||
|
||||
onQueryChanged = (event: FormEvent<HTMLInputElement>) => {
|
||||
this.props.onPropChange({
|
||||
const onQueryChanged = (event: FormEvent<HTMLInputElement>) => {
|
||||
onPropChange({
|
||||
propName: 'query',
|
||||
propValue: event.currentTarget.value,
|
||||
});
|
||||
};
|
||||
|
||||
onQueryBlur = (event: FormEvent<HTMLInputElement>) => {
|
||||
this.props.onPropChange({
|
||||
const onQueryBlur = (event: FormEvent<HTMLInputElement>) => {
|
||||
onPropChange({
|
||||
propName: 'query',
|
||||
propValue: event.currentTarget.value,
|
||||
updateOptions: true,
|
||||
});
|
||||
};
|
||||
|
||||
onAutoCountChanged = (option: SelectableValue<number>) => {
|
||||
this.props.onPropChange({
|
||||
const onAutoCountChanged = (option: SelectableValue<number>) => {
|
||||
onPropChange({
|
||||
propName: 'auto_count',
|
||||
propValue: option.value,
|
||||
updateOptions: true,
|
||||
});
|
||||
};
|
||||
|
||||
onAutoMinChanged = (event: FormEvent<HTMLInputElement>) => {
|
||||
this.props.onPropChange({
|
||||
const onAutoMinChanged = (event: FormEvent<HTMLInputElement>) => {
|
||||
onPropChange({
|
||||
propName: 'auto_min',
|
||||
propValue: event.currentTarget.value,
|
||||
updateOptions: true,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { variable } = this.props;
|
||||
const stepOptions = [1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 200, 300, 400, 500].map((count) => ({
|
||||
label: `${count}`,
|
||||
value: count,
|
||||
}));
|
||||
const stepValue = stepOptions.find((o) => o.value === variable.auto_count) ?? stepOptions[0];
|
||||
const stepValue = STEP_OPTIONS.find((o) => o.value === variable.auto_count) ?? STEP_OPTIONS[0];
|
||||
|
||||
return (
|
||||
<VerticalGroup spacing="xs">
|
||||
<VariableSectionHeader name="Interval options" />
|
||||
<VerticalGroup spacing="none">
|
||||
<VariableTextField
|
||||
value={this.props.variable.query}
|
||||
name="Values"
|
||||
placeholder="1m,10m,1h,6h,1d,7d"
|
||||
onChange={this.onQueryChanged}
|
||||
onBlur={this.onQueryBlur}
|
||||
labelWidth={20}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.IntervalVariable.intervalsValueInput}
|
||||
grow
|
||||
required
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<>
|
||||
<VariableLegend>Interval options</VariableLegend>
|
||||
<VariableTextField
|
||||
value={variable.query}
|
||||
name="Values"
|
||||
placeholder="1m,10m,1h,6h,1d,7d"
|
||||
onChange={onQueryChanged}
|
||||
onBlur={onQueryBlur}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.IntervalVariable.intervalsValueInput}
|
||||
width={32}
|
||||
required
|
||||
/>
|
||||
|
||||
<VariableCheckboxField
|
||||
value={variable.auto}
|
||||
name="Auto option"
|
||||
description="Dynamically calculates interval by dividing time range by the count specified"
|
||||
onChange={onAutoChange}
|
||||
/>
|
||||
{variable.auto && (
|
||||
<div className={styles.autoFields}>
|
||||
<VariableSelectField
|
||||
name="Step count"
|
||||
description="How many times the current time range should be divided to calculate the value"
|
||||
value={stepValue}
|
||||
options={STEP_OPTIONS}
|
||||
onChange={onAutoCountChanged}
|
||||
width={9}
|
||||
/>
|
||||
<InlineFieldRow>
|
||||
<VariableSwitchField
|
||||
value={this.props.variable.auto}
|
||||
name="Auto option"
|
||||
tooltip="Dynamically calculates interval by dividing time range by the count specified."
|
||||
onChange={this.onAutoChange}
|
||||
/>
|
||||
{this.props.variable.auto ? (
|
||||
<>
|
||||
<VariableSelectField
|
||||
name="Step count"
|
||||
value={stepValue}
|
||||
options={stepOptions}
|
||||
onChange={this.onAutoCountChanged}
|
||||
tooltip="How many times the current time range should be divided to calculate the value."
|
||||
labelWidth={7}
|
||||
width={9}
|
||||
/>
|
||||
<VariableTextField
|
||||
value={this.props.variable.auto_min}
|
||||
name="Min interval"
|
||||
placeholder="10s"
|
||||
onChange={this.onAutoMinChanged}
|
||||
tooltip="The calculated value will not go below this threshold."
|
||||
labelWidth={13}
|
||||
width={11}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</InlineFieldRow>
|
||||
</VerticalGroup>
|
||||
</VerticalGroup>
|
||||
);
|
||||
}
|
||||
<VariableTextField
|
||||
value={variable.auto_min}
|
||||
name="Min interval"
|
||||
description="The calculated value will not go below this threshold"
|
||||
placeholder="10s"
|
||||
onChange={onAutoMinChanged}
|
||||
width={11}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
IntervalVariableEditor.displayName = 'IntervalVariableEditor';
|
||||
|
||||
function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
autoFields: css({
|
||||
marginTop: theme.spacing(2),
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
@@ -110,7 +110,7 @@ describe('QueryVariableEditor', () => {
|
||||
const getQueryField = () =>
|
||||
screen.getByRole('textbox', { name: /variable editor form default variable query editor textarea/i });
|
||||
|
||||
const getRegExField = () => screen.getByLabelText('Regex');
|
||||
const getRegExField = () => screen.getByLabelText(/Regex/);
|
||||
|
||||
const fieldAccessors: Record<string, () => HTMLElement> = {
|
||||
query: getQueryField,
|
||||
|
@@ -1,17 +1,16 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { FormEvent, PureComponent } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
|
||||
import { DataSourceInstanceSettings, getDataSourceRef, LoadingState, SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { DataSourcePicker, getTemplateSrv } from '@grafana/runtime';
|
||||
import { InlineField, InlineFieldRow, VerticalGroup } from '@grafana/ui';
|
||||
import { Field } from '@grafana/ui';
|
||||
|
||||
import { StoreState } from '../../../types';
|
||||
import { getTimeSrv } from '../../dashboard/services/TimeSrv';
|
||||
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
|
||||
import { VariableSectionHeader } from '../editor/VariableSectionHeader';
|
||||
import { VariableTextField } from '../editor/VariableTextField';
|
||||
import { VariableLegend } from '../editor/VariableLegend';
|
||||
import { VariableTextAreaField } from '../editor/VariableTextAreaField';
|
||||
import { initialVariableEditorState } from '../editor/reducer';
|
||||
import { getQueryVariableEditorState } from '../editor/selectors';
|
||||
import { OnPropChangeArguments, VariableEditorProps } from '../editor/types';
|
||||
@@ -105,19 +104,19 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
|
||||
}
|
||||
};
|
||||
|
||||
onRegExChange = (event: FormEvent<HTMLInputElement>) => {
|
||||
onRegExChange = (event: FormEvent<HTMLTextAreaElement>) => {
|
||||
this.setState({ regex: event.currentTarget.value });
|
||||
};
|
||||
|
||||
onRegExBlur = async (event: FormEvent<HTMLInputElement>) => {
|
||||
onRegExBlur = async (event: FormEvent<HTMLTextAreaElement>) => {
|
||||
const regex = event.currentTarget.value;
|
||||
if (this.props.variable.regex !== regex) {
|
||||
this.props.onPropChange({ propName: 'regex', propValue: regex, updateOptions: true });
|
||||
}
|
||||
};
|
||||
|
||||
onRefreshChange = (option: SelectableValue<VariableRefresh>) => {
|
||||
this.props.onPropChange({ propName: 'refresh', propValue: option.value });
|
||||
onRefreshChange = (option: VariableRefresh) => {
|
||||
this.props.onPropChange({ propName: 'refresh', propValue: option });
|
||||
};
|
||||
|
||||
onSortChange = async (option: SelectableValue<VariableSort>) => {
|
||||
@@ -141,12 +140,14 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
|
||||
|
||||
if (isLegacyQueryEditor(VariableQueryEditor, datasource)) {
|
||||
return (
|
||||
<VariableQueryEditor
|
||||
datasource={datasource}
|
||||
query={query}
|
||||
templateSrv={getTemplateSrv()}
|
||||
onChange={this.onLegacyQueryChange}
|
||||
/>
|
||||
<Field label="Query">
|
||||
<VariableQueryEditor
|
||||
datasource={datasource}
|
||||
query={query}
|
||||
templateSrv={getTemplateSrv()}
|
||||
onChange={this.onLegacyQueryChange}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -154,16 +155,18 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
|
||||
|
||||
if (isQueryEditor(VariableQueryEditor, datasource)) {
|
||||
return (
|
||||
<VariableQueryEditor
|
||||
datasource={datasource}
|
||||
query={query}
|
||||
onChange={this.onQueryChange}
|
||||
onRunQuery={() => {}}
|
||||
data={{ series: [], state: LoadingState.Done, timeRange: range }}
|
||||
range={range}
|
||||
onBlur={() => {}}
|
||||
history={[]}
|
||||
/>
|
||||
<Field label="Query">
|
||||
<VariableQueryEditor
|
||||
datasource={datasource}
|
||||
query={query}
|
||||
onChange={this.onQueryChange}
|
||||
onRunQuery={() => {}}
|
||||
data={{ series: [], state: LoadingState.Done, timeRange: range }}
|
||||
range={range}
|
||||
onBlur={() => {}}
|
||||
history={[]}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -172,63 +175,55 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
|
||||
|
||||
render() {
|
||||
return (
|
||||
<VerticalGroup spacing="xs">
|
||||
<VariableSectionHeader name="Query Options" />
|
||||
<VerticalGroup spacing="lg">
|
||||
<VerticalGroup spacing="none">
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Data source" labelWidth={20} htmlFor="data-source-picker">
|
||||
<DataSourcePicker
|
||||
current={this.props.variable.datasource}
|
||||
onChange={this.onDataSourceChange}
|
||||
variables={true}
|
||||
/>
|
||||
</InlineField>
|
||||
<QueryVariableRefreshSelect onChange={this.onRefreshChange} refresh={this.props.variable.refresh} />
|
||||
</InlineFieldRow>
|
||||
<div
|
||||
className={css`
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
`}
|
||||
>
|
||||
{this.renderQueryEditor()}
|
||||
</div>
|
||||
<VariableTextField
|
||||
value={this.state.regex ?? this.props.variable.regex}
|
||||
name="Regex"
|
||||
placeholder="/.*-(?<text>.*)-(?<value>.*)-.*/"
|
||||
onChange={this.onRegExChange}
|
||||
onBlur={this.onRegExBlur}
|
||||
labelWidth={20}
|
||||
interactive={true}
|
||||
tooltip={
|
||||
<div>
|
||||
Optional, if you want to extract part of a series name or metric node segment. Named capture groups
|
||||
can be used to separate the display text and value (
|
||||
<a
|
||||
className="external-link"
|
||||
href="https://grafana.com/docs/grafana/latest/variables/filter-variables-with-regex#filter-and-modify-using-named-text-and-value-capture-groups"
|
||||
target="__blank"
|
||||
>
|
||||
see examples
|
||||
</a>
|
||||
).
|
||||
</div>
|
||||
}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2}
|
||||
grow
|
||||
/>
|
||||
<QueryVariableSortSelect onChange={this.onSortChange} sort={this.props.variable.sort} />
|
||||
</VerticalGroup>
|
||||
|
||||
<SelectionOptionsEditor
|
||||
variable={this.props.variable}
|
||||
onPropChange={this.onSelectionOptionsChange}
|
||||
onMultiChanged={this.props.changeVariableMultiValue}
|
||||
<>
|
||||
<VariableLegend>Query options</VariableLegend>
|
||||
<Field label="Data source" htmlFor="data-source-picker">
|
||||
<DataSourcePicker
|
||||
current={this.props.variable.datasource}
|
||||
onChange={this.onDataSourceChange}
|
||||
variables={true}
|
||||
width={30}
|
||||
/>
|
||||
</VerticalGroup>
|
||||
</VerticalGroup>
|
||||
</Field>
|
||||
|
||||
{this.renderQueryEditor()}
|
||||
|
||||
<VariableTextAreaField
|
||||
value={this.state.regex ?? this.props.variable.regex}
|
||||
name="Regex"
|
||||
description={
|
||||
<div>
|
||||
Optional, if you want to extract part of a series name or metric node segment.
|
||||
<br />
|
||||
Named capture groups can be used to separate the display text and value (
|
||||
<a
|
||||
className="external-link"
|
||||
href="https://grafana.com/docs/grafana/latest/variables/filter-variables-with-regex#filter-and-modify-using-named-text-and-value-capture-groups"
|
||||
target="__blank"
|
||||
>
|
||||
see examples
|
||||
</a>
|
||||
).
|
||||
</div>
|
||||
}
|
||||
placeholder="/.*-(?<text>.*)-(?<value>.*)-.*/"
|
||||
onChange={this.onRegExChange}
|
||||
onBlur={this.onRegExBlur}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2}
|
||||
width={52}
|
||||
/>
|
||||
|
||||
<QueryVariableSortSelect onChange={this.onSortChange} sort={this.props.variable.sort} />
|
||||
|
||||
<QueryVariableRefreshSelect onChange={this.onRefreshChange} refresh={this.props.variable.refresh} />
|
||||
|
||||
<VariableLegend>Selection options</VariableLegend>
|
||||
<SelectionOptionsEditor
|
||||
variable={this.props.variable}
|
||||
onPropChange={this.onSelectionOptionsChange}
|
||||
onMultiChanged={this.props.changeVariableMultiValue}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,10 @@
|
||||
import React, { PropsWithChildren, useMemo } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { VariableSelectField } from '../editor/VariableSelectField';
|
||||
import { VariableRefresh } from '../types';
|
||||
import { VariableRefresh } from '@grafana/data';
|
||||
import { Field, RadioButtonGroup } from '@grafana/ui';
|
||||
|
||||
interface Props {
|
||||
onChange: (option: SelectableValue<VariableRefresh>) => void;
|
||||
onChange: (option: VariableRefresh) => void;
|
||||
refresh: VariableRefresh;
|
||||
}
|
||||
|
||||
@@ -17,17 +14,14 @@ const REFRESH_OPTIONS = [
|
||||
];
|
||||
|
||||
export function QueryVariableRefreshSelect({ onChange, refresh }: PropsWithChildren<Props>) {
|
||||
const value = useMemo(() => REFRESH_OPTIONS.find((o) => o.value === refresh) ?? REFRESH_OPTIONS[0], [refresh]);
|
||||
const value = useMemo(
|
||||
() => REFRESH_OPTIONS.find((o) => o.value === refresh)?.value ?? REFRESH_OPTIONS[0].value,
|
||||
[refresh]
|
||||
);
|
||||
|
||||
return (
|
||||
<VariableSelectField
|
||||
name="Refresh"
|
||||
value={value}
|
||||
options={REFRESH_OPTIONS}
|
||||
onChange={onChange}
|
||||
labelWidth={10}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRefreshSelectV2}
|
||||
tooltip="When to update the values of this variable."
|
||||
/>
|
||||
<Field label="Refresh" description="When to update the values of this variable">
|
||||
<RadioButtonGroup options={REFRESH_OPTIONS} onChange={onChange} value={value} />
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
@@ -27,12 +27,12 @@ export function QueryVariableSortSelect({ onChange, sort }: PropsWithChildren<Pr
|
||||
return (
|
||||
<VariableSelectField
|
||||
name="Sort"
|
||||
description="How to sort the values of this variable"
|
||||
value={value}
|
||||
options={SORT_OPTIONS}
|
||||
onChange={onChange}
|
||||
labelWidth={10}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsSortSelectV2}
|
||||
tooltip="How to sort the values of this variable."
|
||||
width={25}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import React, { FormEvent, ReactElement, useCallback } from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { VerticalGroup } from '@grafana/ui';
|
||||
|
||||
import { VariableSectionHeader } from '../editor/VariableSectionHeader';
|
||||
import { VariableLegend } from '../editor/VariableLegend';
|
||||
import { VariableTextField } from '../editor/VariableTextField';
|
||||
import { VariableEditorProps } from '../editor/types';
|
||||
import { TextBoxVariableModel } from '../types';
|
||||
@@ -24,18 +23,17 @@ export function TextBoxVariableEditor({ onPropChange, variable: { query } }: Pro
|
||||
const onBlur = useCallback((e: FormEvent<HTMLInputElement>) => updateVariable(e, true), [updateVariable]);
|
||||
|
||||
return (
|
||||
<VerticalGroup spacing="xs">
|
||||
<VariableSectionHeader name="Text options" />
|
||||
<>
|
||||
<VariableLegend>Text options</VariableLegend>
|
||||
<VariableTextField
|
||||
value={query}
|
||||
name="Default value"
|
||||
placeholder="default value, if any"
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
labelWidth={20}
|
||||
grow
|
||||
width={30}
|
||||
testId={selectors.pages.Dashboard.Settings.Variables.Edit.TextBoxVariable.textBoxOptionsQueryInputV2}
|
||||
/>
|
||||
</VerticalGroup>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@@ -193,9 +193,10 @@ export function getVariableTypes(): Array<{ label: string; value: VariableType }
|
||||
return variableAdapters
|
||||
.list()
|
||||
.filter((v) => v.id !== 'system')
|
||||
.map(({ id, name }) => ({
|
||||
.map(({ id, name, description }) => ({
|
||||
label: name,
|
||||
value: id,
|
||||
description,
|
||||
}));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user