Commit 3c1f27b0 by Hugo Häggmark Committed by GitHub

Chore: reduce strict errors for variables (#31241)

* Chore: reduces a lot of variable errors

* Chore: reduces variable Editor errors

* Chore: reduces variable Picker errors

* Chore: reduce error count

* Chore: reduces errors for ChangeEvent instead of FormEvent

* Chore: reduces errors with CombinedState

* Chore: reduces ComponentType errors

* Chore: reduce errors in reducers

* Chore: reduces misc errors

* Chore: reduce AdhocPicker errors

* Chore: reduce error limit

* Update public/app/features/variables/adhoc/picker/AdHocFilterValue.tsx

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

* Chore: updates after PR comments

* Chore: small refactor

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
parent 6c4be296
......@@ -2,17 +2,7 @@ import { ComponentType } from 'react';
import { Reducer } from 'redux';
import { Registry, UrlQueryValue, VariableType } from '@grafana/data';
import {
AdHocVariableModel,
ConstantVariableModel,
CustomVariableModel,
DataSourceVariableModel,
IntervalVariableModel,
QueryVariableModel,
TextBoxVariableModel,
VariableModel,
VariableOption,
} from './types';
import { VariableModel, VariableOption } from './types';
import { VariableEditorProps } from './editor/types';
import { VariablesState } from './state/variablesReducer';
import { VariablePickerProps } from './pickers/types';
......@@ -36,22 +26,12 @@ export interface VariableAdapter<Model extends VariableModel> {
updateOptions: (variable: Model, searchFilter?: string) => Promise<void>;
getSaveModel: (variable: Model, saveCurrentAsDefault?: boolean) => Partial<Model>;
getValueForUrl: (variable: Model) => string | string[];
picker: ComponentType<VariablePickerProps>;
editor: ComponentType<VariableEditorProps>;
picker: ComponentType<VariablePickerProps<Model>>;
editor: ComponentType<VariableEditorProps<Model>>;
reducer: Reducer<VariablesState>;
beforeAdding?: (model: any) => any;
}
export type VariableModels =
| QueryVariableModel
| CustomVariableModel
| TextBoxVariableModel
| ConstantVariableModel
| DataSourceVariableModel
| IntervalVariableModel
| AdHocVariableModel;
export type VariableTypeRegistry<Model extends VariableModel = VariableModel> = Registry<VariableAdapter<Model>>;
export const getDefaultVariableAdapters = () => [
createQueryVariableAdapter(),
createCustomVariableAdapter(),
......@@ -63,4 +43,4 @@ export const getDefaultVariableAdapters = () => [
createSystemVariableAdapter(),
];
export const variableAdapters: VariableTypeRegistry = new Registry<VariableAdapter<VariableModels>>();
export const variableAdapters = new Registry<VariableAdapter<any>>();
......@@ -3,8 +3,7 @@ import { DataSourcePluginMeta, DataSourceSelectItem } from '@grafana/data';
import { variableAdapters } from '../adapters';
import { createAdHocVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from 'app/features/variables/state/reducers';
import { getRootReducer } from '../state/helpers';
import { getRootReducer, RootReducerType } from '../state/helpers';
import { toVariableIdentifier, toVariablePayload } from '../state/types';
import {
addFilter,
......@@ -19,7 +18,6 @@ import {
import { filterAdded, filterRemoved, filtersRestored, filterUpdated } from './reducer';
import { addVariable, changeVariableProp } from '../state/sharedReducer';
import { updateLocation } from 'app/core/actions';
import { DashboardState, LocationState } from 'app/types';
import { VariableModel } from 'app/features/variables/types';
import { changeVariableEditorExtended, setIdInEditor } from '../editor/reducer';
import { adHocBuilder } from '../shared/testing/builders';
......@@ -34,12 +32,6 @@ jest.mock('app/features/plugins/datasource_srv', () => ({
})),
}));
type ReducersUsedInContext = {
templating: TemplatingState;
dashboard: DashboardState;
location: LocationState;
};
variableAdapters.setInit(() => [createAdHocVariableAdapter()]);
describe('adhoc actions', () => {
......@@ -66,7 +58,7 @@ describe('adhoc actions', () => {
.withDatasource(options.datasource)
.build();
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(createAddVariableAction(variable))
.whenAsyncActionIsDispatched(applyFilterFromTable(options), true);
......@@ -90,7 +82,7 @@ describe('adhoc actions', () => {
operator: '=',
};
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenAsyncActionIsDispatched(applyFilterFromTable(options), true);
......@@ -123,7 +115,7 @@ describe('adhoc actions', () => {
.withDatasource(options.datasource)
.build();
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(createAddVariableAction(variable))
.whenAsyncActionIsDispatched(applyFilterFromTable(options), true);
......@@ -155,7 +147,7 @@ describe('adhoc actions', () => {
const variable = adHocBuilder().withId('Filters').withName('Filters').withDatasource(options.datasource).build();
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(createAddVariableAction(existing))
.whenAsyncActionIsDispatched(applyFilterFromTable(options), true);
......@@ -194,7 +186,7 @@ describe('adhoc actions', () => {
const update = { index: 0, filter: updated };
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(createAddVariableAction(variable))
.whenAsyncActionIsDispatched(changeFilter('elastic-filter', update), true);
......@@ -230,7 +222,7 @@ describe('adhoc actions', () => {
.withDatasource('elasticsearch')
.build();
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(createAddVariableAction(variable))
.whenAsyncActionIsDispatched(addFilter('elastic-filter', adding), true);
......@@ -261,7 +253,7 @@ describe('adhoc actions', () => {
.withDatasource('elasticsearch')
.build();
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(createAddVariableAction(variable))
.whenAsyncActionIsDispatched(addFilter('elastic-filter', adding), true);
......@@ -284,7 +276,7 @@ describe('adhoc actions', () => {
.withDatasource('elasticsearch')
.build();
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(createAddVariableAction(variable))
.whenAsyncActionIsDispatched(removeFilter('elastic-filter', 0), true);
......@@ -314,7 +306,7 @@ describe('adhoc actions', () => {
.withDatasource('elasticsearch')
.build();
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(createAddVariableAction(variable))
.whenAsyncActionIsDispatched(removeFilter('elastic-filter', 0), true);
......@@ -349,7 +341,7 @@ describe('adhoc actions', () => {
{ ...existing, name: 'value-2' },
];
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(createAddVariableAction(variable))
.whenAsyncActionIsDispatched(setFiltersFromUrl('elastic-filter', fromUrl), true);
......@@ -380,7 +372,7 @@ describe('adhoc actions', () => {
getMetricSources.mockRestore();
getMetricSources.mockReturnValue(datasources);
const tester = reduxTester<ReducersUsedInContext>()
const tester = reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(initAdHocVariableEditor());
......@@ -406,7 +398,7 @@ describe('adhoc actions', () => {
getDatasource.mockRestore();
getDatasource.mockResolvedValue(null);
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(createAddVariableAction(variable))
.whenActionIsDispatched(setIdInEditor({ id: variable.id }))
......@@ -434,7 +426,7 @@ describe('adhoc actions', () => {
getTagKeys: () => {},
});
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(createAddVariableAction(variable))
.whenActionIsDispatched(setIdInEditor({ id: variable.id }))
......
......@@ -6,7 +6,7 @@ import { MetricFindValue, SelectableValue } from '@grafana/data';
interface Props {
datasource: string;
filterKey: string;
filterValue: string | null;
filterValue?: string;
onChange: (item: SelectableValue<string>) => void;
placeHolder?: string;
}
......
import React, { PureComponent, ReactNode } from 'react';
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
import { StoreState } from 'app/types';
import { connect, ConnectedProps } from 'react-redux';
import { AdHocVariableFilter, AdHocVariableModel } from 'app/features/variables/types';
import { VariablePickerProps } from '../../pickers/types';
import { SelectableValue } from '@grafana/data';
......@@ -10,20 +9,20 @@ import { addFilter, changeFilter, removeFilter } from '../actions';
import { REMOVE_FILTER_KEY } from './AdHocFilterKey';
import { AdHocFilterRenderer } from './AdHocFilterRenderer';
interface OwnProps extends VariablePickerProps<AdHocVariableModel> {}
const mapDispatchToProps = {
addFilter,
removeFilter,
changeFilter,
};
interface ConnectedProps {}
const connector = connect(null, mapDispatchToProps);
interface DispatchProps {
addFilter: typeof addFilter;
removeFilter: typeof removeFilter;
changeFilter: typeof changeFilter;
}
interface OwnProps extends VariablePickerProps<AdHocVariableModel> {}
type Props = OwnProps & ConnectedProps & DispatchProps;
type Props = OwnProps & ConnectedProps<typeof connector>;
export class AdHocPickerUnconnected extends PureComponent<Props> {
onChange = (index: number, prop: string) => (key: SelectableValue<string>) => {
onChange = (index: number, prop: string) => (key: SelectableValue<string | null>) => {
const { id, filters } = this.props.variable;
const { value } = key;
......@@ -85,13 +84,5 @@ export class AdHocPickerUnconnected extends PureComponent<Props> {
}
}
const mapDispatchToProps: MapDispatchToProps<DispatchProps, OwnProps> = {
addFilter,
removeFilter,
changeFilter,
};
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = (state) => ({});
export const AdHocPicker = connect(mapStateToProps, mapDispatchToProps)(AdHocPickerUnconnected);
export const AdHocPicker = connector(AdHocPickerUnconnected);
AdHocPicker.displayName = 'AdHocPicker';
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react';
import React, { FormEvent, PureComponent } from 'react';
import { selectors } from '@grafana/e2e-selectors';
import { VerticalGroup } from '@grafana/ui';
......@@ -10,17 +10,17 @@ import { VariableTextField } from '../editor/VariableTextField';
export interface Props extends VariableEditorProps<ConstantVariableModel> {}
export class ConstantVariableEditor extends PureComponent<Props> {
onChange = (event: ChangeEvent<HTMLInputElement>) => {
onChange = (event: FormEvent<HTMLInputElement>) => {
this.props.onPropChange({
propName: 'query',
propValue: event.target.value,
propValue: event.currentTarget.value,
});
};
onBlur = (event: FocusEvent<HTMLInputElement>) => {
onBlur = (event: FormEvent<HTMLInputElement>) => {
this.props.onPropChange({
propName: 'query',
propValue: event.target.value,
propValue: event.currentTarget.value,
updateOptions: true,
});
};
......
import { variableAdapters } from '../adapters';
import { createConstantVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from 'app/features/variables/state/reducers';
import { updateConstantVariableOptions } from './actions';
import { getRootReducer } from '../state/helpers';
import { getRootReducer, RootReducerType } from '../state/helpers';
import { ConstantVariableModel, initialVariableModelState, VariableOption } from '../types';
import { toVariablePayload } from '../state/types';
import { createConstantOptionsFromQuery } from './reducer';
......@@ -35,19 +34,15 @@ describe('constant actions', () => {
query: 'A',
};
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateConstantVariableOptions(toVariablePayload(variable)), true);
tester.thenDispatchedActionsPredicateShouldEqual((actions) => {
const [createAction, setCurrentAction] = actions;
const expectedNumberOfActions = 2;
expect(createAction).toEqual(createConstantOptionsFromQuery(toVariablePayload(variable)));
expect(setCurrentAction).toEqual(setCurrentVariableValue(toVariablePayload(variable, { option })));
return actions.length === expectedNumberOfActions;
});
tester.thenDispatchedActionsShouldEqual(
createConstantOptionsFromQuery(toVariablePayload(variable)),
setCurrentVariableValue(toVariablePayload(variable, { option }))
);
});
});
});
......@@ -4,10 +4,10 @@ import { dispatch } from '../../../store/store';
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
import { VariableAdapter } from '../adapters';
import { constantVariableReducer, initialConstantVariableModelState } from './reducer';
import { OptionsPicker } from '../pickers';
import { ConstantVariableEditor } from './ConstantVariableEditor';
import { updateConstantVariableOptions } from './actions';
import { toVariableIdentifier } from '../state/types';
import { optionPickerFactory } from '../pickers';
export const createConstantVariableAdapter = (): VariableAdapter<ConstantVariableModel> => {
return {
......@@ -16,7 +16,7 @@ export const createConstantVariableAdapter = (): VariableAdapter<ConstantVariabl
name: 'Constant',
initialState: initialConstantVariableModelState,
reducer: constantVariableReducer,
picker: OptionsPicker,
picker: optionPickerFactory<ConstantVariableModel>(),
editor: ConstantVariableEditor,
dependsOn: () => {
return false;
......
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react';
import React, { FormEvent, PureComponent } from 'react';
import { CustomVariableModel, VariableWithMultiSupport } from '../types';
import { SelectionOptionsEditor } from '../editor/SelectionOptionsEditor';
import { OnPropChangeArguments, VariableEditorProps } from '../editor/types';
......@@ -21,10 +21,10 @@ interface DispatchProps {
export type Props = OwnProps & ConnectedProps & DispatchProps;
class CustomVariableEditorUnconnected extends PureComponent<Props> {
onChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
onChange = (event: FormEvent<HTMLTextAreaElement>) => {
this.props.onPropChange({
propName: 'query',
propValue: event.target.value,
propValue: event.currentTarget.value,
});
};
......@@ -32,10 +32,10 @@ class CustomVariableEditorUnconnected extends PureComponent<Props> {
this.props.onPropChange({ propName, propValue, updateOptions: true });
};
onBlur = (event: FocusEvent<HTMLTextAreaElement>) => {
onBlur = (event: FormEvent<HTMLTextAreaElement>) => {
this.props.onPropChange({
propName: 'query',
propValue: event.target.value,
propValue: event.currentTarget.value,
updateOptions: true,
});
};
......
......@@ -2,11 +2,10 @@ import { variableAdapters } from '../adapters';
import { updateCustomVariableOptions } from './actions';
import { createCustomVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { getRootReducer } from '../state/helpers';
import { getRootReducer, RootReducerType } from '../state/helpers';
import { CustomVariableModel, initialVariableModelState, VariableOption } from '../types';
import { toVariablePayload } from '../state/types';
import { addVariable, setCurrentVariableValue } from '../state/sharedReducer';
import { TemplatingState } from '../state/reducers';
import { createCustomOptionsFromQuery } from './reducer';
describe('custom actions', () => {
......@@ -48,19 +47,15 @@ describe('custom actions', () => {
includeAll: false,
};
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateCustomVariableOptions(toVariablePayload(variable)), true);
tester.thenDispatchedActionsPredicateShouldEqual((actions) => {
const [createAction, setCurrentAction] = actions;
const expectedNumberOfActions = 2;
expect(createAction).toEqual(createCustomOptionsFromQuery(toVariablePayload(variable)));
expect(setCurrentAction).toEqual(setCurrentVariableValue(toVariablePayload(variable, { option })));
return actions.length === expectedNumberOfActions;
});
tester.thenDispatchedActionsShouldEqual(
createCustomOptionsFromQuery(toVariablePayload(variable)),
setCurrentVariableValue(toVariablePayload(variable, { option }))
);
});
});
});
......@@ -4,11 +4,11 @@ import { dispatch } from '../../../store/store';
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
import { VariableAdapter } from '../adapters';
import { customVariableReducer, initialCustomVariableModelState } from './reducer';
import { OptionsPicker } from '../pickers';
import { CustomVariableEditor } from './CustomVariableEditor';
import { updateCustomVariableOptions } from './actions';
import { ALL_VARIABLE_TEXT, toVariableIdentifier } from '../state/types';
import { isAllVariable } from '../utils';
import { optionPickerFactory } from '../pickers';
export const createCustomVariableAdapter = (): VariableAdapter<CustomVariableModel> => {
return {
......@@ -17,7 +17,7 @@ export const createCustomVariableAdapter = (): VariableAdapter<CustomVariableMod
name: 'Custom',
initialState: initialCustomVariableModelState,
reducer: customVariableReducer,
picker: OptionsPicker,
picker: optionPickerFactory<CustomVariableModel>(),
editor: CustomVariableEditor,
dependsOn: () => {
return false;
......
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react';
import React, { FormEvent, PureComponent } from 'react';
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
import { InlineFieldRow, VerticalGroup } from '@grafana/ui';
......@@ -34,17 +34,17 @@ export class DataSourceVariableEditorUnConnected extends PureComponent<Props> {
this.props.initDataSourceVariableEditor();
}
onRegExChange = (event: ChangeEvent<HTMLInputElement>) => {
onRegExChange = (event: FormEvent<HTMLInputElement>) => {
this.props.onPropChange({
propName: 'regex',
propValue: event.target.value,
propValue: event.currentTarget.value,
});
};
onRegExBlur = (event: FocusEvent<HTMLInputElement>) => {
onRegExBlur = (event: FormEvent<HTMLInputElement>) => {
this.props.onPropChange({
propName: 'regex',
propValue: event.target.value,
propValue: event.currentTarget.value,
updateOptions: true,
});
};
......
import { DataSourceInstanceSettings } from '@grafana/data';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from '../state/reducers';
import { getRootReducer } from '../state/helpers';
import { getRootReducer, RootReducerType } from '../state/helpers';
import { toVariableIdentifier, toVariablePayload } from '../state/types';
import { variableAdapters } from '../adapters';
import { createDataSourceVariableAdapter } from './adapter';
......@@ -49,7 +48,7 @@ describe('data source actions', () => {
query: 'mock-data-id',
});
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(
addVariable(toVariablePayload(datasource, { global: false, index: 0, model: datasource }))
......@@ -97,7 +96,7 @@ describe('data source actions', () => {
regex: '/.*(second-name).*/',
});
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(
addVariable(toVariablePayload(datasource, { global: false, index: 0, model: datasource }))
......@@ -142,7 +141,7 @@ describe('data source actions', () => {
const { dependencies, getListMock, getDatasourceSrvMock } = getTestContext({ sources });
await reduxTester<{ templating: TemplatingState }>()
await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(initDataSourceVariableEditor(dependencies))
.thenDispatchedActionsShouldEqual(
......
......@@ -4,11 +4,11 @@ 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';
import { containsVariable, isAllVariable } from '../utils';
import { optionPickerFactory } from '../pickers';
export const createDataSourceVariableAdapter = (): VariableAdapter<DataSourceVariableModel> => {
return {
......@@ -17,7 +17,7 @@ export const createDataSourceVariableAdapter = (): VariableAdapter<DataSourceVar
name: 'Datasource',
initialState: initialDataSourceVariableModelState,
reducer: dataSourceVariableReducer,
picker: OptionsPicker,
picker: optionPickerFactory<DataSourceVariableModel>(),
editor: DataSourceVariableEditor,
dependsOn: (variable, variableToTest) => {
if (variable.regex) {
......
import React, { FunctionComponent, useCallback } from 'react';
import React, { ChangeEvent, FormEvent, FunctionComponent, useCallback } from 'react';
import { InlineFieldRow, VerticalGroup } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
......@@ -16,22 +16,22 @@ export interface SelectionOptionsEditorProps<Model extends VariableWithMultiSupp
export const SelectionOptionsEditor: FunctionComponent<SelectionOptionsEditorProps> = (props) => {
const onMultiChanged = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
(event: ChangeEvent<HTMLInputElement>) => {
props.onMultiChanged(toVariableIdentifier(props.variable), event.target.checked);
},
[props.onMultiChanged, props.variable]
);
const onIncludeAllChanged = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
(event: ChangeEvent<HTMLInputElement>) => {
props.onPropChange({ propName: 'includeAll', propValue: event.target.checked });
},
[props.onPropChange]
);
const onAllValueChanged = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
props.onPropChange({ propName: 'allValue', propValue: event.target.value });
(event: FormEvent<HTMLInputElement>) => {
props.onPropChange({ propName: 'allValue', propValue: event.currentTarget.value });
},
[props.onPropChange]
);
......
......@@ -5,34 +5,34 @@ import { selectors } from '@grafana/e2e-selectors';
import { toVariableIdentifier, toVariablePayload, VariableIdentifier } from '../state/types';
import { StoreState } from '../../../types';
import { VariableEditorEditor } from './VariableEditorEditor';
import { connect, MapStateToProps, MapDispatchToProps } from 'react-redux';
import { connect, ConnectedProps } from 'react-redux';
import { getEditorVariables } from '../state/selectors';
import { VariableModel } from '../types';
import { switchToEditMode, switchToListMode, switchToNewMode } from './actions';
import { changeVariableOrder, duplicateVariable, removeVariable } from '../state/sharedReducer';
import { VariableEditorList } from './VariableEditorList';
import { DashboardModel } from '../../dashboard/state';
import { VariablesUnknownTable } from '../inspect/VariablesUnknownTable';
import { VariablesDependenciesButton } from '../inspect/VariablesDependenciesButton';
interface OwnProps {}
const mapStateToProps = (state: StoreState) => ({
variables: getEditorVariables(state),
idInEditor: state.templating.editor.id,
dashboard: state.dashboard.getModel(),
});
interface ConnectedProps {
idInEditor: string | null;
variables: VariableModel[];
dashboard: DashboardModel | null;
}
const mapDispatchToProps = {
changeVariableOrder,
duplicateVariable,
removeVariable,
switchToNewMode,
switchToEditMode,
switchToListMode,
};
interface DispatchProps {
changeVariableOrder: typeof changeVariableOrder;
duplicateVariable: typeof duplicateVariable;
removeVariable: typeof removeVariable;
switchToNewMode: typeof switchToNewMode;
switchToEditMode: typeof switchToEditMode;
switchToListMode: typeof switchToListMode;
}
interface OwnProps {}
type Props = OwnProps & ConnectedProps & DispatchProps;
const connector = connect(mapStateToProps, mapDispatchToProps);
type Props = OwnProps & ConnectedProps<typeof connector>;
class VariableEditorContainerUnconnected extends PureComponent<Props> {
componentDidMount(): void {
......@@ -48,7 +48,7 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
this.props.switchToEditMode(identifier);
};
onNewVariable = (event: MouseEvent<HTMLAnchorElement>) => {
onNewVariable = (event: MouseEvent) => {
event.preventDefault();
this.props.switchToNewMode();
};
......@@ -124,19 +124,4 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
}
}
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = (state) => ({
variables: getEditorVariables(state),
idInEditor: state.templating.editor.id,
dashboard: state.dashboard.getModel(),
});
const mapDispatchToProps: MapDispatchToProps<DispatchProps, OwnProps> = {
changeVariableOrder,
duplicateVariable,
removeVariable,
switchToNewMode,
switchToEditMode,
switchToListMode,
};
export const VariableEditorContainer = connect(mapStateToProps, mapDispatchToProps)(VariableEditorContainerUnconnected);
export const VariableEditorContainer = connector(VariableEditorContainerUnconnected);
import React, { ChangeEvent, FormEvent, PureComponent } from 'react';
import React, { FormEvent, PureComponent } from 'react';
import isEqual from 'lodash/isEqual';
import { AppEvents, LoadingState, SelectableValue, VariableType } from '@grafana/data';
import { Button, Icon, InlineFieldRow, VerticalGroup } from '@grafana/ui';
......@@ -62,9 +62,9 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props> {
this.props.variableEditorUnMount(this.props.identifier);
}
onNameChange = (event: ChangeEvent<HTMLInputElement>) => {
onNameChange = (event: FormEvent<HTMLInputElement>) => {
event.preventDefault();
this.props.changeVariableName(this.props.identifier, event.target.value);
this.props.changeVariableName(this.props.identifier, event.currentTarget.value);
};
onTypeChange = (option: SelectableValue<VariableType>) => {
......@@ -74,16 +74,16 @@ export class VariableEditorEditorUnConnected extends PureComponent<Props> {
this.props.changeVariableType(toVariablePayload(this.props.identifier, { newType: option.value }));
};
onLabelChange = (event: ChangeEvent<HTMLInputElement>) => {
onLabelChange = (event: FormEvent<HTMLInputElement>) => {
event.preventDefault();
this.props.changeVariableProp(
toVariablePayload(this.props.identifier, { propName: 'label', propValue: event.target.value })
toVariablePayload(this.props.identifier, { propName: 'label', propValue: event.currentTarget.value })
);
};
onDescriptionChange = (event: ChangeEvent<HTMLInputElement>) => {
onDescriptionChange = (event: FormEvent<HTMLInputElement>) => {
this.props.changeVariableProp(
toVariablePayload(this.props.identifier, { propName: 'description', propValue: event.target.value })
toVariablePayload(this.props.identifier, { propName: 'description', propValue: event.currentTarget.value })
);
};
......
......@@ -15,7 +15,7 @@ import { VariableUsagesButton } from '../inspect/VariableUsagesButton';
export interface Props {
variables: VariableModel[];
dashboard: DashboardModel | null;
onAddClick: (event: MouseEvent<HTMLAnchorElement>) => void;
onAddClick: (event: MouseEvent) => void;
onEditClick: (identifier: VariableIdentifier) => void;
onChangeVariableOrder: (identifier: VariableIdentifier, fromIndex: number, toIndex: number) => void;
onDuplicateVariable: (identifier: VariableIdentifier) => void;
......
......@@ -176,7 +176,7 @@ export function isQueryEditor<
>(
component: VariableQueryEditorType,
datasource: DataSourceApi<TQuery, TOptions>
): component is ComponentType<QueryEditorProps<any>> {
): component is ComponentType<QueryEditorProps<DataSourceApi<TQuery, TOptions>, TQuery, TOptions, any>> {
if (!component) {
return false;
}
......
import React, { ChangeEvent, FocusEvent, PureComponent } from 'react';
import React, { ChangeEvent, FormEvent, PureComponent } from 'react';
import { InlineFieldRow, VerticalGroup } from '@grafana/ui';
import { IntervalVariableModel } from '../types';
......@@ -20,17 +20,17 @@ export class IntervalVariableEditor extends PureComponent<Props> {
});
};
onQueryChanged = (event: ChangeEvent<HTMLInputElement>) => {
onQueryChanged = (event: FormEvent<HTMLInputElement>) => {
this.props.onPropChange({
propName: 'query',
propValue: event.target.value,
propValue: event.currentTarget.value,
});
};
onQueryBlur = (event: FocusEvent<HTMLInputElement>) => {
onQueryBlur = (event: FormEvent<HTMLInputElement>) => {
this.props.onPropChange({
propName: 'query',
propValue: event.target.value,
propValue: event.currentTarget.value,
updateOptions: true,
});
};
......@@ -43,10 +43,10 @@ export class IntervalVariableEditor extends PureComponent<Props> {
});
};
onAutoMinChanged = (event: ChangeEvent<HTMLInputElement>) => {
onAutoMinChanged = (event: FormEvent<HTMLInputElement>) => {
this.props.onPropChange({
propName: 'auto_min',
propValue: event.target.value,
propValue: event.currentTarget.value,
updateOptions: true,
});
};
......
import { getRootReducer } from '../state/helpers';
import { getRootReducer, RootReducerType } from '../state/helpers';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from '../state/reducers';
import { toVariableIdentifier, toVariablePayload } from '../state/types';
import { updateAutoValue, UpdateAutoValueDependencies, updateIntervalVariableOptions } from './actions';
import { createIntervalOptions } from './reducer';
......@@ -26,7 +25,7 @@ describe('interval actions', () => {
it('then correct actions are dispatched', async () => {
const interval = intervalBuilder().withId('0').withQuery('1s,1m,1h,1d').withAuto(false).build();
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval })))
.whenAsyncActionIsDispatched(updateIntervalVariableOptions(toVariableIdentifier(interval)), true);
......@@ -64,7 +63,7 @@ describe('interval actions', () => {
.withAutoMin('1xyz') // illegal interval string
.build();
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval })))
.whenAsyncActionIsDispatched(updateOptions(toVariableIdentifier(interval)), true);
......@@ -121,7 +120,7 @@ describe('interval actions', () => {
} as unknown) as TemplateSrv,
};
await reduxTester<{ templating: TemplatingState }>()
await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(
addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval }))
......@@ -165,7 +164,7 @@ describe('interval actions', () => {
} as unknown) as TemplateSrv,
};
await reduxTester<{ templating: TemplatingState }>()
await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(
addVariable(toVariablePayload(interval, { global: false, index: 0, model: interval }))
......
......@@ -4,10 +4,10 @@ import { dispatch } from '../../../store/store';
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
import { VariableAdapter } from '../adapters';
import { initialIntervalVariableModelState, intervalVariableReducer } from './reducer';
import { OptionsPicker } from '../pickers';
import { toVariableIdentifier } from '../state/types';
import { IntervalVariableEditor } from './IntervalVariableEditor';
import { updateAutoValue, updateIntervalVariableOptions } from './actions';
import { optionPickerFactory } from '../pickers';
export const createIntervalVariableAdapter = (): VariableAdapter<IntervalVariableModel> => {
return {
......@@ -16,7 +16,7 @@ export const createIntervalVariableAdapter = (): VariableAdapter<IntervalVariabl
name: 'Interval',
initialState: initialIntervalVariableModelState,
reducer: intervalVariableReducer,
picker: OptionsPicker,
picker: optionPickerFactory<IntervalVariableModel>(),
editor: IntervalVariableEditor,
dependsOn: () => {
return false;
......
import React, { PureComponent } from 'react';
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
import React, { ComponentType, PureComponent } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { ClickOutsideWrapper } from '@grafana/ui';
import { LoadingState } from '@grafana/data';
import { StoreState } from 'app/types';
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 '../../types';
import { VariableOptions } from '../shared/VariableOptions';
import { isQuery } from '../../guard';
import { isMulti, isQuery } from '../../guard';
import { VariablePickerProps } from '../types';
import { formatVariableLabel } from '../../shared/formatVariable';
import { toVariableIdentifier } from '../../state/types';
import { getVariableQueryRunner } from '../../query/VariableQueryRunner';
import { VariableLink } from '../shared/VariableLink';
interface OwnProps extends VariablePickerProps<VariableWithMultiSupport> {}
export const optionPickerFactory = <Model extends VariableWithOptions | VariableWithMultiSupport>(): ComponentType<
VariablePickerProps<Model>
> => {
const mapDispatchToProps = {
showOptions,
commitChangesToVariable,
filterOrSearchOptions,
toggleAllOptions,
toggleOption,
toggleAndFetchTag,
navigateOptions,
};
interface ConnectedProps {
picker: OptionsPickerState;
}
const mapStateToProps = (state: StoreState) => ({
picker: state.templating.optionsPicker,
});
interface DispatchProps {
showOptions: typeof showOptions;
commitChangesToVariable: typeof commitChangesToVariable;
toggleAllOptions: typeof toggleAllOptions;
toggleOption: typeof toggleOption;
toggleAndFetchTag: typeof toggleAndFetchTag;
filterOrSearchOptions: typeof filterOrSearchOptions;
navigateOptions: typeof navigateOptions;
}
const connector = connect(mapStateToProps, mapDispatchToProps);
type Props = OwnProps & ConnectedProps & DispatchProps;
interface OwnProps extends VariablePickerProps<Model> {}
export class OptionsPickerUnconnected extends PureComponent<Props> {
onShowOptions = () => this.props.showOptions(this.props.variable);
onHideOptions = () => this.props.commitChangesToVariable(this.props.onVariableChange);
type Props = OwnProps & ConnectedProps<typeof connector>;
onToggleOption = (option: VariableOption, clearOthers: boolean) => {
const toggleFunc = this.props.variable.multi ? this.onToggleMultiValueVariable : this.onToggleSingleValueVariable;
toggleFunc(option, clearOthers);
};
class OptionsPickerUnconnected extends PureComponent<Props> {
onShowOptions = () => this.props.showOptions(this.props.variable);
onHideOptions = () => this.props.commitChangesToVariable(this.props.onVariableChange);
onToggleSingleValueVariable = (option: VariableOption, clearOthers: boolean) => {
this.props.toggleOption({ option, clearOthers, forceSelect: false });
this.onHideOptions();
};
onToggleOption = (option: VariableOption, clearOthers: boolean) => {
const toggleFunc =
isMulti(this.props.variable) && this.props.variable.multi
? this.onToggleMultiValueVariable
: this.onToggleSingleValueVariable;
toggleFunc(option, clearOthers);
};
onToggleMultiValueVariable = (option: VariableOption, clearOthers: boolean) => {
this.props.toggleOption({ option, clearOthers, forceSelect: false });
};
onToggleSingleValueVariable = (option: VariableOption, clearOthers: boolean) => {
this.props.toggleOption({ option, clearOthers, forceSelect: false });
this.onHideOptions();
};
render() {
const { variable, picker } = this.props;
const showOptions = picker.id === variable.id;
onToggleMultiValueVariable = (option: VariableOption, clearOthers: boolean) => {
this.props.toggleOption({ option, clearOthers, forceSelect: false });
};
return (
<div className="variable-link-wrapper">
{this.renderLink(showOptions, variable)}
{this.renderOptions(showOptions, picker)}
</div>
);
}
render() {
const { variable, picker } = this.props;
const showOptions = picker.id === variable.id;
renderLink(showOptions: boolean, variable: VariableWithMultiSupport) {
if (showOptions) {
return null;
return (
<div className="variable-link-wrapper">
{showOptions ? this.renderOptions(picker) : this.renderLink(variable)}
</div>
);
}
const linkText = formatVariableLabel(variable);
const tags = getSelectedTags(variable);
const loading = variable.state === LoadingState.Loading;
return (
<VariableLink
text={linkText}
tags={tags}
onClick={this.onShowOptions}
loading={loading}
onCancel={this.onCancel}
/>
);
}
onCancel = () => {
getVariableQueryRunner().cancelRequest(toVariableIdentifier(this.props.variable));
};
renderOptions(showOptions: boolean, picker: OptionsPickerState) {
if (!showOptions) {
return null;
renderLink(variable: VariableWithOptions) {
const linkText = formatVariableLabel(variable);
const tags = getSelectedTags(variable);
const loading = variable.state === LoadingState.Loading;
return (
<VariableLink
text={linkText}
tags={tags}
onClick={this.onShowOptions}
loading={loading}
onCancel={this.onCancel}
/>
);
}
return (
<ClickOutsideWrapper onClick={this.onHideOptions}>
<VariableInput
value={picker.queryValue}
onChange={this.props.filterOrSearchOptions}
onNavigate={this.props.navigateOptions}
/>
<VariableOptions
values={picker.options}
onToggle={this.onToggleOption}
onToggleAll={this.props.toggleAllOptions}
onToggleTag={this.props.toggleAndFetchTag}
highlightIndex={picker.highlightIndex}
multi={picker.multi}
tags={picker.tags}
selectedValues={picker.selectedValues}
/>
</ClickOutsideWrapper>
);
onCancel = () => {
getVariableQueryRunner().cancelRequest(toVariableIdentifier(this.props.variable));
};
renderOptions(picker: OptionsPickerState) {
return (
<ClickOutsideWrapper onClick={this.onHideOptions}>
<VariableInput
value={picker.queryValue}
onChange={this.props.filterOrSearchOptions}
onNavigate={this.props.navigateOptions}
/>
<VariableOptions
values={picker.options}
onToggle={this.onToggleOption}
onToggleAll={this.props.toggleAllOptions}
onToggleTag={this.props.toggleAndFetchTag}
highlightIndex={picker.highlightIndex}
multi={picker.multi}
tags={picker.tags}
selectedValues={picker.selectedValues}
/>
</ClickOutsideWrapper>
);
}
}
}
const OptionsPicker = connector(OptionsPickerUnconnected);
OptionsPicker.displayName = 'OptionsPicker';
return OptionsPicker;
};
const getSelectedTags = (variable: VariableWithOptions): VariableTag[] => {
if (!isQuery(variable) || !Array.isArray(variable.tags)) {
......@@ -121,20 +126,3 @@ const getSelectedTags = (variable: VariableWithOptions): VariableTag[] => {
}
return variable.tags.filter((t) => t.selected);
};
const mapDispatchToProps: MapDispatchToProps<DispatchProps, OwnProps> = {
showOptions,
commitChangesToVariable,
filterOrSearchOptions,
toggleAllOptions,
toggleOption,
toggleAndFetchTag,
navigateOptions,
};
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = (state) => ({
picker: state.templating.optionsPicker,
});
export const OptionsPicker = connect(mapStateToProps, mapDispatchToProps)(OptionsPickerUnconnected);
OptionsPicker.displayName = 'OptionsPicker';
import { reduxTester } from '../../../../../test/core/redux/reduxTester';
import { getRootReducer } from '../../state/helpers';
import { TemplatingState } from '../../state/reducers';
import { getRootReducer, RootReducerType } from '../../state/helpers';
import { initialVariableModelState, QueryVariableModel, VariableRefresh, VariableSort } from '../../types';
import {
hideOptions,
......@@ -53,7 +52,7 @@ describe('options picker actions', () => {
const clearOthers = false;
const key = NavigationKey.cancel;
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -86,7 +85,7 @@ describe('options picker actions', () => {
const clearOthers = false;
const key = NavigationKey.select;
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -109,7 +108,7 @@ describe('options picker actions', () => {
const clearOthers = true;
const key = NavigationKey.select;
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -128,7 +127,7 @@ describe('options picker actions', () => {
const clearOthers = true;
const key = NavigationKey.select;
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -149,7 +148,7 @@ describe('options picker actions', () => {
const clearOthers = true;
const key = NavigationKey.select;
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -171,7 +170,7 @@ describe('options picker actions', () => {
const clearOthers = false;
const key = NavigationKey.selectAndClose;
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -205,7 +204,7 @@ describe('options picker actions', () => {
const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false });
const filter = 'A';
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -220,7 +219,7 @@ describe('options picker actions', () => {
const options = [createOption('A', 'A', true), createOption('B'), createOption('C')];
const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false });
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -247,7 +246,7 @@ describe('options picker actions', () => {
const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false });
const clearOthers = false;
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -278,7 +277,7 @@ describe('options picker actions', () => {
const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false });
const clearOthers = false;
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -310,7 +309,7 @@ describe('options picker actions', () => {
const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false });
const clearOthers = false;
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -329,7 +328,7 @@ describe('options picker actions', () => {
const variable = createMultiVariable({ options, current: createOption(['A'], ['A'], true), includeAll: false });
const clearOthers = false;
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -365,7 +364,7 @@ describe('options picker actions', () => {
tags: [tag],
});
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......@@ -391,7 +390,7 @@ describe('options picker actions', () => {
// @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 }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(showOptions(variable))
......
......@@ -78,7 +78,7 @@ const setVariable = async (updated: VariableWithMultiSupport) => {
return;
};
export const commitChangesToVariable = (callback?: (updated: VariableWithMultiSupport) => void): ThunkResult<void> => {
export const commitChangesToVariable = (callback?: (updated: any) => void): ThunkResult<void> => {
return async (dispatch, getState) => {
const picker = getState().templating.optionsPicker;
const existing = getVariable<VariableWithMultiSupport>(picker.id, getState());
......
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { cloneDeep, isString, trim } from 'lodash';
import { VariableOption, VariableTag, VariableWithMultiSupport } from '../../types';
import { VariableOption, VariableTag, VariableWithMultiSupport, VariableWithOptions } from '../../types';
import { ALL_VARIABLE_VALUE } from '../../state/types';
import { isQuery } from '../../guard';
import { isMulti, isQuery } from '../../guard';
import { applyStateChanges } from '../../../../core/utils/applyStateChanges';
import { containsSearchFilter } from '../../utils';
......@@ -119,15 +119,19 @@ const optionsPickerSlice = createSlice({
name: 'templating/optionsPicker',
initialState,
reducers: {
showOptions: (state, action: PayloadAction<VariableWithMultiSupport>): OptionsPickerState => {
const { query, options, multi } = action.payload;
showOptions: (state, action: PayloadAction<VariableWithOptions>): OptionsPickerState => {
const { query, options } = action.payload;
state.highlightIndex = -1;
state.options = cloneDeep(options);
state.tags = getTags(action.payload);
state.multi = multi ?? false;
state.id = action.payload.id;
state.queryValue = '';
state.multi = false;
if (isMulti(action.payload)) {
state.tags = getTags(action.payload);
state.multi = action.payload.multi ?? false;
}
if (isQuery(action.payload)) {
const { queryValue } = action.payload;
......
export { OptionsPicker } from './OptionsPicker/OptionsPicker';
export { optionPickerFactory } from './OptionsPicker/OptionsPicker';
import React, { ChangeEvent, PureComponent } from 'react';
import React, { ChangeEvent, FormEvent, PureComponent } from 'react';
import { css } from 'emotion';
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
import { InlineField, InlineFieldRow, VerticalGroup } from '@grafana/ui';
......@@ -92,34 +92,34 @@ export class QueryVariableEditorUnConnected extends PureComponent<Props, State>
}
};
onRegExChange = (event: ChangeEvent<HTMLInputElement>) => {
this.setState({ regex: event.target.value });
onRegExChange = (event: FormEvent<HTMLInputElement>) => {
this.setState({ regex: event.currentTarget.value });
};
onRegExBlur = async (event: ChangeEvent<HTMLInputElement>) => {
const regex = event.target.value;
onRegExBlur = async (event: FormEvent<HTMLInputElement>) => {
const regex = event.currentTarget.value;
if (this.props.variable.regex !== regex) {
this.props.onPropChange({ propName: 'regex', propValue: regex, updateOptions: true });
}
};
onTagsQueryChange = async (event: ChangeEvent<HTMLInputElement>) => {
this.setState({ tagsQuery: event.target.value });
onTagsQueryChange = async (event: FormEvent<HTMLInputElement>) => {
this.setState({ tagsQuery: event.currentTarget.value });
};
onTagsQueryBlur = async (event: ChangeEvent<HTMLInputElement>) => {
const tagsQuery = event.target.value;
onTagsQueryBlur = async (event: FormEvent<HTMLInputElement>) => {
const tagsQuery = event.currentTarget.value;
if (this.props.variable.tagsQuery !== tagsQuery) {
this.props.onPropChange({ propName: 'tagsQuery', propValue: tagsQuery, updateOptions: true });
}
};
onTagValuesQueryChange = async (event: ChangeEvent<HTMLInputElement>) => {
this.setState({ tagValuesQuery: event.target.value });
onTagValuesQueryChange = async (event: FormEvent<HTMLInputElement>) => {
this.setState({ tagValuesQuery: event.currentTarget.value });
};
onTagValuesQueryBlur = async (event: ChangeEvent<HTMLInputElement>) => {
const tagValuesQuery = event.target.value;
onTagValuesQueryBlur = async (event: FormEvent<HTMLInputElement>) => {
const tagValuesQuery = event.currentTarget.value;
if (this.props.variable.tagValuesQuery !== tagValuesQuery) {
this.props.onPropChange({ propName: 'tagValuesQuery', propValue: tagValuesQuery, updateOptions: true });
}
......
......@@ -3,7 +3,7 @@ import { getDefaultTimeRange, LoadingState } from '@grafana/data';
import { variableAdapters } from '../adapters';
import { createQueryVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { getRootReducer } from '../state/helpers';
import { getRootReducer, RootReducerType } from '../state/helpers';
import { QueryVariableModel, VariableHide, VariableRefresh, VariableSort } from '../types';
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, toVariablePayload } from '../state/types';
import {
......@@ -14,7 +14,6 @@ import {
variableStateFailed,
variableStateFetching,
} from '../state/sharedReducer';
import { TemplatingState } from '../state/reducers';
import {
changeQueryVariableDataSource,
changeQueryVariableQuery,
......@@ -87,7 +86,7 @@ describe('query actions', () => {
mockDatasourceMetrics(variable, optionsMetrics, tagsMetrics);
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateQueryVariableOptions(toVariablePayload(variable)), true);
......@@ -111,7 +110,7 @@ describe('query actions', () => {
mockDatasourceMetrics(variable, optionsMetrics, tagsMetrics);
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateQueryVariableOptions(toVariablePayload(variable)), true);
......@@ -138,7 +137,7 @@ describe('query actions', () => {
mockDatasourceMetrics(variable, optionsMetrics, []);
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateQueryVariableOptions(toVariablePayload(variable)), true);
......@@ -160,7 +159,7 @@ describe('query actions', () => {
mockDatasourceMetrics(variable, optionsMetrics, []);
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateQueryVariableOptions(toVariablePayload(variable)), true);
......@@ -186,7 +185,7 @@ describe('query actions', () => {
mockDatasourceMetrics(variable, optionsMetrics, []);
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(setIdInEditor({ id: variable.id }))
......@@ -214,7 +213,7 @@ describe('query actions', () => {
mockDatasourceMetrics(variable, optionsMetrics, []);
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(setIdInEditor({ id: variable.id }))
......@@ -241,7 +240,7 @@ describe('query actions', () => {
mocks[variable.datasource!].metricFindQuery = jest.fn(() => Promise.reject(error));
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenActionIsDispatched(setIdInEditor({ id: variable.id }))
......@@ -277,7 +276,7 @@ describe('query actions', () => {
components: { VariableQueryEditor: editor },
});
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(initQueryVariableEditor(toVariablePayload(variable)), true);
......@@ -306,7 +305,7 @@ describe('query actions', () => {
components: { VariableQueryEditor: editor },
});
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(initQueryVariableEditor(toVariablePayload(variable)), true);
......@@ -334,7 +333,7 @@ describe('query actions', () => {
components: { VariableQueryEditor: editor },
});
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(initQueryVariableEditor(toVariablePayload(variable)), true);
......@@ -356,7 +355,7 @@ describe('query actions', () => {
it('then correct actions are dispatched', async () => {
const variable = createVariable({ datasource: undefined });
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(initQueryVariableEditor(toVariablePayload(variable)), true);
......@@ -380,7 +379,7 @@ describe('query actions', () => {
components: { VariableQueryEditor: editor },
});
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(changeQueryVariableDataSource(toVariablePayload(variable), 'datasource'), true);
......@@ -410,7 +409,7 @@ describe('query actions', () => {
components: {},
});
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(changeQueryVariableDataSource(toVariablePayload(variable), 'datasource'), true);
......@@ -442,7 +441,7 @@ describe('query actions', () => {
mockDatasourceMetrics({ ...variable, query }, optionsMetrics, tagsMetrics);
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(changeQueryVariableQuery(toVariablePayload(variable), query, definition), true);
......@@ -473,7 +472,7 @@ describe('query actions', () => {
mockDatasourceMetrics({ ...variable, query }, optionsMetrics, []);
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(changeQueryVariableQuery(toVariablePayload(variable), query, definition), true);
......@@ -502,7 +501,7 @@ describe('query actions', () => {
mockDatasourceMetrics({ ...variable, query }, optionsMetrics, []);
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(changeQueryVariableQuery(toVariablePayload(variable), query, definition), true);
......@@ -528,7 +527,7 @@ describe('query actions', () => {
const query = `$${variable.name}`;
const definition = 'depends on datasource variable';
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(changeQueryVariableQuery(toVariablePayload(variable), query, definition), true);
......
......@@ -5,11 +5,11 @@ import { initialQueryVariableModelState, queryVariableReducer } from './reducer'
import { dispatch } from '../../../store/store';
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
import { VariableAdapter } from '../adapters';
import { OptionsPicker } from '../pickers';
import { QueryVariableEditor } from './QueryVariableEditor';
import { updateQueryVariableOptions } from './actions';
import { ALL_VARIABLE_TEXT, toVariableIdentifier } from '../state/types';
import { containsVariable, isAllVariable } from '../utils';
import { optionPickerFactory } from '../pickers';
export const createQueryVariableAdapter = (): VariableAdapter<QueryVariableModel> => {
return {
......@@ -18,7 +18,7 @@ export const createQueryVariableAdapter = (): VariableAdapter<QueryVariableModel
name: 'Query',
initialState: initialQueryVariableModelState,
reducer: queryVariableReducer,
picker: OptionsPicker,
picker: optionPickerFactory<QueryVariableModel>(),
editor: QueryVariableEditor,
dependsOn: (variable, variableToTest) => {
return containsVariable(variable.query, variable.datasource, variable.regex, variableToTest.name);
......
import { AnyAction } from 'redux';
import { UrlQueryMap } from '@grafana/data';
import { getRootReducer, getTemplatingAndLocationRootReducer, getTemplatingRootReducer } from './helpers';
import {
getRootReducer,
getTemplatingAndLocationRootReducer,
getTemplatingRootReducer,
RootReducerType,
TemplatingAndLocationReducerType,
} from './helpers';
import { variableAdapters } from '../adapters';
import { createQueryVariableAdapter } from '../query/adapter';
import { createCustomVariableAdapter } from '../custom/adapter';
......@@ -49,7 +54,6 @@ import {
initialVariableEditorState,
setIdInEditor,
} from '../editor/reducer';
import { DashboardState, LocationState } from '../../../types';
import {
TransactionStatus,
variablesClearTransaction,
......@@ -63,6 +67,7 @@ import { ConstantVariableModel, VariableRefresh } from '../types';
import { updateVariableOptions } from '../query/reducer';
import { setVariableQueryRunner, VariableQueryRunner } from '../query/VariableQueryRunner';
import { setDataSourceSrv } from '@grafana/runtime';
import { LocationState } from 'app/types';
variableAdapters.setInit(() => [
createQueryVariableAdapter(),
......@@ -145,10 +150,12 @@ describe('shared actions', () => {
const custom = customBuilder().build();
const textbox = textboxBuilder().build();
const list = [query, constant, datasource, custom, textbox];
const preloadedState = {
templating: ({} as unknown) as TemplatingState,
location: ({ query: {} } as unknown) as LocationState,
};
const tester = await reduxTester<{ templating: TemplatingState; location: { query: UrlQueryMap } }>({
preloadedState: { templating: ({} as unknown) as TemplatingState, location: { query: {} } },
})
const tester = await reduxTester<TemplatingAndLocationReducerType>({ preloadedState })
.givenRootReducer(getTemplatingAndLocationRootReducer())
.whenActionIsDispatched(variablesInitTransaction({ uid: '' }))
.whenActionIsDispatched(initDashboardTemplating(list))
......@@ -202,9 +209,12 @@ describe('shared actions', () => {
const list = [stats, substats];
const query = { orgId: '1', 'var-stats': 'response', 'var-substats': ALL_VARIABLE_TEXT };
const tester = await reduxTester<{ templating: TemplatingState; location: { query: UrlQueryMap } }>({
preloadedState: { templating: ({} as unknown) as TemplatingState, location: { query } },
})
const preloadedState = {
templating: ({} as unknown) as TemplatingState,
location: ({ query } as unknown) as LocationState,
};
const tester = await reduxTester<TemplatingAndLocationReducerType>({ preloadedState })
.givenRootReducer(getTemplatingAndLocationRootReducer())
.whenActionIsDispatched(variablesInitTransaction({ uid: '' }))
.whenActionIsDispatched(initDashboardTemplating(list))
......@@ -558,11 +568,6 @@ describe('shared actions', () => {
});
describe('initVariablesTransaction', () => {
type ReducersUsedInContext = {
templating: TemplatingState;
dashboard: DashboardState;
location: LocationState;
};
const constant = constantBuilder().withId('constant').withName('constant').build();
const templating: any = { list: [constant] };
const uid = 'uid';
......@@ -570,7 +575,7 @@ describe('shared actions', () => {
describe('when called and the previous dashboard has completed', () => {
it('then correct actions are dispatched', async () => {
const tester = await reduxTester<ReducersUsedInContext>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenAsyncActionIsDispatched(initVariablesTransaction(uid, dashboard));
......@@ -598,7 +603,7 @@ describe('shared actions', () => {
it('then correct actions are dispatched', async () => {
const transactionState = { uid: 'previous-uid', status: TransactionStatus.Fetching };
const tester = await reduxTester<ReducersUsedInContext>({
const tester = await reduxTester<RootReducerType>({
preloadedState: ({
templating: {
transaction: transactionState,
......@@ -606,7 +611,7 @@ describe('shared actions', () => {
optionsPicker: { ...initialState },
editor: { ...initialVariableEditorState },
},
} as unknown) as ReducersUsedInContext,
} as unknown) as RootReducerType,
})
.givenRootReducer(getRootReducer())
.whenAsyncActionIsDispatched(initVariablesTransaction(uid, dashboard));
......
......@@ -516,7 +516,7 @@ export const onTimeRangeUpdated = (
}
return false;
});
}) as VariableWithOptions[];
const promises = variablesThatNeedRefresh.map((variable: VariableWithOptions) =>
dispatch(timeRangeUpdated(toVariableIdentifier(variable)))
......
......@@ -7,7 +7,8 @@ import { VariablesState } from './variablesReducer';
import { locationReducer } from '../../../core/reducers/location';
import { VariableAdapter } from '../adapters';
import { dashboardReducer } from 'app/features/dashboard/state/reducers';
import { templatingReducers } from './reducers';
import { templatingReducers, TemplatingState } from './reducers';
import { DashboardState, LocationState } from '../../../types';
export const getVariableState = (
noOfVariables: number,
......@@ -76,6 +77,8 @@ export const getRootReducer = () =>
templating: templatingReducers,
});
export type RootReducerType = { location: LocationState; dashboard: DashboardState; templating: TemplatingState };
export const getTemplatingRootReducer = () =>
combineReducers({
templating: templatingReducers,
......@@ -86,3 +89,5 @@ export const getTemplatingAndLocationRootReducer = () =>
templating: templatingReducers,
location: locationReducer,
});
export type TemplatingAndLocationReducerType = { location: LocationState; templating: TemplatingState };
......@@ -10,8 +10,7 @@ import { createConstantVariableAdapter } from '../constant/adapter';
import { VariableRefresh } from '../types';
import { constantBuilder, intervalBuilder } from '../shared/testing/builders';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from './reducers';
import { getRootReducer } from './helpers';
import { getRootReducer, RootReducerType } from './helpers';
import { toVariableIdentifier, toVariablePayload } from './types';
import {
setCurrentVariableValue,
......@@ -23,6 +22,7 @@ import { createIntervalOptions } from '../interval/reducer';
import { silenceConsoleOutput } from '../../../../test/core/utils/silenceConsoleOutput';
import { notifyApp } from '../../../core/reducers/appNotification';
import { expect } from '../../../../test/lib/common';
import { TemplatingState } from './reducers';
variableAdapters.setInit(() => [createIntervalVariableAdapter(), createConstantVariableAdapter()]);
......@@ -63,7 +63,7 @@ const getTestContext = () => {
} as unknown) as DashboardState;
const startRefreshMock = jest.fn();
const adapter = variableAdapters.get('interval');
const preloadedState = {
const preloadedState = ({
dashboard,
location: { query: '' },
templating: ({
......@@ -72,7 +72,7 @@ const getTestContext = () => {
'constant-1': { ...constant },
},
} as unknown) as TemplatingState,
};
} as unknown) as RootReducerType;
return {
interval,
......@@ -98,7 +98,7 @@ describe('when onTimeRangeUpdated is dispatched', () => {
startRefreshMock,
} = getTestContext();
const tester = await reduxTester<{ templating: TemplatingState }>({ preloadedState })
const tester = await reduxTester<RootReducerType>({ preloadedState })
.givenRootReducer(getRootReducer())
.whenAsyncActionIsDispatched(onTimeRangeUpdated(range, dependencies));
......@@ -133,7 +133,7 @@ describe('when onTimeRangeUpdated is dispatched', () => {
startRefreshMock,
} = getTestContext();
const base = await reduxTester<{ templating: TemplatingState }>({ preloadedState })
const base = await reduxTester<RootReducerType>({ preloadedState })
.givenRootReducer(getRootReducer())
.whenAsyncActionIsDispatched(setOptionAsCurrent(toVariableIdentifier(interval), interval.options[0], false));
......@@ -173,7 +173,7 @@ describe('when onTimeRangeUpdated is dispatched', () => {
adapter.updateOptions = jest.fn().mockRejectedValue(new Error('Something broke'));
const tester = await reduxTester<{ templating: TemplatingState }>({ preloadedState, debug: true })
const tester = await reduxTester<RootReducerType>({ preloadedState, debug: true })
.givenRootReducer(getRootReducer())
.whenAsyncActionIsDispatched(onTimeRangeUpdated(range, dependencies), true);
......
......@@ -2,6 +2,7 @@ import { reducerTester } from '../../../../test/core/redux/reducerTester';
import {
initialTransactionState,
transactionReducer,
TransactionState,
TransactionStatus,
variablesClearTransaction,
variablesCompleteTransaction,
......@@ -11,7 +12,7 @@ import {
describe('transactionReducer', () => {
describe('when variablesInitTransaction is dispatched', () => {
it('then state should be correct', () => {
reducerTester()
reducerTester<TransactionState>()
.givenReducer(transactionReducer, { ...initialTransactionState })
.whenActionIsDispatched(variablesInitTransaction({ uid: 'a uid' }))
.thenStateShouldEqual({ ...initialTransactionState, uid: 'a uid', status: TransactionStatus.Fetching });
......@@ -21,7 +22,7 @@ describe('transactionReducer', () => {
describe('when variablesCompleteTransaction is dispatched', () => {
describe('and transaction uid is the same', () => {
it('then state should be correct', () => {
reducerTester()
reducerTester<TransactionState>()
.givenReducer(transactionReducer, {
...initialTransactionState,
uid: 'before',
......@@ -34,7 +35,7 @@ describe('transactionReducer', () => {
describe('and transaction uid is not the same', () => {
it('then state should be correct', () => {
reducerTester()
reducerTester<TransactionState>()
.givenReducer(transactionReducer, {
...initialTransactionState,
uid: 'before',
......@@ -48,7 +49,7 @@ describe('transactionReducer', () => {
describe('when variablesClearTransaction is dispatched', () => {
it('then state should be correct', () => {
reducerTester()
reducerTester<TransactionState>()
.givenReducer(transactionReducer, {
...initialTransactionState,
uid: 'before',
......
import { createAction, PayloadAction } from '@reduxjs/toolkit';
import { createAction } from '@reduxjs/toolkit';
import { variableAdapters } from '../adapters';
import { sharedReducer } from './sharedReducer';
import { VariableModel } from '../types';
import { VariablePayload } from './types';
import { AnyAction } from 'redux';
export interface VariablesState extends Record<string, VariableModel> {}
......@@ -10,10 +10,7 @@ export const initialVariablesState: VariablesState = {};
export const cleanVariables = createAction<undefined>('templating/cleanVariables');
export const variablesReducer = (
state: VariablesState = initialVariablesState,
action: PayloadAction<VariablePayload>
): VariablesState => {
export const variablesReducer = (state: VariablesState = initialVariablesState, action: AnyAction): VariablesState => {
if (cleanVariables.match(action)) {
const globalVariables = Object.values(state).filter((v) => v.global);
if (!globalVariables) {
......
......@@ -20,8 +20,8 @@ export const createSystemVariableAdapter = (): VariableAdapter<SystemVariable<an
state: LoadingState.Done,
},
reducer: (state: any, action: any) => state,
picker: (null as unknown) as ComponentType<VariablePickerProps>,
editor: (null as unknown) as ComponentType<VariableEditorProps>,
picker: (null as unknown) as ComponentType<VariablePickerProps<SystemVariable<any>>>,
editor: (null as unknown) as ComponentType<VariableEditorProps<SystemVariable<any>>>,
dependsOn: () => {
return false;
},
......
import React, { ChangeEvent, ReactElement, useCallback } from 'react';
import React, { FormEvent, ReactElement, useCallback } from 'react';
import { VerticalGroup } from '@grafana/ui';
import { TextBoxVariableModel } from '../types';
......@@ -11,16 +11,16 @@ export interface Props extends VariableEditorProps<TextBoxVariableModel> {}
export function TextBoxVariableEditor({ onPropChange, variable: { query } }: Props): ReactElement {
const updateVariable = useCallback(
(event: ChangeEvent<HTMLInputElement>, updateOptions: boolean) => {
(event: FormEvent<HTMLInputElement>, updateOptions: boolean) => {
event.preventDefault();
onPropChange({ propName: 'originalQuery', propValue: event.target.value, updateOptions: false });
onPropChange({ propName: 'query', propValue: event.target.value, updateOptions });
onPropChange({ propName: 'originalQuery', propValue: event.currentTarget.value, updateOptions: false });
onPropChange({ propName: 'query', propValue: event.currentTarget.value, updateOptions });
},
[onPropChange]
);
const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => updateVariable(e, false), [updateVariable]);
const onBlur = useCallback((e: ChangeEvent<HTMLInputElement>) => updateVariable(e, true), [updateVariable]);
const onChange = useCallback((e: FormEvent<HTMLInputElement>) => updateVariable(e, false), [updateVariable]);
const onBlur = useCallback((e: FormEvent<HTMLInputElement>) => updateVariable(e, true), [updateVariable]);
return (
<VerticalGroup spacing="xs">
......
import { variableAdapters } from '../adapters';
import { createTextBoxVariableAdapter } from './adapter';
import { reduxTester } from '../../../../test/core/redux/reduxTester';
import { TemplatingState } from 'app/features/variables/state/reducers';
import { setTextBoxVariableOptionsFromUrl, updateTextBoxVariableOptions } from './actions';
import { getRootReducer } from '../state/helpers';
import { getRootReducer, RootReducerType } from '../state/helpers';
import { VariableOption } from '../types';
import { toVariablePayload } from '../state/types';
import { createTextBoxOptions } from './reducer';
......@@ -24,7 +23,7 @@ describe('textbox actions', () => {
const variable = textboxBuilder().withId('textbox').withName('textbox').withCurrent('A').withQuery('A').build();
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(updateTextBoxVariableOptions(toVariablePayload(variable)), true);
......@@ -42,7 +41,7 @@ describe('textbox actions', () => {
const urlValue = 'bB';
const variable = textboxBuilder().withId('textbox').withName('textbox').withCurrent('A').withQuery('A').build();
const tester = await reduxTester<{ templating: TemplatingState }>()
const tester = await reduxTester<RootReducerType>()
.givenRootReducer(getRootReducer())
.whenActionIsDispatched(addVariable(toVariablePayload(variable, { global: false, index: 0, model: variable })))
.whenAsyncActionIsDispatched(setTextBoxVariableOptionsFromUrl(toVariablePayload(variable), urlValue), true);
......
......@@ -3,7 +3,7 @@ set -e
echo -e "Collecting code stats (typescript errors & more)"
ERROR_COUNT_LIMIT=584
ERROR_COUNT_LIMIT=452
ERROR_COUNT="$(./node_modules/.bin/tsc --project tsconfig.json --noEmit --strict true | grep -oP 'Found \K(\d+)')"
if [ "$ERROR_COUNT" -gt $ERROR_COUNT_LIMIT ]; then
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment