2
0

♻️ (builder) Change to features-centric folder structure

This commit is contained in:
Baptiste Arnaud
2022-11-15 09:35:48 +01:00
committed by Baptiste Arnaud
parent 3686465a85
commit 643571fe7d
683 changed files with 3907 additions and 3643 deletions

View File

@ -0,0 +1,99 @@
import test, { expect } from '@playwright/test'
import {
createTypebots,
importTypebotInDatabase,
} from 'utils/playwright/databaseActions'
import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import { defaultChoiceInputOptions, InputBlockType, ItemType } from 'models'
import cuid from 'cuid'
import { typebotViewer } from 'utils/playwright/testHelpers'
import { getTestAsset } from '@/test/utils/playwright'
test.describe.parallel('Buttons input block', () => {
test('can edit button items', async ({ page }) => {
const typebotId = cuid()
await createTypebots([
{
id: typebotId,
...parseDefaultGroupWithBlock({
type: InputBlockType.CHOICE,
items: [
{
id: 'choice1',
blockId: 'block1',
type: ItemType.BUTTON,
},
],
options: { ...defaultChoiceInputOptions },
}),
},
])
await page.goto(`/typebots/${typebotId}/edit`)
await page.fill('input[value="Click to edit"]', 'Item 1')
await page.press('input[value="Item 1"]', 'Enter')
await page.fill('input[value="Click to edit"]', 'Item 2')
await page.press('input[value="Item 2"]', 'Enter')
await page.fill('input[value="Click to edit"]', 'Item 3')
await page.press('input[value="Item 3"]', 'Enter')
await page.press('input[value="Click to edit"]', 'Escape')
await page.click('text=Item 2', { button: 'right' })
await page.click('text=Delete')
await expect(page.locator('text=Item 2')).toBeHidden()
await page.click('text=Preview')
const item3Button = typebotViewer(page).locator('button >> text=Item 3')
await item3Button.click()
await expect(item3Button).toBeHidden()
await expect(typebotViewer(page).locator('text=Item 3')).toBeVisible()
await page.click('button[aria-label="Close"]')
await page.click('[data-testid="block2-icon"]')
await page.click('text=Multiple choice?')
await page.fill('#button', 'Go')
await page.click('[data-testid="block2-icon"]')
await page.locator('text=Item 1').hover()
await page.waitForTimeout(1000)
await page.click('[aria-label="Add item"]')
await page.fill('input[value="Click to edit"]', 'Item 2')
await page.press('input[value="Item 2"]', 'Enter')
await page.click('text=Preview')
await typebotViewer(page).locator('button >> text="Item 3"').click()
await typebotViewer(page).locator('button >> text="Item 1"').click()
await typebotViewer(page).locator('text=Go').click()
await expect(
typebotViewer(page).locator('text="Item 3, Item 1"')
).toBeVisible()
})
})
test('Variable buttons should work', async ({ page }) => {
const typebotId = cuid()
await importTypebotInDatabase(
getTestAsset('typebots/inputs/variableButton.json'),
{
id: typebotId,
}
)
await page.goto(`/typebots/${typebotId}/edit`)
await page.click('text=Preview')
await typebotViewer(page).locator('text=Variable item').click()
await expect(typebotViewer(page).locator('text=Variable item')).toBeVisible()
await expect(typebotViewer(page).locator('text=Ok great!')).toBeVisible()
await page.click('text="Item 1"')
await page.fill('input[value="Item 1"]', '{{Item 2}}')
await page.click('[data-testid="block1-icon"]')
await page.click('text=Multiple choice?')
await page.click('text="Restart"')
await typebotViewer(page).locator('text="Variable item" >> nth=0').click()
await typebotViewer(page).locator('text="Variable item" >> nth=1').click()
await typebotViewer(page).locator('text="Send"').click()
await expect(
typebotViewer(page).locator('text="Variable item, Variable item"')
).toBeVisible()
})

View File

@ -0,0 +1,93 @@
import {
EditablePreview,
EditableInput,
Editable,
Fade,
IconButton,
Flex,
} from '@chakra-ui/react'
import { PlusIcon } from '@/components/icons'
import { useTypebot } from '@/features/editor'
import { ButtonItem, ItemIndices, ItemType } from 'models'
import React, { useEffect, useRef, useState } from 'react'
import { isNotDefined } from 'utils'
type Props = {
item: ButtonItem
indices: ItemIndices
isMouseOver: boolean
}
export const ButtonNodeContent = ({ item, indices, isMouseOver }: Props) => {
const { deleteItem, updateItem, createItem } = useTypebot()
const [initialContent] = useState(item.content ?? '')
const [itemValue, setItemValue] = useState(item.content ?? 'Click to edit')
const editableRef = useRef<HTMLDivElement | null>(null)
useEffect(() => {
if (itemValue !== item.content)
setItemValue(item.content ?? 'Click to edit')
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item])
const handleInputSubmit = () => {
if (itemValue === '') deleteItem(indices)
else
updateItem(indices, { content: itemValue === '' ? undefined : itemValue })
}
const handleKeyPress = (e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === 'Escape' && itemValue === 'Click to edit') deleteItem(indices)
if (e.key === 'Enter' && itemValue !== '' && initialContent === '')
handlePlusClick()
}
const handlePlusClick = () => {
const itemIndex = indices.itemIndex + 1
createItem(
{ blockId: item.blockId, type: ItemType.BUTTON },
{ ...indices, itemIndex }
)
}
return (
<Flex px={4} py={2} justify="center" w="90%" pos="relative">
<Editable
ref={editableRef}
flex="1"
startWithEditView={isNotDefined(item.content)}
value={itemValue}
onChange={setItemValue}
onSubmit={handleInputSubmit}
onKeyDownCapture={handleKeyPress}
maxW="180px"
>
<EditablePreview
w="full"
color={item.content !== 'Click to edit' ? 'inherit' : 'gray.500'}
cursor="pointer"
/>
<EditableInput />
</Editable>
<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"
onClick={handlePlusClick}
/>
</Fade>
</Flex>
)
}

View File

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

View File

@ -0,0 +1,54 @@
import { Input } from '@/components/inputs'
import { SwitchWithLabel } from '@/components/SwitchWithLabel'
import { VariableSearchInput } from '@/components/VariableSearchInput'
import { FormLabel, Stack } from '@chakra-ui/react'
import { ChoiceInputOptions, Variable } from 'models'
import React from 'react'
type ButtonsOptionsFormProps = {
options?: ChoiceInputOptions
onOptionsChange: (options: ChoiceInputOptions) => void
}
export const ButtonsOptionsForm = ({
options,
onOptionsChange,
}: ButtonsOptionsFormProps) => {
const handleIsMultipleChange = (isMultipleChoice: boolean) =>
options && onOptionsChange({ ...options, isMultipleChoice })
const handleButtonLabelChange = (buttonLabel: string) =>
options && onOptionsChange({ ...options, buttonLabel })
const handleVariableChange = (variable?: Variable) =>
options && onOptionsChange({ ...options, variableId: variable?.id })
return (
<Stack spacing={4}>
<SwitchWithLabel
label="Multiple choice?"
initialValue={options?.isMultipleChoice ?? false}
onCheckChange={handleIsMultipleChange}
/>
{options?.isMultipleChoice && (
<Stack>
<FormLabel mb="0" htmlFor="button">
Button label:
</FormLabel>
<Input
id="button"
defaultValue={options?.buttonLabel ?? 'Send'}
onChange={handleButtonLabelChange}
/>
</Stack>
)}
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save answer in a variable:
</FormLabel>
<VariableSearchInput
initialVariableId={options?.variableId}
onSelectVariable={handleVariableChange}
/>
</Stack>
</Stack>
)
}

View File

@ -0,0 +1,3 @@
export { ButtonsOptionsForm } from './components/ButtonsOptionsForm'
export { ButtonNodeContent } from './components/ButtonNodeContent'
export { ButtonsInputIcon } from './components/ButtonsInputIcon'