@@ -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 />}
|
||||
|
||||
@@ -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}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user