diff --git a/frontend/src/queries/nodes/DataVisualization/Components/Variables/AddVariableButton.tsx b/frontend/src/queries/nodes/DataVisualization/Components/Variables/AddVariableButton.tsx index b80618f2e823f..076f61e78b53f 100644 --- a/frontend/src/queries/nodes/DataVisualization/Components/Variables/AddVariableButton.tsx +++ b/frontend/src/queries/nodes/DataVisualization/Components/Variables/AddVariableButton.tsx @@ -57,7 +57,7 @@ export const AddVariableButton = (): JSX.Element => { ] : variables.map((n) => ({ label: n.name, - onClick: () => addVariable({ variableId: n.id, code_name: '' }), + onClick: () => addVariable({ variableId: n.id, code_name: n.code_name }), })), }, ]} diff --git a/frontend/src/queries/nodes/DataVisualization/Components/Variables/Variables.tsx b/frontend/src/queries/nodes/DataVisualization/Components/Variables/Variables.tsx index d17fb419452e4..ac902653a6c88 100644 --- a/frontend/src/queries/nodes/DataVisualization/Components/Variables/Variables.tsx +++ b/frontend/src/queries/nodes/DataVisualization/Components/Variables/Variables.tsx @@ -1,7 +1,7 @@ import './Variables.scss' import { IconCopy, IconGear, IconTrash } from '@posthog/icons' -import { LemonButton, LemonDivider, LemonInput, Popover } from '@posthog/lemon-ui' +import { LemonButton, LemonDivider, LemonInput, LemonSegmentedButton, Popover } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { FEATURE_FLAGS } from 'lib/constants' import { LemonField } from 'lib/lemon-ui/LemonField' @@ -88,7 +88,15 @@ const VariableInput = ({ onChange, onRemove, }: VariableInputProps): JSX.Element => { - const [localInputValue, setLocalInputValue] = useState(variable.value ?? variable.default_value ?? '') + const [localInputValue, setLocalInputValue] = useState(() => { + const val = variable.value ?? variable.default_value + + if (variable.type === 'Number' && !val) { + return 0 + } + + return val ?? '' + }) const inputRef = useRef(null) const codeRef = useRef(null) @@ -102,17 +110,50 @@ const VariableInput = ({ return (
- setLocalInputValue(value)} - onPressEnter={() => { - onChange(variable.id, localInputValue) - closePopover() - }} - /> + {variable.type === 'String' && ( + setLocalInputValue(value)} + onPressEnter={() => { + onChange(variable.id, localInputValue) + closePopover() + }} + /> + )} + {variable.type === 'Number' && ( + setLocalInputValue(value ?? 0)} + onPressEnter={() => { + onChange(variable.id, localInputValue) + closePopover() + }} + /> + )} + {variable.type === 'Boolean' && ( + setLocalInputValue(value === 'true')} + options={[ + { + value: 'true', + label: 'true', + }, + { + value: 'false', + label: 'false', + }, + ]} + /> + )} { @@ -213,7 +254,7 @@ const VariableComponent = ({ onClick={() => setPopoverOpen(!isPopoverOpen)} disabledReason={variableOverridesAreSet && 'Discard dashboard variables to change'} > - {variable.value ?? variable.default_value} + {variable.value?.toString() ?? variable.default_value?.toString()}
diff --git a/frontend/src/queries/nodes/DataVisualization/Components/Variables/variablesLogic.ts b/frontend/src/queries/nodes/DataVisualization/Components/Variables/variablesLogic.ts index 61f2590242a73..5bae4e212d673 100644 --- a/frontend/src/queries/nodes/DataVisualization/Components/Variables/variablesLogic.ts +++ b/frontend/src/queries/nodes/DataVisualization/Components/Variables/variablesLogic.ts @@ -7,7 +7,7 @@ import { getVariablesFromQuery, haveVariablesOrFiltersChanged } from 'scenes/ins import { DataVisualizationNode, HogQLVariable } from '~/queries/schema' import { dataVisualizationLogic } from '../../dataVisualizationLogic' -import { Variable } from '../../types' +import { Variable, VariableType } from '../../types' import { variableDataLogic } from './variableDataLogic' import type { variablesLogicType } from './variablesLogicType' @@ -17,6 +17,18 @@ export interface VariablesLogicProps { readOnly: boolean } +const convertValueToCorrectType = (value: string, type: VariableType): number | string | boolean => { + if (type === 'Number') { + return Number(value) + } + + if (type === 'Boolean' && typeof value === 'string') { + return value.toLowerCase() === 'true' + } + + return value +} + export const variablesLogic = kea([ path(['queries', 'nodes', 'DataVisualization', 'Components', 'Variables', 'variablesLogic']), props({ key: '' } as VariablesLogicProps), @@ -32,15 +44,18 @@ export const variablesLogic = kea([ ['featureFlags'], ], }), - actions({ + actions(({ values }) => ({ addVariable: (variable: HogQLVariable) => ({ variable }), addVariables: (variables: HogQLVariable[]) => ({ variables }), removeVariable: (variableId: string) => ({ variableId }), - updateVariableValue: (variableId: string, value: any) => ({ variableId, value }), + updateVariableValue: (variableId: string, value: any) => ({ + variableId, + value, + allVariables: values.variables, + }), setEditorQuery: (query: string) => ({ query }), - resetVariables: true, updateSourceQuery: true, - }), + })), reducers({ internalSelectedVariables: [ [] as HogQLVariable[], @@ -52,17 +67,20 @@ export const variablesLogic = kea([ return [...state, { ...variable }] }, - addVariables: (state, { variables }) => { - return [...state, ...variables.map((n) => ({ ...n }))] + addVariables: (_state, { variables }) => { + return [...variables.map((n) => ({ ...n }))] }, - updateVariableValue: (state, { variableId, value }) => { + updateVariableValue: (state, { variableId, value, allVariables }) => { const variableIndex = state.findIndex((n) => n.variableId === variableId) if (variableIndex < 0) { return state } + const variableType = allVariables.find((n) => n.id === variableId)?.type + const valueWithType = convertValueToCorrectType(value, variableType ?? 'String') + const variablesInState = [...state] - variablesInState[variableIndex] = { ...variablesInState[variableIndex], value } + variablesInState[variableIndex] = { ...variablesInState[variableIndex], value: valueWithType } return variablesInState }, @@ -70,12 +88,11 @@ export const variablesLogic = kea([ const stateCopy = [...state] const index = stateCopy.findIndex((n) => n.variableId === variableId) if (index >= 0) { - stateCopy.splice(index) + stateCopy.splice(index, 1) } return stateCopy }, - resetVariables: () => [], }, ], editorQuery: [ @@ -88,9 +105,9 @@ export const variablesLogic = kea([ }), selectors({ variablesForInsight: [ - (s) => [s.variables, s.internalSelectedVariables, s.variablesLoading], - (variables, internalSelectedVariables, variablesLoading): Variable[] => { - if (!variables.length || !internalSelectedVariables.length || variablesLoading) { + (s) => [s.variables, s.internalSelectedVariables], + (variables, internalSelectedVariables): Variable[] => { + if (!variables.length || !internalSelectedVariables.length) { return [] } @@ -128,16 +145,16 @@ export const variablesLogic = kea([ return } - const variables = values.variablesForInsight + const variables = values.internalSelectedVariables const query: DataVisualizationNode = { ...values.query, source: { ...values.query.source, variables: variables.reduce((acc, cur) => { - if (cur.id) { - acc[cur.id] = { - variableId: cur.id, + if (cur.variableId) { + acc[cur.variableId] = { + variableId: cur.variableId, value: cur.value, code_name: cur.code_name, } @@ -176,7 +193,7 @@ export const variablesLogic = kea([ return } - const variableAlreadySelected = values.variablesForInsight.find((n) => n.code_name === match) + const variableAlreadySelected = values.internalSelectedVariables.find((n) => n.code_name === match) if (!variableAlreadySelected) { actions.addVariable({ variableId: variableExists.id, code_name: variableExists.code_name }) } @@ -187,8 +204,6 @@ export const variablesLogic = kea([ return } - actions.resetVariables() - const variables = Object.values(query.source.variables ?? {}) if (variables.length) { diff --git a/posthog/hogql/variables.py b/posthog/hogql/variables.py index f4e8458823810..efad02ac95417 100644 --- a/posthog/hogql/variables.py +++ b/posthog/hogql/variables.py @@ -44,7 +44,11 @@ def visit_placeholder(self, node): if not matching_insight_variable: raise QueryError(f"Variable {variable_code_name} does not exist") - value = matching_variable.value or matching_insight_variable[0].default_value + value = ( + matching_variable.value + if matching_variable.value is not None + else matching_insight_variable[0].default_value + ) return ast.Constant(value=value)