feat(engine): 🚸 Improve input variable behaviour (for loops)
This commit is contained in:
@ -379,3 +379,11 @@ export const BoxIcon = (props: IconProps) => (
|
||||
<line x1="12" y1="22.08" x2="12" y2="12"></line>
|
||||
</Icon>
|
||||
)
|
||||
|
||||
export const HelpCircleIcon = (props: IconProps) => (
|
||||
<Icon viewBox="0 0 24 24" {...featherIconsBaseProps} {...props}>
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"></line>
|
||||
</Icon>
|
||||
)
|
||||
|
@ -41,6 +41,12 @@ export const GeneralSettingsForm = ({
|
||||
isNewResultOnRefreshEnabled,
|
||||
})
|
||||
|
||||
const handleInputPrefillChange = (isInputPrefillEnabled: boolean) =>
|
||||
onGeneralSettingsChange({
|
||||
...generalSettings,
|
||||
isInputPrefillEnabled,
|
||||
})
|
||||
|
||||
return (
|
||||
<Stack spacing={6}>
|
||||
<UpgradeModal isOpen={isOpen} onClose={onClose} />
|
||||
@ -59,6 +65,13 @@ export const GeneralSettingsForm = ({
|
||||
onChange={handleSwitchChange}
|
||||
/>
|
||||
</Flex>
|
||||
<SwitchWithLabel
|
||||
id="prefill"
|
||||
label="Prefill input"
|
||||
initialValue={generalSettings.isInputPrefillEnabled ?? true}
|
||||
onCheckChange={handleInputPrefillChange}
|
||||
moreInfoContent="Inputs are automatically pre-filled whenever its saving variable has a value"
|
||||
/>
|
||||
<SwitchWithLabel
|
||||
id="new-result"
|
||||
label="Create new session on page refresh"
|
||||
|
@ -1,10 +1,19 @@
|
||||
import { FormLabel, HStack, Switch, SwitchProps } from '@chakra-ui/react'
|
||||
import {
|
||||
chakra,
|
||||
FormLabel,
|
||||
HStack,
|
||||
Switch,
|
||||
SwitchProps,
|
||||
Tooltip,
|
||||
} from '@chakra-ui/react'
|
||||
import { HelpCircleIcon } from 'assets/icons'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
type SwitchWithLabelProps = {
|
||||
id: string
|
||||
label: string
|
||||
initialValue: boolean
|
||||
moreInfoContent?: string
|
||||
onCheckChange: (isChecked: boolean) => void
|
||||
} & SwitchProps
|
||||
|
||||
@ -12,6 +21,7 @@ export const SwitchWithLabel = ({
|
||||
id,
|
||||
label,
|
||||
initialValue,
|
||||
moreInfoContent,
|
||||
onCheckChange,
|
||||
...props
|
||||
}: SwitchWithLabelProps) => {
|
||||
@ -23,9 +33,18 @@ export const SwitchWithLabel = ({
|
||||
}
|
||||
return (
|
||||
<HStack justifyContent="space-between">
|
||||
<FormLabel htmlFor={id} mb="0">
|
||||
{label}
|
||||
</FormLabel>
|
||||
<HStack>
|
||||
<FormLabel htmlFor={id} mb="0" mr="0">
|
||||
{label}
|
||||
</FormLabel>
|
||||
{moreInfoContent && (
|
||||
<Tooltip label={moreInfoContent}>
|
||||
<chakra.span cursor="pointer">
|
||||
<HelpCircleIcon />
|
||||
</chakra.span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</HStack>
|
||||
<Switch
|
||||
isChecked={isChecked}
|
||||
onChange={handleChange}
|
||||
|
119
apps/builder/playwright/fixtures/typebots/settings.json
Normal file
119
apps/builder/playwright/fixtures/typebots/settings.json
Normal file
@ -0,0 +1,119 @@
|
||||
{
|
||||
"id": "cl0kvckdk2754en1avz0i71k0",
|
||||
"createdAt": "2022-03-10T10:50:23.912Z",
|
||||
"updatedAt": "2022-03-10T10:50:23.912Z",
|
||||
"name": "My typebot",
|
||||
"ownerId": "cl0cfi60r0000381a2bft9yis",
|
||||
"publishedTypebotId": null,
|
||||
"folderId": null,
|
||||
"blocks": [
|
||||
{
|
||||
"id": "cAvp3oQUNYcANvcEQEVSpD",
|
||||
"steps": [
|
||||
{
|
||||
"id": "bAkhPioPM1uAda6K2aJzHD",
|
||||
"type": "start",
|
||||
"label": "Start",
|
||||
"blockId": "cAvp3oQUNYcANvcEQEVSpD",
|
||||
"outgoingEdgeId": "2V3HtAH5fSAm6fyYzCyotq"
|
||||
}
|
||||
],
|
||||
"title": "Start",
|
||||
"graphCoordinates": { "x": 0, "y": 0 }
|
||||
},
|
||||
{
|
||||
"id": "8KLYVvRVGVHRQGJHHe2YPv",
|
||||
"graphCoordinates": { "x": 362, "y": 96 },
|
||||
"title": "Block #1",
|
||||
"steps": [
|
||||
{
|
||||
"id": "s7QbNUSgojnka9v9LX7Tp7L",
|
||||
"blockId": "8KLYVvRVGVHRQGJHHe2YPv",
|
||||
"type": "Set variable",
|
||||
"options": {
|
||||
"variableId": "htYvG7crtdjpsZ6XKTh1PM",
|
||||
"expressionToEvaluate": "Baptiste"
|
||||
},
|
||||
"outgoingEdgeId": "7kKfQWo6xFy97cTwV7B2w7"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "4H8ucvLjTiQ7sAyB23Huka",
|
||||
"graphCoordinates": { "x": 723, "y": 203 },
|
||||
"title": "Block #2",
|
||||
"steps": [
|
||||
{
|
||||
"id": "s4xoCc33mHyKv6hbVWd8MLo",
|
||||
"blockId": "4H8ucvLjTiQ7sAyB23Huka",
|
||||
"type": "text",
|
||||
"content": {
|
||||
"html": "<div>What's your name?</div>",
|
||||
"richText": [
|
||||
{ "type": "p", "children": [{ "text": "What's your name?" }] }
|
||||
],
|
||||
"plainText": "What's your name?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "s5AjAPTMbUbhYxVTjSNwQuJ",
|
||||
"blockId": "4H8ucvLjTiQ7sAyB23Huka",
|
||||
"type": "text input",
|
||||
"options": {
|
||||
"isLong": false,
|
||||
"labels": {
|
||||
"button": "Send",
|
||||
"placeholder": "Type your answer..."
|
||||
},
|
||||
"variableId": "htYvG7crtdjpsZ6XKTh1PM"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"variables": [{ "id": "htYvG7crtdjpsZ6XKTh1PM", "name": "Name" }],
|
||||
"edges": [
|
||||
{
|
||||
"from": {
|
||||
"blockId": "cAvp3oQUNYcANvcEQEVSpD",
|
||||
"stepId": "bAkhPioPM1uAda6K2aJzHD"
|
||||
},
|
||||
"to": { "blockId": "8KLYVvRVGVHRQGJHHe2YPv" },
|
||||
"id": "2V3HtAH5fSAm6fyYzCyotq"
|
||||
},
|
||||
{
|
||||
"from": {
|
||||
"blockId": "8KLYVvRVGVHRQGJHHe2YPv",
|
||||
"stepId": "s7QbNUSgojnka9v9LX7Tp7L"
|
||||
},
|
||||
"to": { "blockId": "4H8ucvLjTiQ7sAyB23Huka" },
|
||||
"id": "7kKfQWo6xFy97cTwV7B2w7"
|
||||
}
|
||||
],
|
||||
"theme": {
|
||||
"chat": {
|
||||
"inputs": {
|
||||
"color": "#303235",
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"placeholderColor": "#9095A0"
|
||||
},
|
||||
"buttons": { "color": "#FFFFFF", "backgroundColor": "#0042DA" },
|
||||
"hostBubbles": { "color": "#303235", "backgroundColor": "#F7F8FF" },
|
||||
"guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" }
|
||||
},
|
||||
"general": { "font": "Open Sans", "background": { "type": "None" } }
|
||||
},
|
||||
"settings": {
|
||||
"general": {
|
||||
"isBrandingEnabled": true,
|
||||
"isInputPrefillEnabled": true,
|
||||
"isNewResultOnRefreshEnabled": false
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Build beautiful conversational forms and embed them directly in your applications without a line of code. Triple your response rate and collect answers that has more value compared to a traditional form."
|
||||
},
|
||||
"typingEmulation": { "speed": 300, "enabled": true, "maxDelay": 1.5 }
|
||||
},
|
||||
"publicId": null,
|
||||
"customDomain": null
|
||||
}
|
BIN
apps/builder/playwright/fixtures/typebots/settings.png
Normal file
BIN
apps/builder/playwright/fixtures/typebots/settings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 394 KiB |
@ -1,4 +1,5 @@
|
||||
import test, { expect } from '@playwright/test'
|
||||
import { defaultTextInputOptions } from 'models'
|
||||
import path from 'path'
|
||||
import { generate } from 'short-uuid'
|
||||
import { importTypebotInDatabase } from '../services/database'
|
||||
@ -9,11 +10,12 @@ test.describe.parallel('Settings page', () => {
|
||||
test('should reflect change in real-time', async ({ page }) => {
|
||||
const typebotId = generate()
|
||||
await importTypebotInDatabase(
|
||||
path.join(__dirname, '../fixtures/typebots/theme.json'),
|
||||
path.join(__dirname, '../fixtures/typebots/settings.json'),
|
||||
{
|
||||
id: typebotId,
|
||||
}
|
||||
)
|
||||
|
||||
await page.goto(`/typebots/${typebotId}/settings`)
|
||||
await expect(
|
||||
typebotViewer(page).locator('a:has-text("Made with Typebot")')
|
||||
@ -23,10 +25,24 @@ test.describe.parallel('Settings page', () => {
|
||||
await expect(
|
||||
typebotViewer(page).locator('a:has-text("Made with Typebot")')
|
||||
).toBeHidden()
|
||||
|
||||
await page.click('text=Create new session on page refresh')
|
||||
await expect(
|
||||
page.locator('input[type="checkbox"] >> nth=-1')
|
||||
).toHaveAttribute('checked', '')
|
||||
|
||||
await expect(
|
||||
typebotViewer(page).locator('input[value="Baptiste"]')
|
||||
).toBeVisible()
|
||||
await page.click('text=Prefill input')
|
||||
await page.click('text=Theme')
|
||||
await page.waitForTimeout(1000)
|
||||
await page.click('text=Settings')
|
||||
await expect(
|
||||
typebotViewer(page).locator(
|
||||
`input[placeholder="${defaultTextInputOptions.labels.placeholder}"]`
|
||||
)
|
||||
).toHaveValue('')
|
||||
})
|
||||
})
|
||||
|
||||
@ -34,7 +50,7 @@ test.describe.parallel('Settings page', () => {
|
||||
test('should be fillable', async ({ page }) => {
|
||||
const typebotId = generate()
|
||||
await importTypebotInDatabase(
|
||||
path.join(__dirname, '../fixtures/typebots/theme.json'),
|
||||
path.join(__dirname, '../fixtures/typebots/settings.json'),
|
||||
{
|
||||
id: typebotId,
|
||||
}
|
||||
@ -57,7 +73,7 @@ test.describe.parallel('Settings page', () => {
|
||||
const imageUrl = 'https://www.baptistearno.com/images/site-preview.png'
|
||||
const typebotId = 'metadata-typebot'
|
||||
await importTypebotInDatabase(
|
||||
path.join(__dirname, '../fixtures/typebots/theme.json'),
|
||||
path.join(__dirname, '../fixtures/typebots/settings.json'),
|
||||
{
|
||||
id: typebotId,
|
||||
}
|
||||
@ -101,7 +117,7 @@ test.describe.parallel('Settings page', () => {
|
||||
test("can't remove branding", async ({ page }) => {
|
||||
const typebotId = 'free-branding-typebot'
|
||||
await importTypebotInDatabase(
|
||||
path.join(__dirname, '../fixtures/typebots/theme.json'),
|
||||
path.join(__dirname, '../fixtures/typebots/settings.json'),
|
||||
{
|
||||
id: typebotId,
|
||||
}
|
||||
|
@ -26,7 +26,9 @@ export const InputChatStep = ({
|
||||
|
||||
const { variableId } = step.options
|
||||
const defaultValue =
|
||||
variableId && typebot.variables.find(byId(variableId))?.value
|
||||
typebot.settings.general.isInputPrefillEnabled ?? true
|
||||
? variableId && typebot.variables.find(byId(variableId))?.value
|
||||
: undefined
|
||||
|
||||
const handleSubmit = (content: string) => {
|
||||
setAnswer(content)
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Avatar } from 'components/avatars/Avatar'
|
||||
import React from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import { CSSTransition } from 'react-transition-group'
|
||||
|
||||
interface Props {
|
||||
@ -15,6 +15,8 @@ export const GuestBubble = ({
|
||||
avatarSrc,
|
||||
onClick,
|
||||
}: Props): JSX.Element => {
|
||||
const [content] = useState(message)
|
||||
|
||||
return (
|
||||
<CSSTransition classNames="bubble" timeout={1000}>
|
||||
<div className="flex justify-end mb-2 items-end" onClick={onClick}>
|
||||
@ -22,7 +24,7 @@ export const GuestBubble = ({
|
||||
className="px-4 py-2 rounded-lg mr-2 whitespace-pre-wrap max-w-full typebot-guest-bubble cursor-pointer hover:brightness-90 active:brightness-75"
|
||||
data-testid="guest-bubble"
|
||||
>
|
||||
{message}
|
||||
{content}
|
||||
</span>
|
||||
{showAvatar && <Avatar avatarSrc={avatarSrc} />}
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { BubbleStepType, TextBubbleStep } from 'models'
|
||||
import { computeTypingTimeout } from 'services/chat'
|
||||
@ -23,10 +23,8 @@ export const TextBubble = ({ step, onTransitionEnd }: Props) => {
|
||||
const messageContainer = useRef<HTMLDivElement | null>(null)
|
||||
const [isTyping, setIsTyping] = useState(true)
|
||||
|
||||
const content = useMemo(
|
||||
() => parseVariables(typebot.variables)(step.content.html),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[typebot.variables]
|
||||
const [content] = useState(
|
||||
parseVariables(typebot.variables)(step.content.html)
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -7,6 +7,7 @@ export type Settings = {
|
||||
export type GeneralSettings = {
|
||||
isBrandingEnabled: boolean
|
||||
isNewResultOnRefreshEnabled?: boolean
|
||||
isInputPrefillEnabled?: boolean
|
||||
}
|
||||
|
||||
export type TypingEmulation = {
|
||||
@ -23,7 +24,11 @@ export type Metadata = {
|
||||
}
|
||||
|
||||
export const defaultSettings: Settings = {
|
||||
general: { isBrandingEnabled: true, isNewResultOnRefreshEnabled: false },
|
||||
general: {
|
||||
isBrandingEnabled: true,
|
||||
isNewResultOnRefreshEnabled: false,
|
||||
isInputPrefillEnabled: true,
|
||||
},
|
||||
typingEmulation: { enabled: true, speed: 300, maxDelay: 1.5 },
|
||||
metadata: {
|
||||
description:
|
||||
|
Reference in New Issue
Block a user