diff --git a/frontend/src/metabase/actions/components/ActionViz/ActionParameterMapping.tsx b/frontend/src/metabase/actions/components/ActionViz/ActionParameterMapping.tsx index 2a173874041b9aadc64f0e999c2d905fe97f9d74..20dab49351701fa311981059ccb52efdc32cf7c6 100644 --- a/frontend/src/metabase/actions/components/ActionViz/ActionParameterMapping.tsx +++ b/frontend/src/metabase/actions/components/ActionViz/ActionParameterMapping.tsx @@ -68,7 +68,7 @@ export const ActionParameterMappingForm = ({ setParameterMapping( dashboardParameterId, dashcard.id, - undefined, // this is irrelevant for action parameters + -1, // this is irrelevant for action parameters target, ), ); diff --git a/frontend/src/metabase/dashboard/actions/parameters.js b/frontend/src/metabase/dashboard/actions/parameters.ts similarity index 69% rename from frontend/src/metabase/dashboard/actions/parameters.js rename to frontend/src/metabase/dashboard/actions/parameters.ts index d3571362b8b6c856f8b76cc8b6db2e56e640c2bb..b1d345726067ae6289464169e022a0312ae04a24 100644 --- a/frontend/src/metabase/dashboard/actions/parameters.js +++ b/frontend/src/metabase/dashboard/actions/parameters.ts @@ -16,6 +16,21 @@ import { updateDashboard } from "metabase/dashboard/actions/save"; import { autoWireDashcardsWithMatchingParameters } from "metabase/dashboard/actions/auto-wire-parameters/actions"; import { getParameterMappings } from "metabase/dashboard/actions/auto-wire-parameters/utils"; import { closeAutoWireParameterToast } from "metabase/dashboard/actions/auto-wire-parameters/toasts"; +import type { Dispatch, GetState } from "metabase-types/store"; +import type { + ActionDashboardCard, + CardId, + DashCardId, + Parameter, + ParameterId, + ParameterMappingOptions, + ParameterTarget, + QuestionDashboardCard, + ValuesQueryType, + ValuesSourceConfig, + ValuesSourceType, + WritebackAction, +} from "metabase-types/api"; import { isParameterValueEmpty, PULSE_PARAM_EMPTY, @@ -33,15 +48,24 @@ import { import { trackAutoApplyFiltersDisabled } from "../analytics"; +import { isQuestionDashCard } from "../utils"; import { setDashboardAttributes, setDashCardAttributes } from "./core"; import { setSidebar, closeSidebar } from "./ui"; -function updateParameter(dispatch, getState, id, parameterUpdater) { +type SingleParamUpdater = (p: Parameter) => Parameter; + +function updateParameter( + dispatch: Dispatch, + getState: GetState, + id: ParameterId, + parameterUpdater: SingleParamUpdater, +) { const dashboard = getDashboard(getState()); - const index = _.findIndex( - dashboard && dashboard.parameters, - p => p.id === id, - ); + if (!dashboard || !dashboard.parameters) { + return; + } + + const index = _.findIndex(dashboard.parameters, p => p.id === id); if (index >= 0) { const parameters = assoc( dashboard.parameters, @@ -54,7 +78,13 @@ function updateParameter(dispatch, getState, id, parameterUpdater) { } } -function updateParameters(dispatch, getState, parametersUpdater) { +type MultipleParamUpdater = (p: Parameter[]) => Parameter[]; + +function updateParameters( + dispatch: Dispatch, + getState: GetState, + parametersUpdater: MultipleParamUpdater, +) { const dashboard = getDashboard(getState()); if (dashboard) { const parameters = parametersUpdater(dashboard.parameters || []); @@ -64,46 +94,51 @@ function updateParameters(dispatch, getState, parametersUpdater) { } } -export const setEditingParameter = parameterId => dispatch => { - if (parameterId != null) { - dispatch( - setSidebar({ - name: SIDEBAR_NAME.editParameter, - props: { - parameterId, - }, - }), - ); - } else { - dispatch(closeSidebar()); - } -}; +export const setEditingParameter = + (parameterId: ParameterId) => (dispatch: Dispatch) => { + if (parameterId != null) { + dispatch( + setSidebar({ + name: SIDEBAR_NAME.editParameter, + props: { + parameterId, + }, + }), + ); + } else { + dispatch(closeSidebar()); + } + }; export const ADD_PARAMETER = "metabase/dashboard/ADD_PARAMETER"; export const addParameter = createThunkAction( ADD_PARAMETER, - parameterOption => (dispatch, getState) => { - let parameter; + (option: ParameterMappingOptions) => (dispatch, getState) => { + let newId: undefined | ParameterId = undefined; + updateParameters(dispatch, getState, parameters => { - parameter = createParameter(parameterOption, parameters); - return parameters.concat(parameter); + const parameter = createParameter(option, parameters); + newId = parameter.id; + return [...parameters, parameter]; }); - dispatch( - setSidebar({ - name: SIDEBAR_NAME.editParameter, - props: { - parameterId: parameter.id, - }, - }), - ); + if (newId) { + dispatch( + setSidebar({ + name: SIDEBAR_NAME.editParameter, + props: { + parameterId: newId, + }, + }), + ); + } }, ); export const REMOVE_PARAMETER = "metabase/dashboard/REMOVE_PARAMETER"; export const removeParameter = createThunkAction( REMOVE_PARAMETER, - parameterId => (dispatch, getState) => { + (parameterId: ParameterId) => (dispatch, getState) => { updateParameters(dispatch, getState, parameters => parameters.filter(p => p.id !== parameterId), ); @@ -115,16 +150,21 @@ export const SET_PARAMETER_MAPPING = "metabase/dashboard/SET_PARAMETER_MAPPING"; export const setParameterMapping = createThunkAction( SET_PARAMETER_MAPPING, - (parameter_id, dashcard_id, card_id, target) => { + ( + parameterId: ParameterId, + dashcardId: DashCardId, + cardId: CardId, + target: ParameterTarget | null, + ) => { return (dispatch, getState) => { dispatch(closeAutoWireParameterToast()); - const dashcard = getDashCardById(getState(), dashcard_id); + const dashcard = getDashCardById(getState(), dashcardId); - if (target !== null) { + if (target !== null && isQuestionDashCard(dashcard)) { dispatch( autoWireDashcardsWithMatchingParameters( - parameter_id, + parameterId, dashcard, target, ), @@ -133,12 +173,13 @@ export const setParameterMapping = createThunkAction( dispatch( setDashCardAttributes({ - id: dashcard_id, + id: dashcardId, attributes: { parameter_mappings: getParameterMappings( - dashcard, - parameter_id, - card_id, + // TODO remove type casting when getParameterMappings is fixed + dashcard as QuestionDashboardCard, + parameterId, + cardId, target, ), }, @@ -152,7 +193,7 @@ export const SET_ACTION_FOR_DASHCARD = "metabase/dashboard/SET_ACTION_FOR_DASHCARD"; export const setActionForDashcard = createThunkAction( SET_PARAMETER_MAPPING, - (dashcard, newAction) => dispatch => { + (dashcard: ActionDashboardCard, newAction: WritebackAction) => dispatch => { dispatch( setDashCardAttributes({ id: dashcard.id, @@ -168,7 +209,7 @@ export const setActionForDashcard = createThunkAction( export const SET_PARAMETER_NAME = "metabase/dashboard/SET_PARAMETER_NAME"; export const setParameterName = createThunkAction( SET_PARAMETER_NAME, - (parameterId, name) => (dispatch, getState) => { + (parameterId: ParameterId, name: string) => (dispatch, getState) => { updateParameter(dispatch, getState, parameterId, parameter => setParamName(parameter, name), ); @@ -178,19 +219,20 @@ export const setParameterName = createThunkAction( export const setParameterFilteringParameters = createThunkAction( SET_PARAMETER_NAME, - (parameterId, filteringParameters) => (dispatch, getState) => { - updateParameter(dispatch, getState, parameterId, parameter => ({ - ...parameter, - filteringParameters, - })); - return { id: parameterId, filteringParameters }; - }, + (parameterId: ParameterId, filteringParameters: ParameterId[]) => + (dispatch, getState) => { + updateParameter(dispatch, getState, parameterId, parameter => ({ + ...parameter, + filteringParameters, + })); + return { id: parameterId, filteringParameters }; + }, ); export const SET_PARAMETER_VALUE = "metabase/dashboard/SET_PARAMETER_VALUE"; export const setParameterValue = createThunkAction( SET_PARAMETER_VALUE, - (parameterId, value) => (_dispatch, getState) => { + (parameterId: ParameterId, value: any) => (_dispatch, getState) => { const isSettingDraftParameterValues = !getIsAutoApplyFilters(getState()); return { @@ -219,7 +261,7 @@ export const SET_PARAMETER_DEFAULT_VALUE = "metabase/dashboard/SET_PARAMETER_DEFAULT_VALUE"; export const setParameterDefaultValue = createThunkAction( SET_PARAMETER_DEFAULT_VALUE, - (parameterId, defaultValue) => (dispatch, getState) => { + (parameterId: ParameterId, defaultValue: any) => (dispatch, getState) => { updateParameter(dispatch, getState, parameterId, parameter => ({ ...parameter, default: defaultValue, @@ -232,7 +274,7 @@ export const SET_PARAMETER_VALUE_TO_DEFAULT = "metabase/dashboard/SET_PARAMETER_VALUE_TO_DEFAULT"; export const setParameterValueToDefault = createThunkAction( SET_PARAMETER_VALUE_TO_DEFAULT, - parameterId => (dispatch, getState) => { + (parameterId: ParameterId) => (dispatch, getState) => { const parameter = getParameters(getState()).find( ({ id }) => id === parameterId, ); @@ -247,15 +289,15 @@ export const SET_PARAMETER_REQUIRED = "metabase/dashboard/SET_PARAMETER_REQUIRED"; export const setParameterRequired = createThunkAction( SET_PARAMETER_REQUIRED, - (parameterId, value) => (dispatch, getState) => { + (parameterId: ParameterId, required: boolean) => (dispatch, getState) => { const parameter = getParameters(getState()).find( ({ id }) => id === parameterId, ); - if (parameter.required !== value) { + if (parameter && parameter.required !== required) { updateParameter(dispatch, getState, parameterId, parameter => ({ ...parameter, - required: value, + required, })); } }, @@ -265,7 +307,7 @@ export const SET_PARAMETER_IS_MULTI_SELECT = "metabase/dashboard/SET_PARAMETER_DEFAULT_VALUE"; export const setParameterIsMultiSelect = createThunkAction( SET_PARAMETER_IS_MULTI_SELECT, - (parameterId, isMultiSelect) => (dispatch, getState) => { + (parameterId: ParameterId, isMultiSelect: boolean) => (dispatch, getState) => { updateParameter(dispatch, getState, parameterId, parameter => ({ ...parameter, isMultiSelect: isMultiSelect, @@ -278,46 +320,54 @@ export const SET_PARAMETER_QUERY_TYPE = "metabase/dashboard/SET_PARAMETER_QUERY_TYPE"; export const setParameterQueryType = createThunkAction( SET_PARAMETER_QUERY_TYPE, - (parameterId, queryType) => (dispatch, getState) => { - updateParameter(dispatch, getState, parameterId, parameter => ({ - ...parameter, - values_query_type: queryType, - })); - return { id: parameterId, queryType }; - }, + (parameterId: ParameterId, queryType: ValuesQueryType) => + (dispatch, getState) => { + updateParameter(dispatch, getState, parameterId, parameter => ({ + ...parameter, + values_query_type: queryType, + })); + return { id: parameterId, queryType }; + }, ); export const SET_PARAMETER_SOURCE_TYPE = "metabase/dashboard/SET_PARAMETER_SOURCE_TYPE"; export const setParameterSourceType = createThunkAction( SET_PARAMETER_SOURCE_TYPE, - (parameterId, sourceType) => (dispatch, getState) => { - updateParameter(dispatch, getState, parameterId, parameter => ({ - ...parameter, - values_source_type: sourceType, - })); - return { id: parameterId, sourceType }; - }, + (parameterId: ParameterId, sourceType: ValuesSourceType) => + (dispatch, getState) => { + updateParameter(dispatch, getState, parameterId, parameter => ({ + ...parameter, + values_source_type: sourceType, + })); + return { id: parameterId, sourceType }; + }, ); export const SET_PARAMETER_SOURCE_CONFIG = "metabase/dashboard/SET_PARAMETER_SOURCE_CONFIG"; export const setParameterSourceConfig = createThunkAction( SET_PARAMETER_SOURCE_CONFIG, - (parameterId, sourceConfig) => (dispatch, getState) => { - updateParameter(dispatch, getState, parameterId, parameter => ({ - ...parameter, - values_source_config: sourceConfig, - })); - return { id: parameterId, sourceConfig }; - }, + (parameterId: ParameterId, sourceConfig: ValuesSourceConfig) => + (dispatch, getState) => { + updateParameter(dispatch, getState, parameterId, parameter => ({ + ...parameter, + values_source_config: sourceConfig, + })); + return { id: parameterId, sourceConfig }; + }, ); export const SET_PARAMETER_INDEX = "metabase/dashboard/SET_PARAMETER_INDEX"; export const setParameterIndex = createThunkAction( SET_PARAMETER_INDEX, - (parameterId, index) => (dispatch, getState) => { + (parameterId: ParameterId, index: number) => (dispatch, getState) => { const dashboard = getDashboard(getState()); + + if (!dashboard || !dashboard.parameters) { + return; + } + const parameterIndex = _.findIndex( dashboard.parameters, p => p.id === parameterId, @@ -338,7 +388,6 @@ export const setParameterIndex = createThunkAction( export const SHOW_ADD_PARAMETER_POPOVER = "metabase/dashboard/SHOW_ADD_PARAMETER_POPOVER"; - export const showAddParameterPopover = createAction(SHOW_ADD_PARAMETER_POPOVER); export const HIDE_ADD_PARAMETER_POPOVER = @@ -346,7 +395,8 @@ export const HIDE_ADD_PARAMETER_POPOVER = export const hideAddParameterPopover = createAction(HIDE_ADD_PARAMETER_POPOVER); export const setOrUnsetParameterValues = - parameterIdValuePairs => (dispatch, getState) => { + (parameterIdValuePairs: any[][]) => + (dispatch: Dispatch, getState: GetState) => { const parameterValues = getParameterValues(getState()); parameterIdValuePairs .map(([id, value]) => @@ -356,7 +406,8 @@ export const setOrUnsetParameterValues = }; export const setParameterValuesFromQueryParams = - queryParams => (dispatch, getState) => { + (queryParams: Record<string, string | string[]>) => + (dispatch: Dispatch, getState: GetState) => { const parameters = getParameters(getState()); const parameterValues = getParameterValuesByIdFromQueryParams( parameters, @@ -370,7 +421,7 @@ export const TOGGLE_AUTO_APPLY_FILTERS = "metabase/dashboard/TOGGLE_AUTO_APPLY_FILTERS"; export const toggleAutoApplyFilters = createThunkAction( TOGGLE_AUTO_APPLY_FILTERS, - isEnabled => (dispatch, getState) => { + (isEnabled: boolean) => (dispatch, getState) => { const dashboardId = getDashboardId(getState()); if (dashboardId) {