2
0

Add predefined set variable values (#497)

Closes #234
This commit is contained in:
Baptiste Arnaud
2023-05-11 17:17:24 -04:00
committed by GitHub
parent 9abc50dce5
commit bde941613c
9 changed files with 222 additions and 85 deletions

View File

@@ -35,7 +35,7 @@ type Item =
type Props<T extends Item> = {
isPopoverMatchingInputWidth?: boolean
selectedItem?: string
items: T[]
items: readonly T[]
placeholder?: string
onSelect?: (value: string | undefined, item?: T) => void
}
@@ -190,11 +190,11 @@ export const Select = <T extends Item>({
/>
<InputRightElement
width={selectedItem ? '5rem' : undefined}
width={selectedItem && isOpen ? '5rem' : undefined}
pointerEvents="none"
>
<HStack>
{selectedItem && (
{selectedItem && isOpen && (
<IconButton
onClick={clearSelection}
icon={<CloseIcon />}

View File

@@ -1,18 +1,49 @@
import { Text } from '@chakra-ui/react'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { SetVariableBlock } from '@typebot.io/schemas'
import { byId } from '@typebot.io/lib'
import { SetVariableBlock, Variable } from '@typebot.io/schemas'
import { byId, isEmpty } from '@typebot.io/lib'
export const SetVariableContent = ({ block }: { block: SetVariableBlock }) => {
const { typebot } = useTypebot()
const variableName =
typebot?.variables.find(byId(block.options.variableId))?.name ?? ''
const expression = block.options.expressionToEvaluate ?? ''
return (
<Text color={'gray.500'} noOfLines={2}>
{variableName === '' && expression === ''
<Text color={'gray.500'} noOfLines={4}>
{variableName === '' && isEmpty(block.options.expressionToEvaluate)
? 'Click to edit...'
: `${variableName} ${expression ? `= ${expression}` : ``}`}
: getExpression(typebot?.variables ?? [])(block.options)}
</Text>
)
}
const getExpression =
(variables: Variable[]) =>
(options: SetVariableBlock['options']): string | null => {
const variableName = variables.find(byId(options.variableId))?.name ?? ''
switch (options.type) {
case 'Custom':
case undefined:
return `${variableName} = ${options.expressionToEvaluate}`
case 'Map item with same index': {
const baseItemVariable = variables.find(
byId(options.mapListItemParams?.baseItemVariableId)
)
const baseListVariable = variables.find(
byId(options.mapListItemParams?.baseListVariableId)
)
const targetListVariable = variables.find(
byId(options.mapListItemParams?.targetListVariableId)
)
return `${variableName} = item in ${targetListVariable?.name} with same index as ${baseItemVariable?.name} in ${baseListVariable?.name}`
}
case 'Empty':
return `Reset ${variableName}`
case 'Random ID':
case 'Today':
case 'Tomorrow':
case 'User ID':
case 'Yesterday': {
return `${variableName} = ${options.type}`
}
}
}

View File

@@ -1,10 +1,10 @@
import { FormLabel, HStack, Stack, Switch, Text } from '@chakra-ui/react'
import { FormLabel, Stack, Text } from '@chakra-ui/react'
import { CodeEditor } from '@/components/inputs/CodeEditor'
import { SetVariableOptions, Variable } from '@typebot.io/schemas'
import { SetVariableOptions, Variable, valueTypes } from '@typebot.io/schemas'
import React from 'react'
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
import { Textarea } from '@/components/inputs'
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
import { Select } from '@/components/inputs/Select'
type Props = {
options: SetVariableOptions
@@ -15,19 +15,10 @@ export const SetVariableSettings = ({ options, onOptionsChange }: Props) => {
const updateVariableId = (variable?: Variable) =>
onOptionsChange({ ...options, variableId: variable?.id })
const updateExpression = (expressionToEvaluate: string) =>
onOptionsChange({ ...options, expressionToEvaluate })
const updateExpressionType = () =>
const updateValueType = (type?: string) =>
onOptionsChange({
...options,
isCode: options.isCode ? !options.isCode : true,
})
const updateClientExecution = (isExecutedOnClient: boolean) =>
onOptionsChange({
...options,
isExecutedOnClient,
type: type as SetVariableOptions['type'],
})
return (
@@ -42,42 +33,110 @@ export const SetVariableSettings = ({ options, onOptionsChange }: Props) => {
id="variable-search"
/>
</Stack>
<Stack>
<HStack justify="space-between">
<FormLabel mb="0" htmlFor="expression">
Value:
</FormLabel>
<HStack>
<Text fontSize="sm">Text</Text>
<Switch
size="sm"
isChecked={options.isCode ?? false}
onChange={updateExpressionType}
/>
<Text fontSize="sm">Code</Text>
</HStack>
</HStack>
{options.isCode ?? false ? (
<Stack>
<Text mb="0" fontWeight="medium">
Value:
</Text>
<Select
selectedItem={options.type ?? 'Custom'}
items={valueTypes}
onSelect={updateValueType}
/>
<SetVariableValue options={options} onOptionsChange={onOptionsChange} />
</Stack>
</Stack>
)
}
const SetVariableValue = ({
options,
onOptionsChange,
}: {
options: SetVariableOptions
onOptionsChange: (options: SetVariableOptions) => void
}): JSX.Element | null => {
const updateExpression = (expressionToEvaluate: string) =>
onOptionsChange({ ...options, expressionToEvaluate })
const updateClientExecution = (isExecutedOnClient: boolean) =>
onOptionsChange({
...options,
isExecutedOnClient,
})
const updateItemVariableId = (variable?: Variable) =>
onOptionsChange({
...options,
mapListItemParams: {
...options.mapListItemParams,
baseItemVariableId: variable?.id,
},
})
const updateBaseListVariableId = (variable?: Variable) =>
onOptionsChange({
...options,
mapListItemParams: {
...options.mapListItemParams,
baseListVariableId: variable?.id,
},
})
const updateTargetListVariableId = (variable?: Variable) =>
onOptionsChange({
...options,
mapListItemParams: {
...options.mapListItemParams,
targetListVariableId: variable?.id,
},
})
switch (options.type) {
case 'Custom':
case undefined:
return (
<>
<CodeEditor
defaultValue={options.expressionToEvaluate ?? ''}
onChange={updateExpression}
lang="javascript"
/>
) : (
<Textarea
id="expression"
defaultValue={options.expressionToEvaluate ?? ''}
onChange={updateExpression}
<SwitchWithLabel
label="Execute on client?"
moreInfoContent="Check this if you need access to client-only variables like `window` or `document`."
initialValue={options.isExecutedOnClient ?? false}
onCheckChange={updateClientExecution}
/>
)}
</Stack>
<SwitchWithLabel
label="Execute on client?"
moreInfoContent="Check this if you need access to client-only variables like `window` or `document`."
initialValue={options.isExecutedOnClient ?? false}
onCheckChange={updateClientExecution}
/>
</Stack>
)
</>
)
case 'Map item with same index': {
return (
<Stack p="2" rounded="md" borderWidth={1}>
<VariableSearchInput
initialVariableId={options.mapListItemParams?.baseItemVariableId}
onSelectVariable={updateItemVariableId}
placeholder="Base item"
/>
<VariableSearchInput
initialVariableId={options.mapListItemParams?.baseListVariableId}
onSelectVariable={updateBaseListVariableId}
placeholder="Base list"
/>
<VariableSearchInput
initialVariableId={options.mapListItemParams?.targetListVariableId}
onSelectVariable={updateTargetListVariableId}
placeholder="Target list"
/>
</Stack>
)
}
case 'Random ID':
case 'Today':
case 'Tomorrow':
case 'User ID':
case 'Yesterday':
case 'Empty':
return null
}
}

View File

@@ -22,7 +22,10 @@ test.describe('Set variable block', () => {
await page.click('text=Click to edit... >> nth = 0')
await page.fill('input[placeholder="Select a variable"] >> nth=-1', 'Total')
await page.getByRole('menuitem', { name: 'Create Total' }).click()
await page.fill('textarea', '1000 * {{Num}}')
await page
.getByTestId('code-editor')
.getByRole('textbox')
.fill('1000 * {{Num}}')
await page.click('text=Click to edit...', { force: true })
await page.fill(
@@ -30,7 +33,10 @@ test.describe('Set variable block', () => {
'Custom var'
)
await page.getByRole('menuitem', { name: 'Create Custom var' }).click()
await page.fill('textarea', 'Custom value')
await page
.getByTestId('code-editor')
.getByRole('textbox')
.fill('Custom value')
await page.click('text=Click to edit...', { force: true })
await page.fill(
@@ -38,7 +44,10 @@ test.describe('Set variable block', () => {
'Addition'
)
await page.getByRole('menuitem', { name: 'Create Addition' }).click()
await page.fill('textarea', '1000 + {{Total}}')
await page
.getByTestId('code-editor')
.getByRole('textbox')
.fill('1000 + {{Total}}')
await page.click('text=Preview')
await page