@@ -48,7 +48,6 @@ test.describe.parallel('Image bubble block', () => {
|
||||
await page.goto(`/typebots/${typebotId}/edit`)
|
||||
|
||||
await page.click('text=Click to edit...')
|
||||
await page.click('text=Embed link')
|
||||
await page.fill(
|
||||
'input[placeholder="Paste the image link..."]',
|
||||
unsplashImageSrc
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { TextInput } from '@/components/inputs'
|
||||
import { MoreInfoTooltip } from '@/components/MoreInfoTooltip'
|
||||
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
|
||||
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
|
||||
import { FormControl, FormLabel, Stack } from '@chakra-ui/react'
|
||||
import { ChoiceInputOptions, Variable } from '@typebot.io/schemas'
|
||||
import {
|
||||
ChoiceInputOptions,
|
||||
Variable,
|
||||
defaultChoiceInputOptions,
|
||||
} from '@typebot.io/schemas'
|
||||
import React from 'react'
|
||||
import { SwitchWithRelatedSettings } from '@/components/SwitchWithRelatedSettings'
|
||||
|
||||
type Props = {
|
||||
options?: ChoiceInputOptions
|
||||
@@ -18,6 +22,8 @@ export const ButtonsBlockSettings = ({ options, onOptionsChange }: Props) => {
|
||||
options && onOptionsChange({ ...options, isSearchable })
|
||||
const updateButtonLabel = (buttonLabel: string) =>
|
||||
options && onOptionsChange({ ...options, buttonLabel })
|
||||
const updateSearchInputPlaceholder = (searchInputPlaceholder: string) =>
|
||||
options && onOptionsChange({ ...options, searchInputPlaceholder })
|
||||
const updateSaveVariable = (variable?: Variable) =>
|
||||
options && onOptionsChange({ ...options, variableId: variable?.id })
|
||||
const updateDynamicDataVariable = (variable?: Variable) =>
|
||||
@@ -25,23 +31,31 @@ export const ButtonsBlockSettings = ({ options, onOptionsChange }: Props) => {
|
||||
|
||||
return (
|
||||
<Stack spacing={4}>
|
||||
<SwitchWithLabel
|
||||
<SwitchWithRelatedSettings
|
||||
label="Multiple choice?"
|
||||
initialValue={options?.isMultipleChoice ?? false}
|
||||
onCheckChange={updateIsMultiple}
|
||||
/>
|
||||
<SwitchWithLabel
|
||||
label="Is searchable?"
|
||||
initialValue={options?.isSearchable ?? false}
|
||||
onCheckChange={updateIsSearchable}
|
||||
/>
|
||||
{options?.isMultipleChoice && (
|
||||
>
|
||||
<TextInput
|
||||
label="Button label:"
|
||||
label="Submit button label:"
|
||||
defaultValue={options?.buttonLabel ?? 'Send'}
|
||||
onChange={updateButtonLabel}
|
||||
/>
|
||||
)}
|
||||
</SwitchWithRelatedSettings>
|
||||
<SwitchWithRelatedSettings
|
||||
label="Is searchable?"
|
||||
initialValue={options?.isSearchable ?? false}
|
||||
onCheckChange={updateIsSearchable}
|
||||
>
|
||||
<TextInput
|
||||
label="Input placeholder:"
|
||||
defaultValue={
|
||||
options?.searchInputPlaceholder ??
|
||||
defaultChoiceInputOptions.searchInputPlaceholder
|
||||
}
|
||||
onChange={updateSearchInputPlaceholder}
|
||||
/>
|
||||
</SwitchWithRelatedSettings>
|
||||
<FormControl>
|
||||
<FormLabel>
|
||||
Dynamic data:{' '}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { ImageIcon } from '@/components/icons'
|
||||
import { IconProps } from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
|
||||
export const PictureChoiceIcon = (props: IconProps) => (
|
||||
<ImageIcon color="orange.500" {...props} />
|
||||
)
|
||||
@@ -0,0 +1,148 @@
|
||||
import {
|
||||
Fade,
|
||||
IconButton,
|
||||
Flex,
|
||||
Image,
|
||||
Popover,
|
||||
Portal,
|
||||
PopoverContent,
|
||||
PopoverArrow,
|
||||
PopoverBody,
|
||||
PopoverAnchor,
|
||||
useEventListener,
|
||||
useColorModeValue,
|
||||
} from '@chakra-ui/react'
|
||||
import { ImageIcon, PlusIcon } from '@/components/icons'
|
||||
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
|
||||
import { ItemIndices, ItemType } from '@typebot.io/schemas'
|
||||
import React, { useRef } from 'react'
|
||||
import { PictureChoiceItem } from '@typebot.io/schemas/features/blocks/inputs/pictureChoice'
|
||||
import { useGraph } from '@/features/graph/providers/GraphProvider'
|
||||
import { PictureChoiceItemSettings } from './PictureChoiceItemSettings'
|
||||
|
||||
type Props = {
|
||||
item: PictureChoiceItem
|
||||
indices: ItemIndices
|
||||
isMouseOver: boolean
|
||||
}
|
||||
|
||||
export const PictureChoiceItemNode = ({
|
||||
item,
|
||||
indices,
|
||||
isMouseOver,
|
||||
}: Props) => {
|
||||
const emptyImageBgColor = useColorModeValue('gray.100', 'gray.700')
|
||||
const { openedItemId, setOpenedItemId } = useGraph()
|
||||
const { updateItem, createItem, typebot } = useTypebot()
|
||||
const ref = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
const handlePlusClick = (e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
const itemIndex = indices.itemIndex + 1
|
||||
createItem(
|
||||
{ blockId: item.blockId, type: ItemType.PICTURE_CHOICE },
|
||||
{ ...indices, itemIndex }
|
||||
)
|
||||
}
|
||||
|
||||
const handleMouseDown = (e: React.MouseEvent) => e.stopPropagation()
|
||||
|
||||
const openPopover = () => {
|
||||
setOpenedItemId(item.id)
|
||||
}
|
||||
|
||||
const handleItemChange = (updates: Partial<PictureChoiceItem>) => {
|
||||
updateItem(indices, { ...item, ...updates })
|
||||
}
|
||||
|
||||
const handleMouseWheel = (e: WheelEvent) => {
|
||||
e.stopPropagation()
|
||||
}
|
||||
useEventListener('wheel', handleMouseWheel, ref.current)
|
||||
|
||||
return (
|
||||
<Popover
|
||||
placement="right"
|
||||
isLazy
|
||||
isOpen={openedItemId === item.id}
|
||||
closeOnBlur={false}
|
||||
>
|
||||
<PopoverAnchor>
|
||||
<Flex
|
||||
px={4}
|
||||
py={2}
|
||||
justify="center"
|
||||
w="full"
|
||||
pos="relative"
|
||||
onClick={openPopover}
|
||||
data-testid="item-node"
|
||||
userSelect="none"
|
||||
>
|
||||
{item.pictureSrc ? (
|
||||
<Image
|
||||
src={item.pictureSrc}
|
||||
alt="Picture choice image"
|
||||
rounded="md"
|
||||
maxH="128px"
|
||||
w="full"
|
||||
objectFit="cover"
|
||||
userSelect="none"
|
||||
draggable={false}
|
||||
/>
|
||||
) : (
|
||||
<Flex
|
||||
width="full"
|
||||
height="100px"
|
||||
bgColor={emptyImageBgColor}
|
||||
rounded="md"
|
||||
justify="center"
|
||||
align="center"
|
||||
>
|
||||
<ImageIcon />
|
||||
</Flex>
|
||||
)}
|
||||
<Fade
|
||||
in={isMouseOver}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: '-15px',
|
||||
zIndex: 3,
|
||||
left: '90px',
|
||||
}}
|
||||
unmountOnExit
|
||||
>
|
||||
<IconButton
|
||||
aria-label="Add item"
|
||||
icon={<PlusIcon />}
|
||||
size="xs"
|
||||
shadow="md"
|
||||
colorScheme="gray"
|
||||
borderWidth={1}
|
||||
onClick={handlePlusClick}
|
||||
/>
|
||||
</Fade>
|
||||
</Flex>
|
||||
</PopoverAnchor>
|
||||
<Portal>
|
||||
<PopoverContent pos="relative" onMouseDown={handleMouseDown}>
|
||||
<PopoverArrow />
|
||||
<PopoverBody
|
||||
py="6"
|
||||
overflowY="scroll"
|
||||
maxH="400px"
|
||||
shadow="lg"
|
||||
ref={ref}
|
||||
>
|
||||
{typebot && (
|
||||
<PictureChoiceItemSettings
|
||||
typebotId={typebot.id}
|
||||
item={item}
|
||||
onItemChange={handleItemChange}
|
||||
/>
|
||||
)}
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Portal>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import React from 'react'
|
||||
import { TextInput, Textarea } from '@/components/inputs'
|
||||
import { PictureChoiceItem } from '@typebot.io/schemas/features/blocks/inputs/pictureChoice'
|
||||
import {
|
||||
Button,
|
||||
HStack,
|
||||
Popover,
|
||||
PopoverAnchor,
|
||||
PopoverContent,
|
||||
Stack,
|
||||
Text,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react'
|
||||
import { ImageUploadContent } from '@/components/ImageUploadContent'
|
||||
|
||||
type Props = {
|
||||
typebotId: string
|
||||
item: PictureChoiceItem
|
||||
onItemChange: (updates: Partial<PictureChoiceItem>) => void
|
||||
}
|
||||
|
||||
export const PictureChoiceItemSettings = ({
|
||||
typebotId,
|
||||
item,
|
||||
onItemChange,
|
||||
}: Props) => {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||
const updateTitle = (title: string) => onItemChange({ ...item, title })
|
||||
|
||||
const updateImage = (pictureSrc: string) => {
|
||||
onItemChange({ ...item, pictureSrc })
|
||||
onClose()
|
||||
}
|
||||
|
||||
const updateDescription = (description: string) =>
|
||||
onItemChange({ ...item, description })
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<HStack>
|
||||
<Text fontWeight="medium">Image:</Text>
|
||||
<Popover isLazy isOpen={isOpen}>
|
||||
<PopoverAnchor>
|
||||
<Button size="sm" onClick={onOpen}>
|
||||
Pick an image
|
||||
</Button>
|
||||
</PopoverAnchor>
|
||||
<PopoverContent p="4" w="500px">
|
||||
<ImageUploadContent
|
||||
filePath={`typebots/${typebotId}/blocks/${item.blockId}/items/${item.id}`}
|
||||
defaultUrl={item.pictureSrc}
|
||||
onSubmit={updateImage}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</HStack>
|
||||
<TextInput
|
||||
label="Title:"
|
||||
defaultValue={item.title}
|
||||
onChange={updateTitle}
|
||||
/>
|
||||
<Textarea
|
||||
label="Description:"
|
||||
defaultValue={item.description}
|
||||
onChange={updateDescription}
|
||||
/>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { BlockIndices } from '@typebot.io/schemas'
|
||||
import React from 'react'
|
||||
import { Stack, Tag, Wrap, Text } from '@chakra-ui/react'
|
||||
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
|
||||
import { SetVariableLabel } from '@/components/SetVariableLabel'
|
||||
import { ItemNodesList } from '@/features/graph/components/nodes/item/ItemNodesList'
|
||||
import { PictureChoiceBlock } from '@typebot.io/schemas/features/blocks/inputs/pictureChoice'
|
||||
|
||||
type Props = {
|
||||
block: PictureChoiceBlock
|
||||
indices: BlockIndices
|
||||
}
|
||||
|
||||
export const PictureChoiceNode = ({ block, indices }: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
const dynamicVariableName = typebot?.variables.find(
|
||||
(variable) =>
|
||||
variable.id === block.options.dynamicItems?.pictureSrcsVariableId
|
||||
)?.name
|
||||
|
||||
return (
|
||||
<Stack w="full">
|
||||
{block.options.dynamicItems?.isEnabled && dynamicVariableName ? (
|
||||
<Wrap spacing={1}>
|
||||
<Text>Display</Text>
|
||||
<Tag bg="orange.400" color="white">
|
||||
{dynamicVariableName}
|
||||
</Tag>
|
||||
<Text>pictures</Text>
|
||||
</Wrap>
|
||||
) : (
|
||||
<ItemNodesList block={block} indices={indices} />
|
||||
)}
|
||||
{block.options.variableId ? (
|
||||
<SetVariableLabel
|
||||
variableId={block.options.variableId}
|
||||
variables={typebot?.variables}
|
||||
/>
|
||||
) : null}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import { TextInput } from '@/components/inputs'
|
||||
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
|
||||
import { FormLabel, Stack } from '@chakra-ui/react'
|
||||
import { Variable } from '@typebot.io/schemas'
|
||||
import React from 'react'
|
||||
import {
|
||||
PictureChoiceBlock,
|
||||
defaultPictureChoiceOptions,
|
||||
} from '@typebot.io/schemas/features/blocks/inputs/pictureChoice'
|
||||
import { SwitchWithRelatedSettings } from '@/components/SwitchWithRelatedSettings'
|
||||
|
||||
type Props = {
|
||||
options?: PictureChoiceBlock['options']
|
||||
onOptionsChange: (options: PictureChoiceBlock['options']) => void
|
||||
}
|
||||
|
||||
export const PictureChoiceSettings = ({ options, onOptionsChange }: Props) => {
|
||||
const updateIsMultiple = (isMultipleChoice: boolean) =>
|
||||
options && onOptionsChange({ ...options, isMultipleChoice })
|
||||
const updateButtonLabel = (buttonLabel: string) =>
|
||||
options && onOptionsChange({ ...options, buttonLabel })
|
||||
const updateSaveVariable = (variable?: Variable) =>
|
||||
options && onOptionsChange({ ...options, variableId: variable?.id })
|
||||
const updateSearchInputPlaceholder = (searchInputPlaceholder: string) =>
|
||||
options && onOptionsChange({ ...options, searchInputPlaceholder })
|
||||
const updateIsSearchable = (isSearchable: boolean) =>
|
||||
options && onOptionsChange({ ...options, isSearchable })
|
||||
|
||||
const updateIsDynamicItemsEnabled = (isEnabled: boolean) =>
|
||||
options &&
|
||||
onOptionsChange({
|
||||
...options,
|
||||
dynamicItems: {
|
||||
...options.dynamicItems,
|
||||
isEnabled,
|
||||
},
|
||||
})
|
||||
|
||||
const updateDynamicItemsPictureSrcsVariable = (variable?: Variable) =>
|
||||
options &&
|
||||
onOptionsChange({
|
||||
...options,
|
||||
dynamicItems: {
|
||||
...options.dynamicItems,
|
||||
pictureSrcsVariableId: variable?.id,
|
||||
},
|
||||
})
|
||||
|
||||
const updateDynamicItemsTitlesVariable = (variable?: Variable) =>
|
||||
options &&
|
||||
onOptionsChange({
|
||||
...options,
|
||||
dynamicItems: {
|
||||
...options.dynamicItems,
|
||||
titlesVariableId: variable?.id,
|
||||
},
|
||||
})
|
||||
|
||||
const updateDynamicItemsDescriptionsVariable = (variable?: Variable) =>
|
||||
options &&
|
||||
onOptionsChange({
|
||||
...options,
|
||||
dynamicItems: {
|
||||
...options.dynamicItems,
|
||||
descriptionsVariableId: variable?.id,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<Stack spacing={4}>
|
||||
<SwitchWithRelatedSettings
|
||||
label="Is searchable?"
|
||||
initialValue={options?.isSearchable ?? false}
|
||||
onCheckChange={updateIsSearchable}
|
||||
>
|
||||
<TextInput
|
||||
label="Input placeholder:"
|
||||
defaultValue={
|
||||
options?.searchInputPlaceholder ??
|
||||
defaultPictureChoiceOptions.searchInputPlaceholder
|
||||
}
|
||||
onChange={updateSearchInputPlaceholder}
|
||||
/>
|
||||
</SwitchWithRelatedSettings>
|
||||
<SwitchWithRelatedSettings
|
||||
label="Multiple choice?"
|
||||
initialValue={options?.isMultipleChoice ?? false}
|
||||
onCheckChange={updateIsMultiple}
|
||||
>
|
||||
<TextInput
|
||||
label="Submit button label:"
|
||||
defaultValue={options?.buttonLabel ?? 'Send'}
|
||||
onChange={updateButtonLabel}
|
||||
/>
|
||||
</SwitchWithRelatedSettings>
|
||||
|
||||
<SwitchWithRelatedSettings
|
||||
label="Dynamic items?"
|
||||
initialValue={options?.dynamicItems?.isEnabled ?? false}
|
||||
onCheckChange={updateIsDynamicItemsEnabled}
|
||||
>
|
||||
<Stack>
|
||||
<FormLabel mb="0" htmlFor="variable">
|
||||
Images:
|
||||
</FormLabel>
|
||||
<VariableSearchInput
|
||||
initialVariableId={options?.dynamicItems?.pictureSrcsVariableId}
|
||||
onSelectVariable={updateDynamicItemsPictureSrcsVariable}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<FormLabel mb="0" htmlFor="variable">
|
||||
Titles:
|
||||
</FormLabel>
|
||||
<VariableSearchInput
|
||||
initialVariableId={options?.dynamicItems?.titlesVariableId}
|
||||
onSelectVariable={updateDynamicItemsTitlesVariable}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<FormLabel mb="0" htmlFor="variable">
|
||||
Descriptions:
|
||||
</FormLabel>
|
||||
<VariableSearchInput
|
||||
initialVariableId={options?.dynamicItems?.descriptionsVariableId}
|
||||
onSelectVariable={updateDynamicItemsDescriptionsVariable}
|
||||
/>
|
||||
</Stack>
|
||||
</SwitchWithRelatedSettings>
|
||||
|
||||
<Stack>
|
||||
<FormLabel mb="0" htmlFor="variable">
|
||||
Save answer in a variable:
|
||||
</FormLabel>
|
||||
<VariableSearchInput
|
||||
initialVariableId={options?.variableId}
|
||||
onSelectVariable={updateSaveVariable}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
import test, { expect } from '@playwright/test'
|
||||
import { createTypebots } from '@typebot.io/lib/playwright/databaseActions'
|
||||
import { parseDefaultGroupWithBlock } from '@typebot.io/lib/playwright/databaseHelpers'
|
||||
import { InputBlockType, ItemType } from '@typebot.io/schemas'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
import { defaultPictureChoiceOptions } from '@typebot.io/schemas/features/blocks/inputs/pictureChoice'
|
||||
|
||||
const firstImageSrc =
|
||||
'https://images.unsplash.com/flagged/photo-1575517111839-3a3843ee7f5d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2940&q=80'
|
||||
|
||||
const secondImageSrc =
|
||||
'https://images.unsplash.com/photo-1582582621959-48d27397dc69?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2938&q=80'
|
||||
|
||||
const thirdImageSrc =
|
||||
'https://images.unsplash.com/photo-1564019472231-4586c552dc27?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80'
|
||||
|
||||
test.describe.parallel('Picture choice input block', () => {
|
||||
test('can edit items', async ({ page }) => {
|
||||
const typebotId = createId()
|
||||
await createTypebots([
|
||||
{
|
||||
id: typebotId,
|
||||
...parseDefaultGroupWithBlock({
|
||||
type: InputBlockType.PICTURE_CHOICE,
|
||||
items: [
|
||||
{
|
||||
id: 'choice1',
|
||||
blockId: 'block1',
|
||||
type: ItemType.PICTURE_CHOICE,
|
||||
},
|
||||
],
|
||||
options: { ...defaultPictureChoiceOptions },
|
||||
}),
|
||||
},
|
||||
])
|
||||
|
||||
await page.goto(`/typebots/${typebotId}/edit`)
|
||||
await page.getByTestId('item-node').click()
|
||||
await page.getByRole('button', { name: 'Pick an image' }).click()
|
||||
await page.getByPlaceholder('Paste the image link...').fill(firstImageSrc)
|
||||
await page.getByLabel('Title:').fill('First image')
|
||||
await page.getByLabel('Description:').fill('First description')
|
||||
await page.getByText('Default').click()
|
||||
await page.getByRole('img', { name: 'Picture choice image' }).hover()
|
||||
await page.getByRole('button', { name: 'Add item' }).click()
|
||||
await page.getByTestId('item-node').last().click()
|
||||
await page.getByRole('button', { name: 'Pick an image' }).click()
|
||||
await page.getByPlaceholder('Paste the image link...').fill(secondImageSrc)
|
||||
await page.getByLabel('Title:').fill('Second image')
|
||||
await page.getByLabel('Description:').fill('Second description')
|
||||
await page.getByRole('img', { name: 'Picture choice image' }).last().hover()
|
||||
await page.getByRole('button', { name: 'Add item' }).click()
|
||||
await page.getByTestId('item-node').last().click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Pick an image' })
|
||||
).toHaveCount(1)
|
||||
await page.getByRole('button', { name: 'Pick an image' }).click()
|
||||
await page.getByPlaceholder('Paste the image link...').fill(thirdImageSrc)
|
||||
await page.getByLabel('Title:').fill('Third image')
|
||||
await page.getByLabel('Description:').fill('Third description')
|
||||
await page.getByRole('button', { name: 'Preview' }).click()
|
||||
await expect(
|
||||
page.getByRole('button', {
|
||||
name: 'First image First image First description',
|
||||
})
|
||||
).toBeVisible()
|
||||
await expect(
|
||||
page.getByRole('button', {
|
||||
name: 'Second image Second image Second description',
|
||||
})
|
||||
).toBeVisible()
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Third image Third image Third description',
|
||||
})
|
||||
.click()
|
||||
await expect(page.getByTestId('guest-bubble')).toBeVisible()
|
||||
await expect(
|
||||
page.locator('typebot-standard').getByText('Third image')
|
||||
).toBeVisible()
|
||||
|
||||
await page.getByTestId('block2-icon').click()
|
||||
await page.getByText('Multiple choice?').click()
|
||||
await page.getByLabel('Submit button label:').fill('Go')
|
||||
await page.getByRole('button', { name: 'Restart' }).click()
|
||||
await page
|
||||
.getByRole('checkbox', {
|
||||
name: 'First image First image First description',
|
||||
})
|
||||
.click()
|
||||
await page
|
||||
.getByRole('checkbox', {
|
||||
name: 'Second image Second image Second description',
|
||||
})
|
||||
.click()
|
||||
await page.getByRole('button', { name: 'Go' }).click()
|
||||
await expect(
|
||||
page.locator('typebot-standard').getByText('First image, Second image')
|
||||
).toBeVisible()
|
||||
|
||||
await page.getByTestId('block2-icon').click()
|
||||
await page.getByText('Is searchable?').click()
|
||||
await page.getByLabel('Input placeholder:').fill('Search...')
|
||||
await page.getByRole('button', { name: 'Restart' }).click()
|
||||
await page.getByPlaceholder('Search...').fill('second')
|
||||
await expect(
|
||||
page.getByRole('checkbox', {
|
||||
name: 'First image First image First description',
|
||||
})
|
||||
).toBeHidden()
|
||||
await page
|
||||
.getByRole('checkbox', {
|
||||
name: 'Second image Second image Second description',
|
||||
})
|
||||
.click()
|
||||
await page.getByRole('button', { name: 'Go' }).click()
|
||||
await expect(
|
||||
page.locator('typebot-standard').getByText('Second image')
|
||||
).toBeVisible()
|
||||
})
|
||||
})
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Wrap,
|
||||
Fade,
|
||||
IconButton,
|
||||
PopoverTrigger,
|
||||
Popover,
|
||||
Portal,
|
||||
PopoverContent,
|
||||
@@ -14,6 +13,7 @@ import {
|
||||
PopoverBody,
|
||||
useEventListener,
|
||||
useColorModeValue,
|
||||
PopoverAnchor,
|
||||
} from '@chakra-ui/react'
|
||||
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
|
||||
import {
|
||||
@@ -79,7 +79,7 @@ export const ConditionItemNode = ({ item, isMouseOver, indices }: Props) => {
|
||||
isOpen={openedItemId === item.id}
|
||||
closeOnBlur={false}
|
||||
>
|
||||
<PopoverTrigger>
|
||||
<PopoverAnchor>
|
||||
<Flex p={3} pos="relative" w="full" onClick={openPopover}>
|
||||
{item.content.comparisons.length === 0 ||
|
||||
comparisonIsEmpty(item.content.comparisons[0]) ? (
|
||||
@@ -101,7 +101,7 @@ export const ConditionItemNode = ({ item, isMouseOver, indices }: Props) => {
|
||||
</Tag>
|
||||
)}
|
||||
{comparison.comparisonOperator && (
|
||||
<Text>
|
||||
<Text fontSize="sm">
|
||||
{parseComparisonOperatorSymbol(
|
||||
comparison.comparisonOperator
|
||||
)}
|
||||
@@ -137,7 +137,7 @@ export const ConditionItemNode = ({ item, isMouseOver, indices }: Props) => {
|
||||
/>
|
||||
</Fade>
|
||||
</Flex>
|
||||
</PopoverTrigger>
|
||||
</PopoverAnchor>
|
||||
<Portal>
|
||||
<PopoverContent pos="relative" onMouseDown={handleMouseDown}>
|
||||
<PopoverArrow />
|
||||
@@ -164,7 +164,9 @@ const comparisonIsEmpty = (comparison: Comparison) =>
|
||||
isNotDefined(comparison.value) &&
|
||||
isNotDefined(comparison.variableId)
|
||||
|
||||
const parseComparisonOperatorSymbol = (operator: ComparisonOperators) => {
|
||||
const parseComparisonOperatorSymbol = (
|
||||
operator: ComparisonOperators
|
||||
): string => {
|
||||
switch (operator) {
|
||||
case ComparisonOperators.CONTAINS:
|
||||
return 'contains'
|
||||
@@ -178,5 +180,13 @@ const parseComparisonOperatorSymbol = (operator: ComparisonOperators) => {
|
||||
return '<'
|
||||
case ComparisonOperators.NOT_EQUAL:
|
||||
return '!='
|
||||
case ComparisonOperators.ENDS_WITH:
|
||||
return 'ends with'
|
||||
case ComparisonOperators.STARTS_WITH:
|
||||
return 'starts with'
|
||||
case ComparisonOperators.IS_EMPTY:
|
||||
return 'is empty'
|
||||
case ComparisonOperators.NOT_CONTAINS:
|
||||
return 'not contains'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user