Add AB test block

Closes #449
This commit is contained in:
Baptiste Arnaud
2023-04-17 16:47:17 +02:00
parent b416c6e373
commit 7e937e1c7c
28 changed files with 443 additions and 21 deletions

View File

@@ -8,7 +8,7 @@ import {
} from '@chakra-ui/react'
import { PlusIcon } from '@/components/icons'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { ButtonItem, ItemIndices, ItemType } from '@typebot.io/schemas'
import { ButtonItem, Item, ItemIndices, ItemType } from '@typebot.io/schemas'
import React, { useRef, useState } from 'react'
import { isNotDefined } from '@typebot.io/lib'
@@ -26,7 +26,9 @@ export const ButtonsItemNode = ({ item, indices, isMouseOver }: Props) => {
const handleInputSubmit = () => {
if (itemValue === '') deleteItem(indices)
else
updateItem(indices, { content: itemValue === '' ? undefined : itemValue })
updateItem(indices, {
content: itemValue === '' ? undefined : itemValue,
} as Item)
}
const handleKeyPress = (e: React.KeyboardEvent<HTMLDivElement>) => {

View File

@@ -0,0 +1,24 @@
import test, { expect } from '@playwright/test'
import { importTypebotInDatabase } from '@typebot.io/lib/playwright/databaseActions'
import { createId } from '@paralleldrive/cuid2'
import { getTestAsset } from '@/test/utils/playwright'
const typebotId = createId()
test.describe('AB Test block', () => {
test('its configuration should work', async ({ page }) => {
await importTypebotInDatabase(getTestAsset('typebots/logic/abTest.json'), {
id: typebotId,
})
await page.goto(`/typebots/${typebotId}/edit`)
await page.getByText('A 50%').click()
await page.getByLabel('Percent of users to follow A:').fill('100')
await expect(page.getByText('A 100%')).toBeVisible()
await expect(page.getByText('B 0%')).toBeVisible()
await page.getByRole('button', { name: 'Preview' }).click()
await expect(
page.locator('typebot-standard').getByText('How are you?')
).toBeVisible()
})
})

View File

@@ -0,0 +1,7 @@
import { ShuffleIcon } from '@/components/icons'
import { IconProps } from '@chakra-ui/react'
import React from 'react'
export const AbTestIcon = (props: IconProps) => (
<ShuffleIcon color="purple.500" {...props} />
)

View File

@@ -0,0 +1,66 @@
import React from 'react'
import { Flex, Stack, useColorModeValue, Text, Tag } from '@chakra-ui/react'
import { AbTestBlock } from '@typebot.io/schemas'
import { SourceEndpoint } from '@/features/graph/components/endpoints/SourceEndpoint'
type Props = {
block: AbTestBlock
}
export const AbTestNodeBody = ({ block }: Props) => {
const borderColor = useColorModeValue('gray.200', 'gray.700')
const bg = useColorModeValue('white', undefined)
return (
<Stack spacing={2} w="full">
<Flex
pos="relative"
align="center"
shadow="sm"
rounded="md"
bg={bg}
borderWidth={'1px'}
borderColor={borderColor}
w="full"
>
<Text p="3">
A <Tag>{block.options.aPercent}%</Tag>
</Text>
<SourceEndpoint
source={{
groupId: block.groupId,
blockId: block.id,
itemId: block.items[0].id,
}}
pos="absolute"
right="-49px"
pointerEvents="all"
/>
</Flex>
<Flex
pos="relative"
align="center"
shadow="sm"
rounded="md"
bg={bg}
borderWidth={'1px'}
borderColor={borderColor}
w="full"
>
<Text p="3">
B <Tag>{100 - block.options.aPercent}%</Tag>
</Text>
<SourceEndpoint
source={{
groupId: block.groupId,
blockId: block.id,
itemId: block.items[1].id,
}}
pos="absolute"
right="-49px"
pointerEvents="all"
/>
</Flex>
</Stack>
)
}

View File

@@ -0,0 +1,29 @@
import { Stack } from '@chakra-ui/react'
import React from 'react'
import { isDefined } from '@typebot.io/lib'
import { AbTestBlock } from '@typebot.io/schemas'
import { NumberInput } from '@/components/inputs'
type Props = {
options: AbTestBlock['options']
onOptionsChange: (options: AbTestBlock['options']) => void
}
export const AbTestSettings = ({ options, onOptionsChange }: Props) => {
const updateAPercent = (aPercent?: number) =>
isDefined(aPercent) ? onOptionsChange({ ...options, aPercent }) : null
return (
<Stack spacing={4}>
<NumberInput
defaultValue={options.aPercent}
onValueChange={updateAPercent}
withVariableButton={false}
label="Percent of users to follow A:"
direction="column"
max={100}
min={0}
/>
</Stack>
)
}