Variables: migrates data source variable type to React/Redux (#22770)

* Refactor: moves all the newVariables part to features/variables directory

* Feature: adds datasource type

* Tests: adds reducer tests

* Tests: covers data source actions with tests

* Chore: reduces strict null errors
This commit is contained in:
Hugo Häggmark 2020-03-16 06:32:04 +01:00 committed by GitHub
parent b30f4c7bb0
commit 1db067396a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 652 additions and 85 deletions

View File

@ -28,7 +28,7 @@ import {
SaveDashboardAsButtonConnected,
SaveDashboardButtonConnected,
} from '../features/dashboard/components/SaveDashboard/SaveDashboardButton';
import { VariableEditorContainer } from '../features/templating/editor/VariableEditorContainer';
import { VariableEditorContainer } from '../features/variables/editor/VariableEditorContainer';
export function registerAngularDirectives() {
react2AngularDirective('footer', Footer, []);

View File

@ -13,7 +13,7 @@ import usersReducers from 'app/features/users/state/reducers';
import userReducers from 'app/features/profile/state/reducers';
import organizationReducers from 'app/features/org/state/reducers';
import ldapReducers from 'app/features/admin/state/reducers';
import templatingReducers from 'app/features/templating/state/reducers';
import templatingReducers from 'app/features/variables/state/reducers';
const rootReducers = {
...sharedReducers,

View File

@ -1,7 +1,7 @@
import React, { PureComponent } from 'react';
import { connect, MapStateToProps } from 'react-redux';
import { StoreState } from '../../../../types';
import { getVariableClones } from '../../../templating/state/selectors';
import { getVariableClones } from '../../../variables/state/selectors';
import { VariableHide, VariableModel } from '../../../templating/variable';
import { DashboardModel } from '../../state';
import { AngularDashboardLinks } from './AngularDashboardLinks';

View File

@ -1,7 +1,7 @@
import React, { FunctionComponent, useEffect, useState } from 'react';
import { VariableHide, VariableModel } from '../../../templating/variable';
import { e2e } from '@grafana/e2e';
import { PickerRenderer } from '../../../templating/pickers/PickerRenderer';
import { PickerRenderer } from '../../../variables/pickers/PickerRenderer';
interface Props {
variables: VariableModel[];

View File

@ -15,8 +15,8 @@ import { UrlQueryValue } from '@grafana/runtime';
import { CoreEvents, DashboardMeta, KIOSK_MODE_TV } from 'app/types';
import { VariableModel } from '../../templating/variable';
import { getConfig } from '../../../core/config';
import { getVariables } from 'app/features/templating/state/selectors';
import { variableAdapters } from 'app/features/templating/adapters';
import { getVariables } from 'app/features/variables/state/selectors';
import { variableAdapters } from 'app/features/variables/adapters';
export interface CloneOptions {
saveVariables?: boolean;

View File

@ -23,8 +23,8 @@ import { DashboardDTO, DashboardRouteInfo, StoreState, ThunkDispatch, ThunkResul
import { DashboardModel } from './DashboardModel';
import { DataQuery } from '@grafana/data';
import { getConfig } from '../../../core/config';
import { initDashboardTemplating, processVariables } from '../../templating/state/actions';
import { variableAdapters } from '../../templating/adapters';
import { initDashboardTemplating, processVariables } from '../../variables/state/actions';
import { variableAdapters } from '../../variables/adapters';
export interface InitDashboardArgs {
$injector: any;

View File

@ -1,7 +1,7 @@
import { coreModule } from 'app/core/core';
import { VariableSrv } from 'app/features/templating/variable_srv';
import { getConfig } from '../../core/config';
import { getVariables } from '../templating/state/selectors';
import { getVariables } from '../variables/state/selectors';
const template = `
<div class="gf-form-select-wrapper max-width-18">

View File

@ -10,11 +10,12 @@ import { CustomVariable } from './custom_variable';
import { ConstantVariable } from './constant_variable';
import { AdhocVariable } from './adhoc_variable';
import { TextBoxVariable } from './TextBoxVariable';
import { variableAdapters } from './adapters';
import { createQueryVariableAdapter } from './query/adapter';
import { createCustomVariableAdapter } from './custom/adapter';
import { createTextBoxVariableAdapter } from './textbox/adapter';
import { createConstantVariableAdapter } from './constant/adapter';
import { variableAdapters } from '../variables/adapters';
import { createQueryVariableAdapter } from '../variables/query/adapter';
import { createCustomVariableAdapter } from '../variables/custom/adapter';
import { createTextBoxVariableAdapter } from '../variables/textbox/adapter';
import { createConstantVariableAdapter } from '../variables/constant/adapter';
import { createDataSourceVariableAdapter } from '../variables/datasource/adapter';
coreModule.factory('templateSrv', () => templateSrv);
@ -33,3 +34,4 @@ variableAdapters.set('query', createQueryVariableAdapter());
variableAdapters.set('custom', createCustomVariableAdapter());
variableAdapters.set('textbox', createTextBoxVariableAdapter());
variableAdapters.set('constant', createConstantVariableAdapter());
variableAdapters.set('datasource', createDataSourceVariableAdapter());

View File

@ -3,7 +3,7 @@ import _ from 'lodash';
import { variableRegex } from 'app/features/templating/variable';
import { escapeHtml } from 'app/core/utils/text';
import { ScopedVars, TimeRange } from '@grafana/data';
import { getVariableWithName } from './state/selectors';
import { getVariableWithName } from '../variables/state/selectors';
import { getState } from '../../store/store';
import { getConfig } from 'app/core/config';

View File

@ -2,7 +2,7 @@ import { ComponentType } from 'react';
import { Reducer } from 'redux';
import { UrlQueryValue } from '@grafana/runtime';
import { VariableModel, VariableOption, VariableType } from './variable';
import { VariableModel, VariableOption, VariableType } from '../templating/variable';
import { VariableEditorProps } from './editor/types';
import { VariablesState } from './state/variablesReducer';
import { VariablePickerProps } from './pickers/types';

View File

@ -1,7 +1,7 @@
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react';
import { e2e } from '@grafana/e2e';
import { ConstantVariableModel } from '../variable';
import { ConstantVariableModel } from '../../templating/variable';
import { VariableEditorProps } from '../editor/types';
export interface Props extends VariableEditorProps<ConstantVariableModel> {}

View File

@ -1,10 +1,10 @@
import { variableAdapters } from '../adapters';
import { createConstantVariableAdapter } from '../constant/adapter';
import { createConstantVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from 'app/features/templating/state/reducers';
import { TemplatingState } from 'app/features/variables/state/reducers';
import { updateConstantVariableOptions } from './actions';
import { getTemplatingRootReducer } from '../state/helpers';
import { ConstantVariableModel, VariableOption, VariableHide } from '../variable';
import { ConstantVariableModel, VariableHide, VariableOption } from '../../templating/variable';
import { toVariablePayload } from '../state/types';
import { createConstantOptionsFromQuery } from './reducer';
import { setCurrentVariableValue } from '../state/sharedReducer';

View File

@ -1,5 +1,5 @@
import cloneDeep from 'lodash/cloneDeep';
import { ConstantVariableModel } from '../variable';
import { ConstantVariableModel } from '../../templating/variable';
import { dispatch } from '../../../store/store';
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
import { VariableAdapter } from '../adapters';

View File

@ -4,7 +4,7 @@ import { getVariableTestContext } from '../state/helpers';
import { toVariablePayload } from '../state/types';
import { constantVariableReducer, createConstantOptionsFromQuery } from './reducer';
import { VariablesState } from '../state/variablesReducer';
import { ConstantVariableModel } from '../variable';
import { ConstantVariableModel } from '../../templating/variable';
import { createConstantVariableAdapter } from './adapter';
describe('constantVariableReducer', () => {

View File

@ -1,5 +1,5 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { ConstantVariableModel, VariableHide, VariableOption } from '../variable';
import { ConstantVariableModel, VariableHide, VariableOption } from '../../templating/variable';
import { EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
import { initialVariablesState, VariablesState } from '../state/variablesReducer';

View File

@ -1,5 +1,5 @@
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react';
import { CustomVariableModel, VariableWithMultiSupport } from '../variable';
import { CustomVariableModel, VariableWithMultiSupport } from '../../templating/variable';
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
import { OnPropChangeArguments, VariableEditorProps } from '../editor/types';

View File

@ -3,7 +3,7 @@ import { updateCustomVariableOptions } from './actions';
import { createCustomVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { getTemplatingRootReducer } from '../state/helpers';
import { VariableOption, VariableHide, CustomVariableModel } from '../variable';
import { CustomVariableModel, VariableHide, VariableOption } from '../../templating/variable';
import { toVariablePayload } from '../state/types';
import { setCurrentVariableValue } from '../state/sharedReducer';
import { initDashboardTemplating } from '../state/actions';

View File

@ -1,5 +1,5 @@
import cloneDeep from 'lodash/cloneDeep';
import { CustomVariableModel } from '../variable';
import { CustomVariableModel } from '../../templating/variable';
import { dispatch } from '../../../store/store';
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
import { VariableAdapter } from '../adapters';

View File

@ -1,11 +1,11 @@
import { reducerTester } from '../../../../test/core/redux/reducerTester';
import cloneDeep from 'lodash/cloneDeep';
import { getVariableTestContext } from '../state/helpers';
import { toVariablePayload, ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from '../state/types';
import { customVariableReducer, createCustomOptionsFromQuery } from './reducer';
import { createCustomVariableAdapter } from '../custom/adapter';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, toVariablePayload } from '../state/types';
import { createCustomOptionsFromQuery, customVariableReducer } from './reducer';
import { createCustomVariableAdapter } from './adapter';
import { VariablesState } from '../state/variablesReducer';
import { CustomVariableModel } from '../variable';
import { CustomVariableModel } from '../../templating/variable';
describe('customVariableReducer', () => {
const adapter = createCustomVariableAdapter();

View File

@ -1,6 +1,6 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { CustomVariableModel, VariableHide, VariableOption } from '../variable';
import { CustomVariableModel, VariableHide, VariableOption } from '../../templating/variable';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
import { initialVariablesState, VariablesState } from '../state/variablesReducer';

View File

@ -0,0 +1,131 @@
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react';
import { DataSourceVariableModel, VariableWithMultiSupport } from '../../templating/variable';
import { OnPropChangeArguments, VariableEditorProps } from '../editor/types';
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
import { FormLabel } from '@grafana/ui';
import { VariableEditorState } from '../editor/reducer';
import { DataSourceVariableEditorState } from './reducer';
import { initDataSourceVariableEditor } from './actions';
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
import { StoreState } from '../../../types';
import { connectWithStore } from '../../../core/utils/connectWithReduxStore';
export interface OwnProps extends VariableEditorProps<DataSourceVariableModel> {}
interface ConnectedProps {
editor: VariableEditorState<DataSourceVariableEditorState>;
}
interface DispatchProps {
initDataSourceVariableEditor: typeof initDataSourceVariableEditor;
}
type Props = OwnProps & ConnectedProps & DispatchProps;
export class DataSourceVariableEditorUnConnected extends PureComponent<Props> {
async componentDidMount() {
await this.props.initDataSourceVariableEditor();
}
onRegExChange = (event: ChangeEvent<HTMLInputElement>) => {
this.props.onPropChange({
propName: 'regex',
propValue: event.target.value,
});
};
onRegExBlur = (event: FocusEvent<HTMLInputElement>) => {
this.props.onPropChange({
propName: 'regex',
propValue: event.target.value,
updateOptions: true,
});
};
onSelectionOptionsChange = async ({ propValue, propName }: OnPropChangeArguments<VariableWithMultiSupport>) => {
this.props.onPropChange({ propName, propValue, updateOptions: true });
};
getSelectedDataSourceTypeValue = (): string => {
if (!this.props.editor.extended?.dataSourceTypes?.length) {
return '';
}
const foundItem = this.props.editor.extended?.dataSourceTypes.find(ds => ds.value === this.props.variable.query);
const value = foundItem ? foundItem.value : this.props.editor.extended?.dataSourceTypes[0].value;
return value ?? '';
};
onDataSourceTypeChanged = (event: ChangeEvent<HTMLSelectElement>) => {
this.props.onPropChange({ propName: 'query', propValue: event.target.value, updateOptions: true });
};
render() {
return (
<>
<div className="gf-form-group">
<h5 className="section-heading">Data source options</h5>
<div className="gf-form">
<label className="gf-form-label width-12">Type</label>
<div className="gf-form-select-wrapper max-width-18">
<select
className="gf-form-input"
value={this.getSelectedDataSourceTypeValue()}
onChange={this.onDataSourceTypeChanged}
>
{this.props.editor.extended?.dataSourceTypes?.length &&
this.props.editor.extended?.dataSourceTypes?.map(ds => (
<option key={ds.value ?? ''} value={ds.value ?? ''} label={ds.text}>
{ds.text}
</option>
))}
</select>
</div>
</div>
<div className="gf-form">
<FormLabel
width={12}
tooltip={
<div>
Regex filter for which data source instances to choose from in the variable value dropdown. Leave
empty for all.
<br />
<br />
Example: <code>/^prod/</code>
</div>
}
>
Instance name filter
</FormLabel>
<input
type="text"
className="gf-form-input max-width-18"
placeholder="/.*-(.*)-.*/"
value={this.props.variable.regex}
onChange={this.onRegExChange}
onBlur={this.onRegExBlur}
/>
</div>
</div>
<SelectionOptionsEditor variable={this.props.variable} onPropChange={this.onSelectionOptionsChange} />
</>
);
}
}
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = (state, ownProps) => ({
editor: state.templating.editor as VariableEditorState<DataSourceVariableEditorState>,
});
const mapDispatchToProps: MapDispatchToProps<DispatchProps, OwnProps> = {
initDataSourceVariableEditor,
};
export const DataSourceVariableEditor = connectWithStore(
DataSourceVariableEditorUnConnected,
mapStateToProps,
mapDispatchToProps
);

View File

@ -0,0 +1,175 @@
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from '../state/reducers';
import { getTemplatingRootReducer, variableMockBuilder } from '../state/helpers';
import { initDashboardTemplating } from '../state/actions';
import { toVariableIdentifier, toVariablePayload } from '../state/types';
import { variableAdapters } from '../adapters';
import { createDataSourceVariableAdapter } from './adapter';
import {
DataSourceVariableActionDependencies,
initDataSourceVariableEditor,
updateDataSourceVariableOptions,
} from './actions';
import { DataSourcePluginMeta, DataSourceSelectItem } from '@grafana/data';
import { getMockPlugin } from '../../plugins/__mocks__/pluginMocks';
import { createDataSourceOptions } from './reducer';
import { setCurrentVariableValue } from '../state/sharedReducer';
import { changeVariableEditorExtended } from '../editor/reducer';
describe('data source actions', () => {
variableAdapters.set('datasource', createDataSourceVariableAdapter());
describe('when updateDataSourceVariableOptions is dispatched', () => {
describe('and there is no regex', () => {
it('then the correct actions are dispatched', async () => {
const sources: DataSourceSelectItem[] = [
{
name: 'first-name',
value: 'first-value',
meta: getMockPlugin({ name: 'mock-data-name', id: 'mock-data-id' }),
sort: '',
},
{
name: 'second-name',
value: 'second-value',
meta: getMockPlugin({ name: 'mock-data-name', id: 'mock-data-id' }),
sort: '',
},
];
const getMetricSourcesMock = jest.fn().mockResolvedValue(sources);
const getDatasourceSrvMock = jest.fn().mockReturnValue({ getMetricSources: getMetricSourcesMock });
const dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrvMock };
const datasource = variableMockBuilder('datasource')
.withUuid('0')
.withQuery('mock-data-id')
.create();
const tester = await reduxTester<{ templating: TemplatingState }>()
.givenRootReducer(getTemplatingRootReducer())
.whenActionIsDispatched(initDashboardTemplating([datasource]))
.whenAsyncActionIsDispatched(
updateDataSourceVariableOptions(toVariableIdentifier(datasource), dependencies),
true
);
await tester.thenDispatchedActionShouldEqual(
createDataSourceOptions(
toVariablePayload({ type: 'datasource', uuid: '0' }, { sources, regex: (undefined as unknown) as RegExp })
),
setCurrentVariableValue(
toVariablePayload(
{ type: 'datasource', uuid: '0' },
{ option: { text: 'first-name', value: 'first-name', selected: false } }
)
)
);
expect(getMetricSourcesMock).toHaveBeenCalledTimes(1);
expect(getMetricSourcesMock).toHaveBeenCalledWith({ skipVariables: true });
expect(getDatasourceSrvMock).toHaveBeenCalledTimes(1);
});
});
describe('and there is a regex', () => {
it('then the correct actions are dispatched', async () => {
const sources: DataSourceSelectItem[] = [
{
name: 'first-name',
value: 'first-value',
meta: getMockPlugin({ name: 'mock-data-name', id: 'mock-data-id' }),
sort: '',
},
{
name: 'second-name',
value: 'second-value',
meta: getMockPlugin({ name: 'mock-data-name', id: 'mock-data-id' }),
sort: '',
},
];
const getMetricSourcesMock = jest.fn().mockResolvedValue(sources);
const getDatasourceSrvMock = jest.fn().mockReturnValue({ getMetricSources: getMetricSourcesMock });
const dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrvMock };
const datasource = variableMockBuilder('datasource')
.withUuid('0')
.withQuery('mock-data-id')
.withRegEx('/.*(second-name).*/')
.create();
const tester = await reduxTester<{ templating: TemplatingState }>()
.givenRootReducer(getTemplatingRootReducer())
.whenActionIsDispatched(initDashboardTemplating([datasource]))
.whenAsyncActionIsDispatched(
updateDataSourceVariableOptions(toVariableIdentifier(datasource), dependencies),
true
);
await tester.thenDispatchedActionShouldEqual(
createDataSourceOptions(
toVariablePayload({ type: 'datasource', uuid: '0' }, { sources, regex: /.*(second-name).*/ })
),
setCurrentVariableValue(
toVariablePayload(
{ type: 'datasource', uuid: '0' },
{ option: { text: 'second-name', value: 'second-name', selected: false } }
)
)
);
expect(getMetricSourcesMock).toHaveBeenCalledTimes(1);
expect(getMetricSourcesMock).toHaveBeenCalledWith({ skipVariables: true });
expect(getDatasourceSrvMock).toHaveBeenCalledTimes(1);
});
});
});
describe('when initDataSourceVariableEditor is dispatched', () => {
it('then the correct actions are dispatched', async () => {
const sources: DataSourceSelectItem[] = [
{
name: 'first-name',
value: 'first-value',
meta: getMockPlugin({ name: 'mock-data-name', id: 'mock-data-id' }),
sort: '',
},
{
name: 'second-name',
value: 'second-value',
meta: getMockPlugin({ name: 'mock-data-name', id: 'mock-data-id' }),
sort: '',
},
{
name: 'mixed-name',
value: 'mixed-value',
meta: getMockPlugin(({
name: 'mixed-data-name',
id: 'mixed-data-id',
mixed: true,
} as unknown) as DataSourcePluginMeta),
sort: '',
},
];
const getMetricSourcesMock = jest.fn().mockResolvedValue(sources);
const getDatasourceSrvMock = jest.fn().mockReturnValue({ getMetricSources: getMetricSourcesMock });
const dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrvMock };
const tester = await reduxTester<{ templating: TemplatingState }>()
.givenRootReducer(getTemplatingRootReducer())
.whenAsyncActionIsDispatched(initDataSourceVariableEditor(dependencies));
await tester.thenDispatchedActionShouldEqual(
changeVariableEditorExtended({
propName: 'dataSourceTypes',
propValue: [
{ text: '', value: '' },
{ text: 'mock-data-name', value: 'mock-data-id' },
],
})
);
expect(getMetricSourcesMock).toHaveBeenCalledTimes(1);
expect(getMetricSourcesMock).toHaveBeenCalledWith();
expect(getDatasourceSrvMock).toHaveBeenCalledTimes(1);
});
});
});

View File

@ -0,0 +1,54 @@
import { toVariablePayload, VariableIdentifier } from '../state/types';
import { ThunkResult } from '../../../types';
import { createDataSourceOptions } from './reducer';
import { validateVariableSelectionState } from '../state/actions';
import { DataSourceSelectItem, stringToJsRegex } from '@grafana/data';
import { getDatasourceSrv } from '../../plugins/datasource_srv';
import { getVariable } from '../state/selectors';
import { DataSourceVariableModel } from '../../templating/variable';
import templateSrv from '../../templating/template_srv';
import _ from 'lodash';
import { changeVariableEditorExtended } from '../editor/reducer';
export interface DataSourceVariableActionDependencies {
getDatasourceSrv: typeof getDatasourceSrv;
}
export const updateDataSourceVariableOptions = (
identifier: VariableIdentifier,
dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrv }
): ThunkResult<void> => async (dispatch, getState) => {
const sources = await dependencies.getDatasourceSrv().getMetricSources({ skipVariables: true });
const variableInState = getVariable<DataSourceVariableModel>(identifier.uuid!, getState());
let regex;
if (variableInState.regex) {
regex = templateSrv.replace(variableInState.regex, undefined, 'regex');
regex = stringToJsRegex(regex);
}
await dispatch(createDataSourceOptions(toVariablePayload(identifier, { sources, regex })));
await dispatch(validateVariableSelectionState(identifier));
};
export const initDataSourceVariableEditor = (
dependencies: DataSourceVariableActionDependencies = { getDatasourceSrv: getDatasourceSrv }
): ThunkResult<void> => async dispatch => {
const dataSources: DataSourceSelectItem[] = await dependencies.getDatasourceSrv().getMetricSources();
const filtered = dataSources.filter(ds => !ds.meta.mixed && ds.value !== null);
const dataSourceTypes = _(filtered)
.uniqBy('meta.id')
.map((ds: any) => {
return { text: ds.meta.name, value: ds.meta.id };
})
.value();
dataSourceTypes.unshift({ text: '', value: '' });
dispatch(
changeVariableEditorExtended({
propName: 'dataSourceTypes',
propValue: dataSourceTypes,
})
);
};

View File

@ -0,0 +1,46 @@
import cloneDeep from 'lodash/cloneDeep';
import { containsVariable, DataSourceVariableModel } from '../../templating/variable';
import { dispatch } from '../../../store/store';
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
import { VariableAdapter } from '../adapters';
import { dataSourceVariableReducer, initialDataSourceVariableModelState } from './reducer';
import { OptionsPicker } from '../pickers';
import { ALL_VARIABLE_TEXT, toVariableIdentifier } from '../state/types';
import { DataSourceVariableEditor } from './DataSourceVariableEditor';
import { updateDataSourceVariableOptions } from './actions';
export const createDataSourceVariableAdapter = (): VariableAdapter<DataSourceVariableModel> => {
return {
description: 'Enabled you to dynamically switch the datasource for multiple panels',
label: 'Datasource',
initialState: initialDataSourceVariableModelState,
reducer: dataSourceVariableReducer,
picker: OptionsPicker,
editor: DataSourceVariableEditor,
dependsOn: (variable, variableToTest) => {
if (variable.regex) {
return containsVariable(variable.regex, variableToTest.name);
}
return false;
},
setValue: async (variable, option, emitChanges = false) => {
await dispatch(setOptionAsCurrent(toVariableIdentifier(variable), option, emitChanges));
},
setValueFromUrl: async (variable, urlValue) => {
await dispatch(setOptionFromUrl(toVariableIdentifier(variable), urlValue));
},
updateOptions: async variable => {
await dispatch(updateDataSourceVariableOptions(toVariableIdentifier(variable)));
},
getSaveModel: variable => {
const { index, uuid, initLock, global, ...rest } = cloneDeep(variable);
return { ...rest, options: [] };
},
getValueForUrl: variable => {
if (variable.current.text === ALL_VARIABLE_TEXT) {
return ALL_VARIABLE_TEXT;
}
return variable.current.value;
},
};
};

View File

@ -0,0 +1,64 @@
import { reducerTester } from '../../../../test/core/redux/reducerTester';
import { VariablesState } from '../state/variablesReducer';
import { createDataSourceOptions, dataSourceVariableReducer } from './reducer';
import { DataSourceVariableModel } from '../../templating/variable';
import { getVariableTestContext } from '../state/helpers';
import cloneDeep from 'lodash/cloneDeep';
import { createDataSourceVariableAdapter } from './adapter';
import { DataSourceSelectItem } from '@grafana/data';
import { toVariablePayload } from '../state/types';
import { getMockPlugins } from '../../plugins/__mocks__/pluginMocks';
describe('dataSourceVariableReducer', () => {
const adapter = createDataSourceVariableAdapter();
describe('when createDataSourceOptions is dispatched', () => {
const plugins = getMockPlugins(3);
const sources: DataSourceSelectItem[] = plugins.map(p => ({
name: p.name,
value: `${p.name} value`,
meta: p,
sort: '',
}));
it.each`
query | regex | includeAll | expected
${sources[1].meta.id} | ${undefined} | ${false} | ${[{ text: 'pretty cool plugin-1', value: 'pretty cool plugin-1', selected: false }]}
${'not-found-plugin'} | ${undefined} | ${false} | ${[{ text: 'No data sources found', value: '', selected: false }]}
${sources[1].meta.id} | ${/.*(pretty cool plugin-1).*/} | ${false} | ${[{ text: 'pretty cool plugin-1', value: 'pretty cool plugin-1', selected: false }]}
${sources[1].meta.id} | ${/.*(pretty cool plugin-2).*/} | ${false} | ${[{ text: 'No data sources found', value: '', selected: false }]}
${sources[1].meta.id} | ${undefined} | ${true} | ${[
{ text: 'All', value: '$__all', selected: false },
{ text: 'pretty cool plugin-1', value: 'pretty cool plugin-1', selected: false },
]}
${'not-found-plugin'} | ${undefined} | ${true} | ${[
{ text: 'All', value: '$__all', selected: false },
{ text: 'No data sources found', value: '', selected: false },
]}
${sources[1].meta.id} | ${/.*(pretty cool plugin-1).*/} | ${true} | ${[
{ text: 'All', value: '$__all', selected: false },
{ text: 'pretty cool plugin-1', value: 'pretty cool plugin-1', selected: false },
]}
${sources[1].meta.id} | ${/.*(pretty cool plugin-2).*/} | ${true} | ${[
{ text: 'All', value: '$__all', selected: false },
{ text: 'No data sources found', value: '', selected: false },
]}
`(
"when called with query: '$query' and regex: '$regex' and includeAll: '$includeAll' then state should be correct",
({ query, regex, includeAll, expected }) => {
const { initialState } = getVariableTestContext<DataSourceVariableModel>(adapter, { query, includeAll });
const payload = toVariablePayload({ uuid: '0', type: 'datasource' }, { sources, regex });
reducerTester<VariablesState>()
.givenReducer(dataSourceVariableReducer, cloneDeep(initialState))
.whenActionIsDispatched(createDataSourceOptions(payload))
.thenStateShouldEqual({
...initialState,
['0']: ({
...initialState['0'],
options: expected,
} as unknown) as DataSourceVariableModel,
});
}
);
});
});

View File

@ -0,0 +1,70 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { DataSourceVariableModel, VariableHide, VariableOption, VariableRefresh } from '../../templating/variable';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
import { initialVariablesState, VariablesState } from '../state/variablesReducer';
import { DataSourceSelectItem } from '@grafana/data';
export interface DataSourceVariableEditorState {
dataSourceTypes: Array<{ text: string; value: string }>;
}
export const initialDataSourceVariableModelState: DataSourceVariableModel = {
uuid: EMPTY_UUID,
global: false,
type: 'datasource',
name: '',
hide: VariableHide.dontHide,
label: '',
current: {} as VariableOption,
regex: '',
options: [],
query: '',
multi: false,
includeAll: false,
refresh: VariableRefresh.onDashboardLoad,
skipUrlSync: false,
index: -1,
initLock: null,
};
export const dataSourceVariableSlice = createSlice({
name: 'templating/datasource',
initialState: initialVariablesState,
reducers: {
createDataSourceOptions: (
state: VariablesState,
action: PayloadAction<VariablePayload<{ sources: DataSourceSelectItem[]; regex: RegExp | undefined }>>
) => {
const { sources, regex } = action.payload.data;
const options: VariableOption[] = [];
const instanceState = getInstanceState<DataSourceVariableModel>(state, action.payload.uuid);
for (let i = 0; i < sources.length; i++) {
const source = sources[i];
// must match on type
if (source.meta.id !== instanceState.query) {
continue;
}
if (regex && !regex.exec(source.name)) {
continue;
}
options.push({ text: source.name, value: source.name, selected: false });
}
if (options.length === 0) {
options.push({ text: 'No data sources found', value: '', selected: false });
}
if (instanceState.includeAll) {
options.unshift({ text: ALL_VARIABLE_TEXT, value: ALL_VARIABLE_VALUE, selected: false });
}
instanceState.options = options;
},
},
});
export const dataSourceVariableReducer = dataSourceVariableSlice.reducer;
export const { createDataSourceOptions } = dataSourceVariableSlice.actions;

View File

@ -2,7 +2,7 @@ import React, { FunctionComponent, useCallback } from 'react';
import { Switch } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { VariableWithMultiSupport } from '../variable';
import { VariableWithMultiSupport } from '../../templating/variable';
import { VariableEditorProps } from './types';
export interface SelectionOptionsEditorProps<Model extends VariableWithMultiSupport = VariableWithMultiSupport>

View File

@ -7,7 +7,7 @@ import { VariableEditorEditor } from './VariableEditorEditor';
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
import { connectWithStore } from '../../../core/utils/connectWithReduxStore';
import { getVariableClones } from '../state/selectors';
import { VariableModel } from '../variable';
import { VariableModel } from '../../templating/variable';
import { switchToEditMode, switchToListMode, switchToNewMode } from './actions';
import { changeVariableOrder, duplicateVariable, removeVariable } from '../state/sharedReducer';
@ -53,7 +53,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
};
onDuplicateVariable = (identifier: VariableIdentifier) => {
this.props.duplicateVariable(toVariablePayload(identifier));
this.props.duplicateVariable(toVariablePayload(identifier, { newUuid: (undefined as unknown) as string }));
};
onRemoveVariable = (identifier: VariableIdentifier) => {

View File

@ -5,7 +5,7 @@ import { FormLabel } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { variableAdapters } from '../adapters';
import { EMPTY_UUID, toVariablePayload, VariableIdentifier } from '../state/types';
import { VariableHide, VariableModel, VariableType } from '../variable';
import { VariableHide, VariableModel, VariableType } from '../../templating/variable';
import { appEvents } from '../../../core/core';
import { VariableValuesPreview } from './VariableValuesPreview';
import { changeVariableName, onEditorAdd, onEditorUpdate, variableEditorMount, variableEditorUnMount } from './actions';

View File

@ -1,7 +1,7 @@
import React, { MouseEvent, PureComponent } from 'react';
import { e2e } from '@grafana/e2e';
import EmptyListCTA from '../../../core/components/EmptyListCTA/EmptyListCTA';
import { QueryVariableModel, VariableModel } from '../variable';
import { QueryVariableModel, VariableModel } from '../../templating/variable';
import { toVariableIdentifier, VariableIdentifier } from '../state/types';
export interface Props {

View File

@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useState } from 'react';
import { VariableModel, VariableOption, VariableWithOptions } from '../variable';
import { VariableModel, VariableOption, VariableWithOptions } from '../../templating/variable';
import { e2e } from '@grafana/e2e';
export interface VariableValuesPreviewProps {

View File

@ -12,7 +12,7 @@ import { variableAdapters } from '../adapters';
import { v4 } from 'uuid';
import { AddVariable, EMPTY_UUID, toVariablePayload, VariableIdentifier } from '../state/types';
import cloneDeep from 'lodash/cloneDeep';
import { VariableType } from '../variable';
import { VariableType } from '../../templating/variable';
import { addVariable, removeVariable, storeNewVariable } from '../state/sharedReducer';
export const variableEditorMount = (identifier: VariableIdentifier): ThunkResult<void> => {

View File

@ -1,4 +1,4 @@
import { VariableModel } from '../variable';
import { VariableModel } from '../../templating/variable';
export interface OnPropChangeArguments<Model extends VariableModel = VariableModel> {
propName: keyof Model;

View File

@ -1,4 +1,4 @@
import { VariableModel, QueryVariableModel } from './variable';
import { QueryVariableModel, VariableModel } from '../templating/variable';
export const isQuery = (model: VariableModel): model is QueryVariableModel => {
return model.type === 'query';

View File

@ -6,7 +6,12 @@ import { VariableLink } from '../shared/VariableLink';
import { VariableInput } from '../shared/VariableInput';
import { commitChangesToVariable, filterOrSearchOptions, navigateOptions, toggleAndFetchTag } from './actions';
import { OptionsPickerState, showOptions, toggleAllOptions, toggleOption } from './reducer';
import { VariableOption, VariableTag, VariableWithMultiSupport, VariableWithOptions } from '../../variable';
import {
VariableOption,
VariableTag,
VariableWithMultiSupport,
VariableWithOptions,
} from '../../../templating/variable';
import { VariableOptions } from '../shared/VariableOptions';
import { isQuery } from '../../guard';
import { VariablePickerProps } from '../types';

View File

@ -2,7 +2,7 @@ import { reduxTester } from '../../../../../test/core/redux/reduxTester';
import { getTemplatingRootReducer } from '../../state/helpers';
import { initDashboardTemplating } from '../../state/actions';
import { TemplatingState } from '../../state/reducers';
import { QueryVariableModel, VariableHide, VariableRefresh, VariableSort } from '../../variable';
import { QueryVariableModel, VariableHide, VariableRefresh, VariableSort } from '../../../templating/variable';
import {
hideOptions,
showOptions,
@ -374,8 +374,7 @@ describe('options picker actions', () => {
const variable = createVariable({ options, includeAll: false, tags: [tag] });
datasource.metricFindQuery.mockReset();
// @ts-ignore couldn't wrap my head around this
// error TS2345: Argument of type '() => Promise<{ value: string; text: string; }[]>' is not assignable to parameter of type '() => Promise<never[]>'.
// @ts-ignore strict null error TS2345: Argument of type '() => Promise<{ value: string; text: string; }[]>' is not assignable to parameter of type '() => Promise<never[]>'
datasource.metricFindQuery.mockImplementation(() => Promise.resolve(values));
const tester = await reduxTester<{ templating: TemplatingState }>()

View File

@ -8,7 +8,7 @@ import {
VariableTag,
VariableWithMultiSupport,
VariableWithOptions,
} from '../../variable';
} from '../../../templating/variable';
import { variableAdapters } from '../../adapters';
import { getVariable } from '../../state/selectors';
import { NavigationKey } from '../types';
@ -24,7 +24,7 @@ import {
} from './reducer';
import { getDataSourceSrv } from '@grafana/runtime';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { setCurrentVariableValue, changeVariableProp } from '../../state/sharedReducer';
import { changeVariableProp, setCurrentVariableValue } from '../../state/sharedReducer';
import { toVariablePayload } from '../../state/types';
export const navigateOptions = (key: NavigationKey, clearOthers: boolean): ThunkResult<void> => {

View File

@ -14,7 +14,7 @@ import {
updateSearchQuery,
} from './reducer';
import { reducerTester } from '../../../../../test/core/redux/reducerTester';
import { QueryVariableModel, VariableTag } from '../../variable';
import { QueryVariableModel, VariableTag } from '../../../templating/variable';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from '../../state/types';
const getVariableTestContext = (extend: Partial<OptionsPickerState>) => {

View File

@ -1,6 +1,11 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { cloneDeep } from 'lodash';
import { containsSearchFilter, VariableOption, VariableTag, VariableWithMultiSupport } from '../../variable';
import {
containsSearchFilter,
VariableOption,
VariableTag,
VariableWithMultiSupport,
} from '../../../templating/variable';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from '../../state/types';
import { isQuery } from '../../guard';
import { applyStateChanges } from '../../../../core/utils/applyStateChanges';

View File

@ -1,5 +1,5 @@
import React, { FunctionComponent, useMemo } from 'react';
import { VariableHide, VariableModel } from '../variable';
import { VariableHide, VariableModel } from '../../templating/variable';
import { e2e } from '@grafana/e2e';
import { variableAdapters } from '../adapters';

View File

@ -1,7 +1,7 @@
import React, { PureComponent } from 'react';
import { getTagColorsFromName } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { VariableTag } from '../../variable';
import { VariableTag } from '../../../templating/variable';
interface Props {
onClick: () => void;

View File

@ -1,7 +1,7 @@
import React, { PureComponent } from 'react';
import { getTagColorsFromName, Tooltip } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import { VariableOption, VariableTag } from '../../variable';
import { VariableOption, VariableTag } from '../../../templating/variable';
export interface Props {
multi: boolean;

View File

@ -1,4 +1,4 @@
import { VariableModel } from '../variable';
import { VariableModel } from '../../templating/variable';
export interface VariablePickerProps<Model extends VariableModel = VariableModel> {
variable: Model;

View File

@ -2,9 +2,9 @@ import React, { ChangeEvent, PureComponent } from 'react';
import { e2e } from '@grafana/e2e';
import { FormLabel, Switch } from '@grafana/ui';
import templateSrv from '../template_srv';
import templateSrv from '../../templating/template_srv';
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
import { QueryVariableModel, VariableRefresh, VariableSort, VariableWithMultiSupport } from '../variable';
import { QueryVariableModel, VariableRefresh, VariableSort, VariableWithMultiSupport } from '../../templating/variable';
import { QueryVariableEditorState } from './reducer';
import { changeQueryVariableDataSource, changeQueryVariableQuery, initQueryVariableEditor } from './actions';
import { VariableEditorState } from '../editor/reducer';

View File

@ -2,7 +2,7 @@ import { variableAdapters } from '../adapters';
import { createQueryVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { getTemplatingRootReducer } from '../state/helpers';
import { QueryVariableModel, VariableHide, VariableRefresh, VariableSort } from '../variable';
import { QueryVariableModel, VariableHide, VariableRefresh, VariableSort } from '../../templating/variable';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, toVariablePayload } from '../state/types';
import { changeVariableProp, setCurrentVariableValue } from '../state/sharedReducer';
import { initDashboardTemplating } from '../state/actions';
@ -20,7 +20,7 @@ import {
removeVariableEditorError,
setIdInEditor,
} from '../editor/reducer';
import DefaultVariableQueryEditor from '../DefaultVariableQueryEditor';
import DefaultVariableQueryEditor from '../../templating/DefaultVariableQueryEditor';
import { expect } from 'test/lib/common';
const mocks: Record<string, any> = {

View File

@ -1,13 +1,13 @@
import { AppEvents, DataSourcePluginMeta, DataSourceSelectItem } from '@grafana/data';
import { validateVariableSelectionState } from '../state/actions';
import { QueryVariableModel, VariableRefresh } from '../variable';
import { QueryVariableModel, VariableRefresh } from '../../templating/variable';
import { ThunkResult } from '../../../types';
import { getDatasourceSrv } from '../../plugins/datasource_srv';
import { getTimeSrv } from '../../dashboard/services/TimeSrv';
import appEvents from '../../../core/app_events';
import { importDataSourcePlugin } from '../../plugins/plugin_loader';
import DefaultVariableQueryEditor from '../DefaultVariableQueryEditor';
import DefaultVariableQueryEditor from '../../templating/DefaultVariableQueryEditor';
import { getVariable } from '../state/selectors';
import { addVariableEditorError, changeVariableEditorExtended, removeVariableEditorError } from '../editor/reducer';
import { variableAdapters } from '../adapters';

View File

@ -1,6 +1,6 @@
import cloneDeep from 'lodash/cloneDeep';
import { containsVariable, QueryVariableModel, VariableRefresh } from '../variable';
import { containsVariable, QueryVariableModel, VariableRefresh } from '../../templating/variable';
import { initialQueryVariableModelState, queryVariableReducer } from './reducer';
import { dispatch } from '../../../store/store';
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';

View File

@ -1,6 +1,6 @@
import { reducerTester } from '../../../../test/core/redux/reducerTester';
import { queryVariableReducer, updateVariableOptions, updateVariableTags } from './reducer';
import { QueryVariableModel, VariableOption } from '../variable';
import { QueryVariableModel, VariableOption } from '../../templating/variable';
import cloneDeep from 'lodash/cloneDeep';
import { VariablesState } from '../state/variablesReducer';
import { getVariableTestContext } from '../state/helpers';

View File

@ -9,8 +9,8 @@ import {
VariableRefresh,
VariableSort,
VariableTag,
} from '../variable';
import templateSrv from '../template_srv';
} from '../../templating/variable';
import templateSrv from '../../templating/template_srv';
import {
ALL_VARIABLE_TEXT,
ALL_VARIABLE_VALUE,

View File

@ -7,7 +7,7 @@ import { createCustomVariableAdapter } from '../custom/adapter';
import { createTextBoxVariableAdapter } from '../textbox/adapter';
import { createConstantVariableAdapter } from '../constant/adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from 'app/features/templating/state/reducers';
import { TemplatingState } from 'app/features/variables/state/reducers';
import { initDashboardTemplating, processVariables, setOptionFromUrl, validateVariableSelectionState } from './actions';
import { addInitLock, addVariable, removeInitLock, resolveInitLock, setCurrentVariableValue } from './sharedReducer';
import { toVariableIdentifier, toVariablePayload } from './types';

View File

@ -1,7 +1,13 @@
import castArray from 'lodash/castArray';
import { UrlQueryMap, UrlQueryValue } from '@grafana/runtime';
import { QueryVariableModel, VariableModel, VariableOption, VariableRefresh, VariableWithOptions } from '../variable';
import {
QueryVariableModel,
VariableModel,
VariableOption,
VariableRefresh,
VariableWithOptions,
} from '../../templating/variable';
import { StoreState, ThunkResult } from '../../../types';
import { getVariable, getVariables } from './selectors';
import { variableAdapters } from '../adapters';

View File

@ -2,7 +2,7 @@ import { combineReducers } from '@reduxjs/toolkit';
import cloneDeep from 'lodash/cloneDeep';
import { EMPTY_UUID } from './types';
import { VariableHide, VariableModel, VariableRefresh, VariableType } from '../variable';
import { VariableHide, VariableModel, VariableRefresh, VariableType } from '../../templating/variable';
import { variablesReducer, VariablesState } from './variablesReducer';
import { optionsPickerReducer } from '../pickers/OptionsPicker/reducer';
import { variableEditorReducer } from '../editor/reducer';
@ -106,6 +106,11 @@ export const variableMockBuilder = (type: VariableType) => {
return instance;
};
const withRegEx = (regex: any) => {
model.regex = regex;
return instance;
};
const create = () => model;
const instance = {
@ -116,6 +121,7 @@ export const variableMockBuilder = (type: VariableType) => {
withRefresh,
withQuery,
withMulti,
withRegEx,
create,
};

View File

@ -5,11 +5,11 @@ import { variableAdapters } from '../adapters';
import { createQueryVariableAdapter } from '../query/adapter';
import { createCustomVariableAdapter } from '../custom/adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from 'app/features/templating/state/reducers';
import { TemplatingState } from 'app/features/variables/state/reducers';
import { initDashboardTemplating, processVariable } from './actions';
import { resolveInitLock, setCurrentVariableValue } from './sharedReducer';
import { toVariableIdentifier, toVariablePayload } from './types';
import { VariableRefresh } from '../variable';
import { VariableRefresh } from '../../templating/variable';
import { updateVariableOptions } from '../query/reducer';
jest.mock('app/features/dashboard/services/TimeSrv', () => ({

View File

@ -1,6 +1,6 @@
import { reducerTester } from '../../../../test/core/redux/reducerTester';
import { cleanUpDashboard } from 'app/features/dashboard/state/reducers';
import { VariableHide, VariableModel } from '../variable';
import { VariableHide, VariableModel } from '../../templating/variable';
import { VariableAdapter, variableAdapters } from '../adapters';
import { createAction } from '@reduxjs/toolkit';
import { variablesReducer, VariablesState } from './variablesReducer';

View File

@ -2,7 +2,7 @@ import { combineReducers } from '@reduxjs/toolkit';
import { optionsPickerReducer, OptionsPickerState } from '../pickers/OptionsPicker/reducer';
import { variableEditorReducer, VariableEditorState } from '../editor/reducer';
import { variablesReducer } from './variablesReducer';
import { VariableModel } from '../variable';
import { VariableModel } from '../../templating/variable';
export interface TemplatingState {
variables: Record<string, VariableModel>;

View File

@ -1,7 +1,7 @@
import { cloneDeep } from 'lodash';
import { StoreState } from '../../../types';
import { VariableModel } from '../variable';
import { VariableModel } from '../../templating/variable';
import { getState } from '../../../store/store';
import { EMPTY_UUID } from './types';

View File

@ -14,7 +14,7 @@ import {
sharedReducer,
storeNewVariable,
} from './sharedReducer';
import { QueryVariableModel, VariableHide } from '../variable';
import { QueryVariableModel, VariableHide } from '../../templating/variable';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, EMPTY_UUID, toVariablePayload } from './types';
import { variableAdapters } from '../adapters';
import { createQueryVariableAdapter } from '../query/adapter';

View File

@ -1,7 +1,7 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import cloneDeep from 'lodash/cloneDeep';
import { VariableModel, VariableOption, VariableType, VariableWithOptions } from '../variable';
import { VariableModel, VariableOption, VariableType, VariableWithOptions } from '../../templating/variable';
import { AddVariable, ALL_VARIABLE_VALUE, EMPTY_UUID, getInstanceState, VariablePayload } from './types';
import { variableAdapters } from '../adapters';
import { changeVariableNameSucceeded } from '../editor/reducer';
@ -100,8 +100,12 @@ const sharedReducerSlice = createSlice({
},
setCurrentVariableValue: (
state: VariablesState,
action: PayloadAction<VariablePayload<{ option: VariableOption }>>
action: PayloadAction<VariablePayload<{ option: VariableOption | undefined }>>
) => {
if (!action.payload.data.option) {
return;
}
const instanceState = getInstanceState<VariableWithOptions>(state, action.payload.uuid);
const current = { ...action.payload.data.option };

View File

@ -1,4 +1,4 @@
import { VariableModel, VariableType } from '../variable';
import { VariableModel, VariableType } from '../../templating/variable';
import { VariablesState } from './variablesReducer';
export const EMPTY_UUID = '00000000-0000-0000-0000-000000000000';

View File

@ -2,7 +2,7 @@ import { PayloadAction } from '@reduxjs/toolkit';
import { cleanUpDashboard } from '../../dashboard/state/reducers';
import { variableAdapters } from '../adapters';
import { sharedReducer } from './sharedReducer';
import { VariableModel } from '../variable';
import { VariableModel } from '../../templating/variable';
import { VariablePayload } from './types';
export interface VariablesState extends Record<string, VariableModel> {}

View File

@ -1,5 +1,5 @@
import React, { ChangeEvent, PureComponent } from 'react';
import { TextBoxVariableModel } from '../variable';
import { TextBoxVariableModel } from '../../templating/variable';
import { VariableEditorProps } from '../editor/types';
export interface Props extends VariableEditorProps<TextBoxVariableModel> {}

View File

@ -1,6 +1,6 @@
import React, { ChangeEvent, FocusEvent, KeyboardEvent, PureComponent } from 'react';
import { TextBoxVariableModel } from '../variable';
import { TextBoxVariableModel } from '../../templating/variable';
import { toVariablePayload } from '../state/types';
import { dispatch } from '../../../store/store';
import { variableAdapters } from '../adapters';

View File

@ -1,10 +1,10 @@
import { variableAdapters } from '../adapters';
import { createTextBoxVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from 'app/features/templating/state/reducers';
import { TemplatingState } from 'app/features/variables/state/reducers';
import { updateTextBoxVariableOptions } from './actions';
import { getTemplatingRootReducer } from '../state/helpers';
import { VariableOption, VariableHide, TextBoxVariableModel } from '../variable';
import { TextBoxVariableModel, VariableHide, VariableOption } from '../../templating/variable';
import { toVariablePayload } from '../state/types';
import { createTextBoxOptions } from './reducer';
import { setCurrentVariableValue } from '../state/sharedReducer';

View File

@ -1,4 +1,4 @@
import { TextBoxVariableModel } from '../variable';
import { TextBoxVariableModel } from '../../templating/variable';
import { ThunkResult } from '../../../types';
import { getVariable } from '../state/selectors';
import { variableAdapters } from '../adapters';

View File

@ -1,6 +1,6 @@
import cloneDeep from 'lodash/cloneDeep';
import { TextBoxVariableModel } from '../variable';
import { TextBoxVariableModel } from '../../templating/variable';
import { initialTextBoxVariableModelState, textBoxVariableReducer } from './reducer';
import { dispatch } from '../../../store/store';
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';

View File

@ -2,9 +2,9 @@ import { reducerTester } from '../../../../test/core/redux/reducerTester';
import cloneDeep from 'lodash/cloneDeep';
import { getVariableTestContext } from '../state/helpers';
import { toVariablePayload } from '../state/types';
import { textBoxVariableReducer, createTextBoxOptions } from './reducer';
import { createTextBoxOptions, textBoxVariableReducer } from './reducer';
import { VariablesState } from '../state/variablesReducer';
import { TextBoxVariableModel } from '../variable';
import { TextBoxVariableModel } from '../../templating/variable';
import { createTextBoxVariableAdapter } from './adapter';
describe('textBoxVariableReducer', () => {

View File

@ -1,6 +1,6 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { TextBoxVariableModel, VariableHide, VariableOption } from '../variable';
import { TextBoxVariableModel, VariableHide, VariableOption } from '../../templating/variable';
import { EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types';
import { initialVariablesState, VariablesState } from '../state/variablesReducer';

View File

@ -18,7 +18,7 @@ import { LdapState } from './ldap';
import { PanelEditorState } from '../features/dashboard/panel_editor/state/reducers';
import { PanelEditorStateNew } from '../features/dashboard/components/PanelEditor/state/reducers';
import { ApiKeysState } from './apiKeys';
import { TemplatingState } from '../features/templating/state/reducers';
import { TemplatingState } from '../features/variables/state/reducers';
export interface StoreState {
navIndex: NavIndex;