2
0

🚸 (buttons) Show collected variable in buttons block preview

This commit is contained in:
Baptiste Arnaud
2022-12-23 11:30:39 +01:00
parent 4109e63b7b
commit f224ab9b16
5 changed files with 85 additions and 18 deletions

View File

@@ -51,6 +51,9 @@ test.describe.parallel('Buttons input block', () => {
await page.click('[data-testid="block2-icon"]') await page.click('[data-testid="block2-icon"]')
await page.click('text=Multiple choice?') await page.click('text=Multiple choice?')
await page.fill('#button', 'Go') await page.fill('#button', 'Go')
await page.getByPlaceholder('Select a variable').click()
await page.getByText('var1').click()
await expect(page.getByText('Collectsvar1')).toBeVisible()
await page.click('[data-testid="block2-icon"]') await page.click('[data-testid="block2-icon"]')
await page.locator('text=Item 1').hover() await page.locator('text=Item 1').hover()

View File

@@ -4,39 +4,72 @@ import {
BlockWithItems, BlockWithItems,
defaultConditionContent, defaultConditionContent,
ItemType, ItemType,
Block,
LogicBlockType,
InputBlockType,
} from 'models' } from 'models'
import { SetTypebot } from '../TypebotProvider' import { SetTypebot } from '../TypebotProvider'
import produce from 'immer' import produce from 'immer'
import { cleanUpEdgeDraft } from './edges' import { cleanUpEdgeDraft } from './edges'
import { byId, blockHasItems } from 'utils' import { byId, blockHasItems } from 'utils'
import cuid from 'cuid' import cuid from 'cuid'
import { WritableDraft } from 'immer/dist/types/types-external'
type NewItem = Pick<Item, 'blockId' | 'outgoingEdgeId' | 'type'> & Partial<Item>
export type ItemsActions = { export type ItemsActions = {
createItem: (item: Item | Omit<Item, 'id'>, indices: ItemIndices) => void createItem: (item: NewItem, indices: ItemIndices) => void
updateItem: (indices: ItemIndices, updates: Partial<Omit<Item, 'id'>>) => void updateItem: (indices: ItemIndices, updates: Partial<Omit<Item, 'id'>>) => void
detachItemFromBlock: (indices: ItemIndices) => void detachItemFromBlock: (indices: ItemIndices) => void
deleteItem: (indices: ItemIndices) => void deleteItem: (indices: ItemIndices) => void
} }
const createItem = (
block: WritableDraft<Block>,
item: NewItem,
itemIndex: number
) => {
switch (block.type) {
case LogicBlockType.CONDITION: {
if (item.type === ItemType.CONDITION) {
const newItem = {
...item,
id: 'id' in item && item.id ? item.id : cuid(),
content: item.content ?? defaultConditionContent,
}
block.items.splice(itemIndex, 0, newItem)
return newItem
}
break
}
case InputBlockType.CHOICE: {
if (item.type === ItemType.BUTTON) {
const newItem = {
...item,
id: 'id' in item && item.id ? item.id : cuid(),
content: item.content,
}
block.items.splice(itemIndex, 0, newItem)
return newItem
}
break
}
}
}
const itemsAction = (setTypebot: SetTypebot): ItemsActions => ({ const itemsAction = (setTypebot: SetTypebot): ItemsActions => ({
createItem: ( createItem: (
item: Item | Omit<Item, 'id'>, item: NewItem,
{ groupIndex, blockIndex, itemIndex }: ItemIndices { groupIndex, blockIndex, itemIndex }: ItemIndices
) => ) =>
setTypebot((typebot) => setTypebot((typebot) =>
produce(typebot, (typebot) => { produce(typebot, (typebot) => {
const block = typebot.groups[groupIndex].blocks[ const block = typebot.groups[groupIndex].blocks[blockIndex]
blockIndex
] as BlockWithItems const newItem = createItem(block, item, itemIndex)
if (!newItem) return
const newItem = {
id: 'id' in item ? item.id : cuid(),
content:
item.type === ItemType.CONDITION
? defaultConditionContent
: undefined,
...item,
} as Item
if (item.outgoingEdgeId) { if (item.outgoingEdgeId) {
const edgeIndex = typebot.edges.findIndex(byId(item.outgoingEdgeId)) const edgeIndex = typebot.edges.findIndex(byId(item.outgoingEdgeId))
edgeIndex !== -1 edgeIndex !== -1
@@ -47,7 +80,6 @@ const itemsAction = (setTypebot: SetTypebot): ItemsActions => ({
}) })
: (newItem.outgoingEdgeId = undefined) : (newItem.outgoingEdgeId = undefined)
} }
block.items.splice(itemIndex, 0, newItem)
}) })
), ),
updateItem: ( updateItem: (

View File

@@ -1,7 +1,9 @@
import { import {
Flex, Flex,
HStack,
Portal, Portal,
Stack, Stack,
Tag,
Text, Text,
useColorModeValue, useColorModeValue,
useEventListener, useEventListener,
@@ -13,7 +15,13 @@ import {
useGraph, useGraph,
} from '../../../providers' } from '../../../providers'
import { useTypebot } from '@/features/editor' import { useTypebot } from '@/features/editor'
import { BlockIndices, BlockWithItems, LogicBlockType, Item } from 'models' import {
BlockIndices,
BlockWithItems,
LogicBlockType,
Item,
Variable,
} from 'models'
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import { ItemNode } from './ItemNode' import { ItemNode } from './ItemNode'
import { SourceEndpoint } from '../../Endpoints' import { SourceEndpoint } from '../../Endpoints'
@@ -127,8 +135,16 @@ export const ItemNodesList = ({
elem && (placeholderRefs.current[idx] = elem) elem && (placeholderRefs.current[idx] = elem)
} }
const collectedVariableId = 'options' in block && block.options.variableId
return ( return (
<Stack flex={1} spacing={1} maxW="full" onClick={stopPropagating}> <Stack flex={1} spacing={1} maxW="full" onClick={stopPropagating}>
{collectedVariableId && (
<CollectVariableLabel
variableId={collectedVariableId}
variables={typebot?.variables ?? []}
/>
)}
<PlaceholderNode <PlaceholderNode
isVisible={showPlaceholders} isVisible={showPlaceholders}
isExpanded={expandedPlaceholderIndex === 0} isExpanded={expandedPlaceholderIndex === 0}
@@ -202,3 +218,20 @@ const DefaultItemNode = ({ block }: { block: BlockWithItems }) => {
</Flex> </Flex>
) )
} }
const CollectVariableLabel = ({
variableId,
variables,
}: {
variableId: string
variables: Variable[]
}) => (
<HStack fontStyle="italic" spacing={1}>
<Text fontSize="sm" color={useColorModeValue('gray.600', 'gray.400')}>
Collects
</Text>
<Tag bg="orange.400" color="white" size="sm">
{variables.find((variable) => variable.id === variableId)?.name}
</Tag>
</HStack>
)

View File

@@ -5,7 +5,7 @@ import {
LogicBlockOptions, LogicBlockOptions,
} from '.' } from '.'
import { BubbleBlock, bubbleBlockSchema } from './bubbles' import { BubbleBlock, bubbleBlockSchema } from './bubbles'
import { InputBlock, inputBlockSchema } from './inputs' import { ChoiceInputBlock, InputBlock, inputBlockSchema } from './inputs'
import { IntegrationBlock, integrationBlockSchema } from './integrations' import { IntegrationBlock, integrationBlockSchema } from './integrations'
import { ConditionBlock, LogicBlock, logicBlockSchema } from './logic' import { ConditionBlock, LogicBlock, logicBlockSchema } from './logic'
import { z } from 'zod' import { z } from 'zod'
@@ -51,7 +51,7 @@ export type BlockOptions =
| LogicBlockOptions | LogicBlockOptions
| IntegrationBlockOptions | IntegrationBlockOptions
export type BlockWithItems = Omit<Block, 'items'> & { items: Item[] } export type BlockWithItems = ConditionBlock | ChoiceInputBlock
export type BlockBase = z.infer<typeof blockBaseSchema> export type BlockBase = z.infer<typeof blockBaseSchema>

View File

@@ -43,7 +43,6 @@ export const conditionBlockSchema = blockBaseSchema.and(
z.object({ z.object({
type: z.enum([LogicBlockType.CONDITION]), type: z.enum([LogicBlockType.CONDITION]),
items: z.array(conditionItemSchema), items: z.array(conditionItemSchema),
options: z.object({}),
}) })
) )