From 47162cb28afb58e83458cd344ba18c27f6ad9787 Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Sat, 8 Jan 2022 08:20:54 +0100 Subject: [PATCH] =?UTF-8?q?feat(inputs):=20=E2=9C=A8=20Add=20email=20input?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/builder/assets/icons.tsx | 7 ++ .../board/StepTypesList/StepIcon.tsx | 11 +- .../board/StepTypesList/StepTypeLabel.tsx | 3 + .../EmailInputSettingsBody.tsx | 46 +++++++ .../SettingsPopoverContent.tsx | 9 ++ .../BlockNode/StepNode/StepNodeLabel.tsx | 7 ++ apps/builder/cypress/tests/inputs.ts | 31 ++++- .../ChatBlock/ChatStep/ChatStep.tsx | 9 +- .../ChatStep/inputs/TextForm/TextForm.tsx | 46 +++++++ .../inputs/TextForm/TextInputContent.tsx | 112 ++++++++++++++++++ .../ChatStep/inputs/TextForm/index.tsx | 1 + .../ChatBlock/ChatStep/inputs/TextInput.tsx | 67 ----------- packages/models/src/typebot/steps.ts | 10 +- 13 files changed, 282 insertions(+), 77 deletions(-) create mode 100644 apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/EmailInputSettingsBody.tsx create mode 100644 packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/TextForm.tsx create mode 100644 packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/TextInputContent.tsx create mode 100644 packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/index.tsx delete mode 100644 packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextInput.tsx diff --git a/apps/builder/assets/icons.tsx b/apps/builder/assets/icons.tsx index 3039a1a83..7ca909ee6 100644 --- a/apps/builder/assets/icons.tsx +++ b/apps/builder/assets/icons.tsx @@ -217,3 +217,10 @@ export const NumberIcon = (props: IconProps) => ( ) + +export const EmailIcon = (props: IconProps) => ( + + + + +) diff --git a/apps/builder/components/board/StepTypesList/StepIcon.tsx b/apps/builder/components/board/StepTypesList/StepIcon.tsx index 80792505d..9d10d3a2d 100644 --- a/apps/builder/components/board/StepTypesList/StepIcon.tsx +++ b/apps/builder/components/board/StepTypesList/StepIcon.tsx @@ -1,4 +1,10 @@ -import { ChatIcon, FlagIcon, NumberIcon, TextIcon } from 'assets/icons' +import { + ChatIcon, + EmailIcon, + FlagIcon, + NumberIcon, + TextIcon, +} from 'assets/icons' import { BubbleStepType, InputStepType, StepType } from 'models' import React from 'react' @@ -15,6 +21,9 @@ export const StepIcon = ({ type }: StepIconProps) => { case InputStepType.NUMBER: { return } + case InputStepType.EMAIL: { + return + } case 'start': { return } diff --git a/apps/builder/components/board/StepTypesList/StepTypeLabel.tsx b/apps/builder/components/board/StepTypesList/StepTypeLabel.tsx index 231fa7d74..c7109690e 100644 --- a/apps/builder/components/board/StepTypesList/StepTypeLabel.tsx +++ b/apps/builder/components/board/StepTypesList/StepTypeLabel.tsx @@ -13,6 +13,9 @@ export const StepTypeLabel = ({ type }: Props) => { case InputStepType.NUMBER: { return Number } + case InputStepType.EMAIL: { + return Email + } default: { return <> } diff --git a/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/EmailInputSettingsBody.tsx b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/EmailInputSettingsBody.tsx new file mode 100644 index 000000000..dd8aaeeee --- /dev/null +++ b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/EmailInputSettingsBody.tsx @@ -0,0 +1,46 @@ +import { FormLabel, Stack } from '@chakra-ui/react' +import { DebouncedInput } from 'components/shared/DebouncedInput' +import { EmailInputOptions } from 'models' +import React from 'react' + +type EmailInputSettingsBodyProps = { + options?: EmailInputOptions + onOptionsChange: (options: EmailInputOptions) => void +} + +export const EmailInputSettingsBody = ({ + options, + onOptionsChange, +}: EmailInputSettingsBodyProps) => { + const handlePlaceholderChange = (placeholder: string) => + onOptionsChange({ ...options, labels: { ...options?.labels, placeholder } }) + const handleButtonLabelChange = (button: string) => + onOptionsChange({ ...options, labels: { ...options?.labels, button } }) + + return ( + + + + Placeholder: + + + + + + Button label: + + + + + ) +} diff --git a/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx index 2c2915257..242339d86 100644 --- a/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx +++ b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx @@ -1,6 +1,7 @@ import { PopoverContent, PopoverArrow, PopoverBody } from '@chakra-ui/react' import { useTypebot } from 'contexts/TypebotContext/TypebotContext' import { InputStepType, Step, TextInputOptions } from 'models' +import { EmailInputSettingsBody } from './EmailInputSettingsBody' import { NumberInputSettingsBody } from './NumberInputSettingsBody' import { TextInputSettingsBody } from './TextInputSettingsBody' @@ -42,6 +43,14 @@ const SettingsPopoverBodyContent = ({ step }: Props) => { /> ) } + case InputStepType.EMAIL: { + return ( + + ) + } default: { return <> } diff --git a/apps/builder/components/board/graph/BlockNode/StepNode/StepNodeLabel.tsx b/apps/builder/components/board/graph/BlockNode/StepNode/StepNodeLabel.tsx index bcee1ce80..3c2d137e5 100644 --- a/apps/builder/components/board/graph/BlockNode/StepNode/StepNodeLabel.tsx +++ b/apps/builder/components/board/graph/BlockNode/StepNode/StepNodeLabel.tsx @@ -32,6 +32,13 @@ export const StepNodeLabel = (props: Step | StartStep) => { ) } + case InputStepType.EMAIL: { + return ( + + {props.options?.labels?.placeholder ?? 'Type your email...'} + + ) + } case 'start': { return {props.label} } diff --git a/apps/builder/cypress/tests/inputs.ts b/apps/builder/cypress/tests/inputs.ts index 7fb78250a..e8de99452 100644 --- a/apps/builder/cypress/tests/inputs.ts +++ b/apps/builder/cypress/tests/inputs.ts @@ -13,7 +13,7 @@ describe('Text input', () => { cy.visit('/typebots/typebot3/edit') cy.findByRole('button', { name: 'Preview' }).click() getIframeBody().findByPlaceholderText('Type your answer...').should('exist') - getIframeBody().findByRole('button', { name: 'Send' }) + getIframeBody().findByRole('button', { name: 'Send' }).should('exist') cy.findByTestId('step-step1').click({ force: true }) cy.findByRole('textbox', { name: 'Placeholder:' }) .clear() @@ -37,12 +37,12 @@ describe('Number input', () => { cy.signOut() }) - it.only('options should work', () => { + it('options should work', () => { cy.signIn('test2@gmail.com') cy.visit('/typebots/typebot3/edit') cy.findByRole('button', { name: 'Preview' }).click() getIframeBody().findByPlaceholderText('Type your answer...').should('exist') - getIframeBody().findByRole('button', { name: 'Send' }) + getIframeBody().findByRole('button', { name: 'Send' }).should('exist') cy.findByTestId('step-step1').click({ force: true }) cy.findByRole('textbox', { name: 'Placeholder:' }) .clear() @@ -64,6 +64,31 @@ describe('Number input', () => { }) }) +describe('Email input', () => { + beforeEach(() => { + cy.task('seed') + createTypebotWithStep({ type: InputStepType.EMAIL }) + cy.signOut() + }) + + it.only('options should work', () => { + cy.signIn('test2@gmail.com') + cy.visit('/typebots/typebot3/edit') + cy.findByRole('button', { name: 'Preview' }).click() + getIframeBody().findByPlaceholderText('Type your email...').should('exist') + getIframeBody().findByRole('button', { name: 'Send' }) + cy.findByTestId('step-step1').click({ force: true }) + cy.findByRole('textbox', { name: 'Placeholder:' }) + .clear() + .type('Your email...') + cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go') + cy.findByTestId('step-step1').should('contain.text', 'Your email...') + cy.findByRole('button', { name: 'Restart' }).click() + getIframeBody().findByPlaceholderText('Your email...').should('exist') + getIframeBody().findByRole('button', { name: 'Go' }) + }) +}) + const createTypebotWithStep = (step: Omit) => { cy.task( 'createTypebot', diff --git a/packages/bot-engine/src/components/ChatBlock/ChatStep/ChatStep.tsx b/packages/bot-engine/src/components/ChatBlock/ChatStep/ChatStep.tsx index 82fb3fcb5..75bcaeaf2 100644 --- a/packages/bot-engine/src/components/ChatBlock/ChatStep/ChatStep.tsx +++ b/packages/bot-engine/src/components/ChatBlock/ChatStep/ChatStep.tsx @@ -4,9 +4,8 @@ import { useHostAvatars } from '../../../contexts/HostAvatarsContext' import { InputStep, InputStepType, Step } from 'models' import { GuestBubble } from './bubbles/GuestBubble' import { HostMessageBubble } from './bubbles/HostMessageBubble' -import { TextInput } from './inputs/TextInput' -import { isInputStep, isTextBubbleStep, isTextInputStep } from 'utils' -import { NumberInput } from './inputs/NumberInput' +import { TextForm } from './inputs/TextForm' +import { isInputStep, isTextBubbleStep } from 'utils' export const ChatStep = ({ step, @@ -53,8 +52,8 @@ const InputChatStep = ({ } switch (step.type) { case InputStepType.TEXT: - return case InputStepType.NUMBER: - return + case InputStepType.EMAIL: + return } } diff --git a/packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/TextForm.tsx b/packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/TextForm.tsx new file mode 100644 index 000000000..b52bd0ba9 --- /dev/null +++ b/packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/TextForm.tsx @@ -0,0 +1,46 @@ +import { EmailInputStep, NumberInputStep, TextInputStep } from 'models' +import React, { FormEvent, useState } from 'react' +import { SendIcon } from '../../../../../assets/icons' +import { TextInput } from './TextInputContent' + +type TextFormProps = { + step: TextInputStep | EmailInputStep | NumberInputStep + onSubmit: (value: string) => void +} + +export const TextForm = ({ step, onSubmit }: TextFormProps) => { + const [inputValue, setInputValue] = useState('') + + const handleChange = (inputValue: string) => setInputValue(inputValue) + + const handleSubmit = (e: FormEvent) => { + e.preventDefault() + if (inputValue === '') return + onSubmit(inputValue) + } + + return ( +
+
+
+ + + +
+
+ ) +} diff --git a/packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/TextInputContent.tsx b/packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/TextInputContent.tsx new file mode 100644 index 000000000..1dfb7c49c --- /dev/null +++ b/packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/TextInputContent.tsx @@ -0,0 +1,112 @@ +import { + TextInputStep, + EmailInputStep, + NumberInputStep, + InputStepType, +} from 'models' +import React, { + ChangeEvent, + ChangeEventHandler, + RefObject, + useEffect, + useRef, +} from 'react' + +type TextInputProps = { + step: TextInputStep | EmailInputStep | NumberInputStep + onChange: (value: string) => void +} + +export const TextInput = ({ step, onChange }: TextInputProps) => { + const inputRef = useRef(null) + + useEffect(() => { + if (!inputRef.current) return + inputRef.current.focus() + }, []) + + const handleInputChange = ( + e: ChangeEvent + ) => onChange(e.target.value) + + switch (step.type) { + case InputStepType.TEXT: { + return step.options?.isLong ? ( + } + placeholder={ + step.options?.labels?.placeholder ?? 'Type your answer...' + } + onChange={handleInputChange} + /> + ) : ( + + ) + } + case InputStepType.EMAIL: { + return ( + + ) + } + case InputStepType.NUMBER: { + return ( + + ) + } + } +} + +const ShortTextInput = React.forwardRef( + ( + props: React.InputHTMLAttributes, + ref: React.ForwardedRef + ) => ( + + ) +) + +const LongTextInput = React.forwardRef( + ( + props: { placeholder: string; onChange: ChangeEventHandler }, + ref: React.ForwardedRef + ) => ( +