mirror of
https://github.com/grafana/grafana.git
synced 2025-09-28 13:34:27 +08:00

* Variables: move state tree into a keyed state * Update public/app/features/variables/state/transactionReducer.ts Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com> * Chore: fix prettier error * Chore: renamed slices and lastUid * Chore: rename toUidAction * Chore: rename dashboardVariableReducer * Chore: rename state prop back to templating * Chore renames variable.dashboardUid * Chore: rename toDashboardVariableIdentifier * Chore: rename getDashboardVariable * Chore: rename getDashboardVariablesState * Chore: rename getDashboardVariables * Chore: some more renames * Chore: small clean up * Chore: small rename * Chore: removes unused function * Chore: rename VariableModel.stateKey * Chore: rename KeyedVariableIdentifier.stateKey * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com> Co-authored-by: kay delaney <kay@grafana.com> Co-authored-by: Alexandra Vargas <alexa1866@gmail.com> Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
176 lines
6.1 KiB
TypeScript
176 lines
6.1 KiB
TypeScript
import { debounce, trim } from 'lodash';
|
|
import { StoreState, ThunkDispatch, ThunkResult } from 'app/types';
|
|
import { VariableOption, VariableWithMultiSupport, VariableWithOptions } from '../../types';
|
|
import { variableAdapters } from '../../adapters';
|
|
import { getVariable, getVariablesState } from '../../state/selectors';
|
|
import { NavigationKey } from '../types';
|
|
import {
|
|
hideOptions,
|
|
moveOptionsHighlight,
|
|
OptionsPickerState,
|
|
showOptions,
|
|
toggleOption,
|
|
updateOptionsAndFilter,
|
|
updateOptionsFromSearch,
|
|
updateSearchQuery,
|
|
} from './reducer';
|
|
import { changeVariableProp, setCurrentVariableValue } from '../../state/sharedReducer';
|
|
import { KeyedVariableIdentifier } from '../../state/types';
|
|
import { containsSearchFilter, getCurrentText, toVariablePayload } from '../../utils';
|
|
import { toKeyedAction } from '../../state/keyedVariablesReducer';
|
|
|
|
export const navigateOptions = (rootStateKey: string, key: NavigationKey, clearOthers: boolean): ThunkResult<void> => {
|
|
return async (dispatch, getState) => {
|
|
if (key === NavigationKey.cancel) {
|
|
return await dispatch(commitChangesToVariable(rootStateKey));
|
|
}
|
|
|
|
if (key === NavigationKey.select) {
|
|
return dispatch(toggleOptionByHighlight(rootStateKey, clearOthers));
|
|
}
|
|
|
|
if (key === NavigationKey.selectAndClose) {
|
|
dispatch(toggleOptionByHighlight(rootStateKey, clearOthers, true));
|
|
return await dispatch(commitChangesToVariable(rootStateKey));
|
|
}
|
|
|
|
if (key === NavigationKey.moveDown) {
|
|
return dispatch(toKeyedAction(rootStateKey, moveOptionsHighlight(1)));
|
|
}
|
|
|
|
if (key === NavigationKey.moveUp) {
|
|
return dispatch(toKeyedAction(rootStateKey, moveOptionsHighlight(-1)));
|
|
}
|
|
|
|
return undefined;
|
|
};
|
|
};
|
|
|
|
export const filterOrSearchOptions = (
|
|
passedIdentifier: KeyedVariableIdentifier,
|
|
searchQuery = ''
|
|
): ThunkResult<void> => {
|
|
return async (dispatch, getState) => {
|
|
const { rootStateKey } = passedIdentifier;
|
|
const { id, queryValue } = getVariablesState(rootStateKey, getState()).optionsPicker;
|
|
const identifier: KeyedVariableIdentifier = { id, rootStateKey: rootStateKey, type: 'query' };
|
|
const { query, options } = getVariable<VariableWithOptions>(identifier, getState());
|
|
dispatch(toKeyedAction(rootStateKey, updateSearchQuery(searchQuery)));
|
|
|
|
if (trim(queryValue) === trim(searchQuery)) {
|
|
return;
|
|
}
|
|
|
|
if (containsSearchFilter(query)) {
|
|
return searchForOptionsWithDebounce(dispatch, getState, searchQuery, rootStateKey);
|
|
}
|
|
return dispatch(toKeyedAction(rootStateKey, updateOptionsAndFilter(options)));
|
|
};
|
|
};
|
|
|
|
const setVariable = async (updated: VariableWithMultiSupport) => {
|
|
const adapter = variableAdapters.get(updated.type);
|
|
await adapter.setValue(updated, updated.current, true);
|
|
return;
|
|
};
|
|
|
|
export const commitChangesToVariable = (key: string, callback?: (updated: any) => void): ThunkResult<void> => {
|
|
return async (dispatch, getState) => {
|
|
const picker = getVariablesState(key, getState()).optionsPicker;
|
|
const identifier: KeyedVariableIdentifier = { id: picker.id, rootStateKey: key, type: 'query' };
|
|
const existing = getVariable<VariableWithMultiSupport>(identifier, getState());
|
|
const currentPayload = { option: mapToCurrent(picker) };
|
|
const searchQueryPayload = { propName: 'queryValue', propValue: picker.queryValue };
|
|
|
|
dispatch(toKeyedAction(key, setCurrentVariableValue(toVariablePayload(existing, currentPayload))));
|
|
dispatch(toKeyedAction(key, changeVariableProp(toVariablePayload(existing, searchQueryPayload))));
|
|
const updated = getVariable<VariableWithMultiSupport>(identifier, getState());
|
|
dispatch(toKeyedAction(key, hideOptions()));
|
|
|
|
if (getCurrentText(existing) === getCurrentText(updated)) {
|
|
return;
|
|
}
|
|
|
|
if (callback) {
|
|
return callback(updated);
|
|
}
|
|
|
|
return await setVariable(updated);
|
|
};
|
|
};
|
|
|
|
export const openOptions =
|
|
(identifier: KeyedVariableIdentifier, callback?: (updated: any) => void): ThunkResult<void> =>
|
|
async (dispatch, getState) => {
|
|
const { id, rootStateKey: uid } = identifier;
|
|
const picker = getVariablesState(uid, getState()).optionsPicker;
|
|
|
|
if (picker.id && picker.id !== id) {
|
|
await dispatch(commitChangesToVariable(uid, callback));
|
|
}
|
|
|
|
const variable = getVariable<VariableWithMultiSupport>(identifier, getState());
|
|
dispatch(toKeyedAction(uid, showOptions(variable)));
|
|
};
|
|
|
|
export const toggleOptionByHighlight = (key: string, clearOthers: boolean, forceSelect = false): ThunkResult<void> => {
|
|
return (dispatch, getState) => {
|
|
const { highlightIndex, options } = getVariablesState(key, getState()).optionsPicker;
|
|
const option = options[highlightIndex];
|
|
dispatch(toKeyedAction(key, toggleOption({ option, forceSelect, clearOthers })));
|
|
};
|
|
};
|
|
|
|
const searchForOptions = async (
|
|
dispatch: ThunkDispatch,
|
|
getState: () => StoreState,
|
|
searchQuery: string,
|
|
key: string
|
|
) => {
|
|
try {
|
|
const { id } = getVariablesState(key, getState()).optionsPicker;
|
|
const identifier: KeyedVariableIdentifier = { id, rootStateKey: key, type: 'query' };
|
|
const existing = getVariable<VariableWithOptions>(identifier, getState());
|
|
|
|
const adapter = variableAdapters.get(existing.type);
|
|
await adapter.updateOptions(existing, searchQuery);
|
|
|
|
const updated = getVariable<VariableWithOptions>(identifier, getState());
|
|
dispatch(toKeyedAction(key, updateOptionsFromSearch(updated.options)));
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
};
|
|
|
|
const searchForOptionsWithDebounce = debounce(searchForOptions, 500);
|
|
|
|
export function mapToCurrent(picker: OptionsPickerState): VariableOption | undefined {
|
|
const { options, selectedValues, queryValue: searchQuery, multi } = picker;
|
|
|
|
if (options.length === 0 && searchQuery && searchQuery.length > 0) {
|
|
return { text: searchQuery, value: searchQuery, selected: false };
|
|
}
|
|
|
|
if (!multi) {
|
|
return selectedValues.find((o) => o.selected);
|
|
}
|
|
|
|
const texts: string[] = [];
|
|
const values: string[] = [];
|
|
|
|
for (const option of selectedValues) {
|
|
if (!option.selected) {
|
|
continue;
|
|
}
|
|
|
|
texts.push(option.text.toString());
|
|
values.push(option.value.toString());
|
|
}
|
|
|
|
return {
|
|
value: values,
|
|
text: texts,
|
|
selected: true,
|
|
};
|
|
}
|