mirror of https://github.com/grafana/grafana
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 errorspull/22144/head^2
parent
b30f4c7bb0
commit
1db067396a
@ -1,7 +1,7 @@ |
|||||||
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react'; |
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react'; |
||||||
import { e2e } from '@grafana/e2e'; |
import { e2e } from '@grafana/e2e'; |
||||||
|
|
||||||
import { ConstantVariableModel } from '../variable'; |
import { ConstantVariableModel } from '../../templating/variable'; |
||||||
import { VariableEditorProps } from '../editor/types'; |
import { VariableEditorProps } from '../editor/types'; |
||||||
|
|
||||||
export interface Props extends VariableEditorProps<ConstantVariableModel> {} |
export interface Props extends VariableEditorProps<ConstantVariableModel> {} |
@ -1,10 +1,10 @@ |
|||||||
import { variableAdapters } from '../adapters'; |
import { variableAdapters } from '../adapters'; |
||||||
import { createConstantVariableAdapter } from '../constant/adapter'; |
import { createConstantVariableAdapter } from './adapter'; |
||||||
import { reduxTester } from '../../../../test/core/redux/reduxTester'; |
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 { updateConstantVariableOptions } from './actions'; |
||||||
import { getTemplatingRootReducer } from '../state/helpers'; |
import { getTemplatingRootReducer } from '../state/helpers'; |
||||||
import { ConstantVariableModel, VariableOption, VariableHide } from '../variable'; |
import { ConstantVariableModel, VariableHide, VariableOption } from '../../templating/variable'; |
||||||
import { toVariablePayload } from '../state/types'; |
import { toVariablePayload } from '../state/types'; |
||||||
import { createConstantOptionsFromQuery } from './reducer'; |
import { createConstantOptionsFromQuery } from './reducer'; |
||||||
import { setCurrentVariableValue } from '../state/sharedReducer'; |
import { setCurrentVariableValue } from '../state/sharedReducer'; |
@ -1,5 +1,5 @@ |
|||||||
import cloneDeep from 'lodash/cloneDeep'; |
import cloneDeep from 'lodash/cloneDeep'; |
||||||
import { ConstantVariableModel } from '../variable'; |
import { ConstantVariableModel } from '../../templating/variable'; |
||||||
import { dispatch } from '../../../store/store'; |
import { dispatch } from '../../../store/store'; |
||||||
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions'; |
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions'; |
||||||
import { VariableAdapter } from '../adapters'; |
import { VariableAdapter } from '../adapters'; |
@ -1,5 +1,5 @@ |
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; |
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 { EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types'; |
||||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer'; |
import { initialVariablesState, VariablesState } from '../state/variablesReducer'; |
||||||
|
|
@ -1,5 +1,5 @@ |
|||||||
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react'; |
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react'; |
||||||
import { CustomVariableModel, VariableWithMultiSupport } from '../variable'; |
import { CustomVariableModel, VariableWithMultiSupport } from '../../templating/variable'; |
||||||
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor'; |
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor'; |
||||||
import { OnPropChangeArguments, VariableEditorProps } from '../editor/types'; |
import { OnPropChangeArguments, VariableEditorProps } from '../editor/types'; |
||||||
|
|
@ -1,5 +1,5 @@ |
|||||||
import cloneDeep from 'lodash/cloneDeep'; |
import cloneDeep from 'lodash/cloneDeep'; |
||||||
import { CustomVariableModel } from '../variable'; |
import { CustomVariableModel } from '../../templating/variable'; |
||||||
import { dispatch } from '../../../store/store'; |
import { dispatch } from '../../../store/store'; |
||||||
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions'; |
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions'; |
||||||
import { VariableAdapter } from '../adapters'; |
import { VariableAdapter } from '../adapters'; |
@ -1,11 +1,11 @@ |
|||||||
import { reducerTester } from '../../../../test/core/redux/reducerTester'; |
import { reducerTester } from '../../../../test/core/redux/reducerTester'; |
||||||
import cloneDeep from 'lodash/cloneDeep'; |
import cloneDeep from 'lodash/cloneDeep'; |
||||||
import { getVariableTestContext } from '../state/helpers'; |
import { getVariableTestContext } from '../state/helpers'; |
||||||
import { toVariablePayload, ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from '../state/types'; |
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, toVariablePayload } from '../state/types'; |
||||||
import { customVariableReducer, createCustomOptionsFromQuery } from './reducer'; |
import { createCustomOptionsFromQuery, customVariableReducer } from './reducer'; |
||||||
import { createCustomVariableAdapter } from '../custom/adapter'; |
import { createCustomVariableAdapter } from './adapter'; |
||||||
import { VariablesState } from '../state/variablesReducer'; |
import { VariablesState } from '../state/variablesReducer'; |
||||||
import { CustomVariableModel } from '../variable'; |
import { CustomVariableModel } from '../../templating/variable'; |
||||||
|
|
||||||
describe('customVariableReducer', () => { |
describe('customVariableReducer', () => { |
||||||
const adapter = createCustomVariableAdapter(); |
const adapter = createCustomVariableAdapter(); |
@ -1,6 +1,6 @@ |
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; |
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 { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types'; |
||||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer'; |
import { initialVariablesState, VariablesState } from '../state/variablesReducer'; |
||||||
|
|
@ -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 |
||||||
|
); |
@ -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); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
@ -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, |
||||||
|
}) |
||||||
|
); |
||||||
|
}; |
@ -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; |
||||||
|
}, |
||||||
|
}; |
||||||
|
}; |
@ -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, |
||||||
|
}); |
||||||
|
} |
||||||
|
); |
||||||
|
}); |
||||||
|
}); |
@ -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; |
@ -1,7 +1,7 @@ |
|||||||
import React, { MouseEvent, PureComponent } from 'react'; |
import React, { MouseEvent, PureComponent } from 'react'; |
||||||
import { e2e } from '@grafana/e2e'; |
import { e2e } from '@grafana/e2e'; |
||||||
import EmptyListCTA from '../../../core/components/EmptyListCTA/EmptyListCTA'; |
import EmptyListCTA from '../../../core/components/EmptyListCTA/EmptyListCTA'; |
||||||
import { QueryVariableModel, VariableModel } from '../variable'; |
import { QueryVariableModel, VariableModel } from '../../templating/variable'; |
||||||
import { toVariableIdentifier, VariableIdentifier } from '../state/types'; |
import { toVariableIdentifier, VariableIdentifier } from '../state/types'; |
||||||
|
|
||||||
export interface Props { |
export interface Props { |
@ -1,5 +1,5 @@ |
|||||||
import React, { useCallback, useEffect, useState } from 'react'; |
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'; |
import { e2e } from '@grafana/e2e'; |
||||||
|
|
||||||
export interface VariableValuesPreviewProps { |
export interface VariableValuesPreviewProps { |
@ -1,4 +1,4 @@ |
|||||||
import { VariableModel } from '../variable'; |
import { VariableModel } from '../../templating/variable'; |
||||||
|
|
||||||
export interface OnPropChangeArguments<Model extends VariableModel = VariableModel> { |
export interface OnPropChangeArguments<Model extends VariableModel = VariableModel> { |
||||||
propName: keyof Model; |
propName: keyof Model; |
@ -1,4 +1,4 @@ |
|||||||
import { VariableModel, QueryVariableModel } from './variable'; |
import { QueryVariableModel, VariableModel } from '../templating/variable'; |
||||||
|
|
||||||
export const isQuery = (model: VariableModel): model is QueryVariableModel => { |
export const isQuery = (model: VariableModel): model is QueryVariableModel => { |
||||||
return model.type === 'query'; |
return model.type === 'query'; |
@ -1,6 +1,11 @@ |
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; |
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; |
||||||
import { cloneDeep } from 'lodash'; |
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 { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from '../../state/types'; |
||||||
import { isQuery } from '../../guard'; |
import { isQuery } from '../../guard'; |
||||||
import { applyStateChanges } from '../../../../core/utils/applyStateChanges'; |
import { applyStateChanges } from '../../../../core/utils/applyStateChanges'; |
@ -1,5 +1,5 @@ |
|||||||
import React, { FunctionComponent, useMemo } from 'react'; |
import React, { FunctionComponent, useMemo } from 'react'; |
||||||
import { VariableHide, VariableModel } from '../variable'; |
import { VariableHide, VariableModel } from '../../templating/variable'; |
||||||
import { e2e } from '@grafana/e2e'; |
import { e2e } from '@grafana/e2e'; |
||||||
import { variableAdapters } from '../adapters'; |
import { variableAdapters } from '../adapters'; |
||||||
|
|
@ -1,7 +1,7 @@ |
|||||||
import React, { PureComponent } from 'react'; |
import React, { PureComponent } from 'react'; |
||||||
import { getTagColorsFromName } from '@grafana/ui'; |
import { getTagColorsFromName } from '@grafana/ui'; |
||||||
import { e2e } from '@grafana/e2e'; |
import { e2e } from '@grafana/e2e'; |
||||||
import { VariableTag } from '../../variable'; |
import { VariableTag } from '../../../templating/variable'; |
||||||
|
|
||||||
interface Props { |
interface Props { |
||||||
onClick: () => void; |
onClick: () => void; |
@ -1,7 +1,7 @@ |
|||||||
import React, { PureComponent } from 'react'; |
import React, { PureComponent } from 'react'; |
||||||
import { getTagColorsFromName, Tooltip } from '@grafana/ui'; |
import { getTagColorsFromName, Tooltip } from '@grafana/ui'; |
||||||
import { e2e } from '@grafana/e2e'; |
import { e2e } from '@grafana/e2e'; |
||||||
import { VariableOption, VariableTag } from '../../variable'; |
import { VariableOption, VariableTag } from '../../../templating/variable'; |
||||||
|
|
||||||
export interface Props { |
export interface Props { |
||||||
multi: boolean; |
multi: boolean; |
@ -1,4 +1,4 @@ |
|||||||
import { VariableModel } from '../variable'; |
import { VariableModel } from '../../templating/variable'; |
||||||
|
|
||||||
export interface VariablePickerProps<Model extends VariableModel = VariableModel> { |
export interface VariablePickerProps<Model extends VariableModel = VariableModel> { |
||||||
variable: Model; |
variable: Model; |
@ -1,13 +1,13 @@ |
|||||||
import { AppEvents, DataSourcePluginMeta, DataSourceSelectItem } from '@grafana/data'; |
import { AppEvents, DataSourcePluginMeta, DataSourceSelectItem } from '@grafana/data'; |
||||||
|
|
||||||
import { validateVariableSelectionState } from '../state/actions'; |
import { validateVariableSelectionState } from '../state/actions'; |
||||||
import { QueryVariableModel, VariableRefresh } from '../variable'; |
import { QueryVariableModel, VariableRefresh } from '../../templating/variable'; |
||||||
import { ThunkResult } from '../../../types'; |
import { ThunkResult } from '../../../types'; |
||||||
import { getDatasourceSrv } from '../../plugins/datasource_srv'; |
import { getDatasourceSrv } from '../../plugins/datasource_srv'; |
||||||
import { getTimeSrv } from '../../dashboard/services/TimeSrv'; |
import { getTimeSrv } from '../../dashboard/services/TimeSrv'; |
||||||
import appEvents from '../../../core/app_events'; |
import appEvents from '../../../core/app_events'; |
||||||
import { importDataSourcePlugin } from '../../plugins/plugin_loader'; |
import { importDataSourcePlugin } from '../../plugins/plugin_loader'; |
||||||
import DefaultVariableQueryEditor from '../DefaultVariableQueryEditor'; |
import DefaultVariableQueryEditor from '../../templating/DefaultVariableQueryEditor'; |
||||||
import { getVariable } from '../state/selectors'; |
import { getVariable } from '../state/selectors'; |
||||||
import { addVariableEditorError, changeVariableEditorExtended, removeVariableEditorError } from '../editor/reducer'; |
import { addVariableEditorError, changeVariableEditorExtended, removeVariableEditorError } from '../editor/reducer'; |
||||||
import { variableAdapters } from '../adapters'; |
import { variableAdapters } from '../adapters'; |
@ -1,6 +1,6 @@ |
|||||||
import cloneDeep from 'lodash/cloneDeep'; |
import cloneDeep from 'lodash/cloneDeep'; |
||||||
|
|
||||||
import { containsVariable, QueryVariableModel, VariableRefresh } from '../variable'; |
import { containsVariable, QueryVariableModel, VariableRefresh } from '../../templating/variable'; |
||||||
import { initialQueryVariableModelState, queryVariableReducer } from './reducer'; |
import { initialQueryVariableModelState, queryVariableReducer } from './reducer'; |
||||||
import { dispatch } from '../../../store/store'; |
import { dispatch } from '../../../store/store'; |
||||||
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions'; |
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions'; |
@ -1,6 +1,6 @@ |
|||||||
import { reducerTester } from '../../../../test/core/redux/reducerTester'; |
import { reducerTester } from '../../../../test/core/redux/reducerTester'; |
||||||
import { queryVariableReducer, updateVariableOptions, updateVariableTags } from './reducer'; |
import { queryVariableReducer, updateVariableOptions, updateVariableTags } from './reducer'; |
||||||
import { QueryVariableModel, VariableOption } from '../variable'; |
import { QueryVariableModel, VariableOption } from '../../templating/variable'; |
||||||
import cloneDeep from 'lodash/cloneDeep'; |
import cloneDeep from 'lodash/cloneDeep'; |
||||||
import { VariablesState } from '../state/variablesReducer'; |
import { VariablesState } from '../state/variablesReducer'; |
||||||
import { getVariableTestContext } from '../state/helpers'; |
import { getVariableTestContext } from '../state/helpers'; |
@ -1,7 +1,13 @@ |
|||||||
import castArray from 'lodash/castArray'; |
import castArray from 'lodash/castArray'; |
||||||
import { UrlQueryMap, UrlQueryValue } from '@grafana/runtime'; |
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 { StoreState, ThunkResult } from '../../../types'; |
||||||
import { getVariable, getVariables } from './selectors'; |
import { getVariable, getVariables } from './selectors'; |
||||||
import { variableAdapters } from '../adapters'; |
import { variableAdapters } from '../adapters'; |
@ -1,6 +1,6 @@ |
|||||||
import { reducerTester } from '../../../../test/core/redux/reducerTester'; |
import { reducerTester } from '../../../../test/core/redux/reducerTester'; |
||||||
import { cleanUpDashboard } from 'app/features/dashboard/state/reducers'; |
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 { VariableAdapter, variableAdapters } from '../adapters'; |
||||||
import { createAction } from '@reduxjs/toolkit'; |
import { createAction } from '@reduxjs/toolkit'; |
||||||
import { variablesReducer, VariablesState } from './variablesReducer'; |
import { variablesReducer, VariablesState } from './variablesReducer'; |
@ -1,7 +1,7 @@ |
|||||||
import { cloneDeep } from 'lodash'; |
import { cloneDeep } from 'lodash'; |
||||||
|
|
||||||
import { StoreState } from '../../../types'; |
import { StoreState } from '../../../types'; |
||||||
import { VariableModel } from '../variable'; |
import { VariableModel } from '../../templating/variable'; |
||||||
import { getState } from '../../../store/store'; |
import { getState } from '../../../store/store'; |
||||||
import { EMPTY_UUID } from './types'; |
import { EMPTY_UUID } from './types'; |
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
import { VariableModel, VariableType } from '../variable'; |
import { VariableModel, VariableType } from '../../templating/variable'; |
||||||
import { VariablesState } from './variablesReducer'; |
import { VariablesState } from './variablesReducer'; |
||||||
|
|
||||||
export const EMPTY_UUID = '00000000-0000-0000-0000-000000000000'; |
export const EMPTY_UUID = '00000000-0000-0000-0000-000000000000'; |
@ -1,5 +1,5 @@ |
|||||||
import React, { ChangeEvent, PureComponent } from 'react'; |
import React, { ChangeEvent, PureComponent } from 'react'; |
||||||
import { TextBoxVariableModel } from '../variable'; |
import { TextBoxVariableModel } from '../../templating/variable'; |
||||||
import { VariableEditorProps } from '../editor/types'; |
import { VariableEditorProps } from '../editor/types'; |
||||||
|
|
||||||
export interface Props extends VariableEditorProps<TextBoxVariableModel> {} |
export interface Props extends VariableEditorProps<TextBoxVariableModel> {} |
@ -1,6 +1,6 @@ |
|||||||
import React, { ChangeEvent, FocusEvent, KeyboardEvent, PureComponent } from 'react'; |
import React, { ChangeEvent, FocusEvent, KeyboardEvent, PureComponent } from 'react'; |
||||||
|
|
||||||
import { TextBoxVariableModel } from '../variable'; |
import { TextBoxVariableModel } from '../../templating/variable'; |
||||||
import { toVariablePayload } from '../state/types'; |
import { toVariablePayload } from '../state/types'; |
||||||
import { dispatch } from '../../../store/store'; |
import { dispatch } from '../../../store/store'; |
||||||
import { variableAdapters } from '../adapters'; |
import { variableAdapters } from '../adapters'; |
@ -1,10 +1,10 @@ |
|||||||
import { variableAdapters } from '../adapters'; |
import { variableAdapters } from '../adapters'; |
||||||
import { createTextBoxVariableAdapter } from './adapter'; |
import { createTextBoxVariableAdapter } from './adapter'; |
||||||
import { reduxTester } from '../../../../test/core/redux/reduxTester'; |
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 { updateTextBoxVariableOptions } from './actions'; |
||||||
import { getTemplatingRootReducer } from '../state/helpers'; |
import { getTemplatingRootReducer } from '../state/helpers'; |
||||||
import { VariableOption, VariableHide, TextBoxVariableModel } from '../variable'; |
import { TextBoxVariableModel, VariableHide, VariableOption } from '../../templating/variable'; |
||||||
import { toVariablePayload } from '../state/types'; |
import { toVariablePayload } from '../state/types'; |
||||||
import { createTextBoxOptions } from './reducer'; |
import { createTextBoxOptions } from './reducer'; |
||||||
import { setCurrentVariableValue } from '../state/sharedReducer'; |
import { setCurrentVariableValue } from '../state/sharedReducer'; |
@ -1,4 +1,4 @@ |
|||||||
import { TextBoxVariableModel } from '../variable'; |
import { TextBoxVariableModel } from '../../templating/variable'; |
||||||
import { ThunkResult } from '../../../types'; |
import { ThunkResult } from '../../../types'; |
||||||
import { getVariable } from '../state/selectors'; |
import { getVariable } from '../state/selectors'; |
||||||
import { variableAdapters } from '../adapters'; |
import { variableAdapters } from '../adapters'; |
@ -1,6 +1,6 @@ |
|||||||
import cloneDeep from 'lodash/cloneDeep'; |
import cloneDeep from 'lodash/cloneDeep'; |
||||||
|
|
||||||
import { TextBoxVariableModel } from '../variable'; |
import { TextBoxVariableModel } from '../../templating/variable'; |
||||||
import { initialTextBoxVariableModelState, textBoxVariableReducer } from './reducer'; |
import { initialTextBoxVariableModelState, textBoxVariableReducer } from './reducer'; |
||||||
import { dispatch } from '../../../store/store'; |
import { dispatch } from '../../../store/store'; |
||||||
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions'; |
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions'; |
@ -1,6 +1,6 @@ |
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; |
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 { EMPTY_UUID, getInstanceState, VariablePayload } from '../state/types'; |
||||||
import { initialVariablesState, VariablesState } from '../state/variablesReducer'; |
import { initialVariablesState, VariablesState } from '../state/variablesReducer'; |
||||||
|
|
Loading…
Reference in new issue