diff --git a/apps/builder/assets/icons.tsx b/apps/builder/assets/icons.tsx
index 7ca909ee6d..40514726e0 100644
--- a/apps/builder/assets/icons.tsx
+++ b/apps/builder/assets/icons.tsx
@@ -224,3 +224,9 @@ export const EmailIcon = (props: IconProps) => (
)
+
+export const PhoneIcon = (props: IconProps) => (
+
+
+
+)
diff --git a/apps/builder/components/board/StepTypesList/StepIcon.tsx b/apps/builder/components/board/StepTypesList/StepIcon.tsx
index d4f5a7c1fc..e08e3ca42a 100644
--- a/apps/builder/components/board/StepTypesList/StepIcon.tsx
+++ b/apps/builder/components/board/StepTypesList/StepIcon.tsx
@@ -5,6 +5,7 @@ import {
FlagIcon,
GlobeIcon,
NumberIcon,
+ PhoneIcon,
TextIcon,
} from 'assets/icons'
import { BubbleStepType, InputStepType, StepType } from 'models'
@@ -32,6 +33,9 @@ export const StepIcon = ({ type }: StepIconProps) => {
case InputStepType.DATE: {
return
}
+ case InputStepType.PHONE: {
+ return
+ }
case 'start': {
return
}
diff --git a/apps/builder/components/board/StepTypesList/StepTypeLabel.tsx b/apps/builder/components/board/StepTypesList/StepTypeLabel.tsx
index caf448add6..ce53045945 100644
--- a/apps/builder/components/board/StepTypesList/StepTypeLabel.tsx
+++ b/apps/builder/components/board/StepTypesList/StepTypeLabel.tsx
@@ -22,6 +22,9 @@ export const StepTypeLabel = ({ type }: Props) => {
case InputStepType.DATE: {
return Date
}
+ case InputStepType.PHONE: {
+ return Phone
+ }
default: {
return <>>
}
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 1dae6c2770..c2582fe47d 100644
--- a/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx
+++ b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx
@@ -8,6 +8,7 @@ import {
UrlInputSettingsBody,
DateInputSettingsBody,
} from './bodies'
+import { PhoneNumberSettingsBody } from './bodies/PhoneNumberSettingsBody'
type Props = {
step: Step
@@ -71,6 +72,14 @@ const SettingsPopoverBodyContent = ({ step }: Props) => {
/>
)
}
+ case InputStepType.PHONE: {
+ return (
+
+ )
+ }
default: {
return <>>
}
diff --git a/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/bodies/PhoneNumberSettingsBody.tsx b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/bodies/PhoneNumberSettingsBody.tsx
new file mode 100644
index 0000000000..8a352b885a
--- /dev/null
+++ b/apps/builder/components/board/graph/BlockNode/StepNode/SettingsPopoverContent/bodies/PhoneNumberSettingsBody.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 PhoneNumberSettingsBodyProps = {
+ options?: EmailInputOptions
+ onOptionsChange: (options: EmailInputOptions) => void
+}
+
+export const PhoneNumberSettingsBody = ({
+ options,
+ onOptionsChange,
+}: PhoneNumberSettingsBodyProps) => {
+ 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/StepNodeLabel.tsx b/apps/builder/components/board/graph/BlockNode/StepNode/StepNodeLabel.tsx
index 28bb950f0a..01cd99c8c8 100644
--- a/apps/builder/components/board/graph/BlockNode/StepNode/StepNodeLabel.tsx
+++ b/apps/builder/components/board/graph/BlockNode/StepNode/StepNodeLabel.tsx
@@ -53,6 +53,13 @@ export const StepNodeLabel = (props: Step | StartStep) => {
)
}
+ case InputStepType.PHONE: {
+ return (
+
+ {props.options?.labels?.placeholder ?? 'Your phone number...'}
+
+ )
+ }
case 'start': {
return {props.label}
}
diff --git a/apps/builder/cypress/tests/inputs.ts b/apps/builder/cypress/tests/inputs.ts
index 1160b00911..8df832956e 100644
--- a/apps/builder/cypress/tests/inputs.ts
+++ b/apps/builder/cypress/tests/inputs.ts
@@ -144,7 +144,7 @@ describe('Date 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()
@@ -172,6 +172,41 @@ describe('Date input', () => {
})
})
+describe('Phone number input', () => {
+ beforeEach(() => {
+ cy.task('seed')
+ cy.log(JSON.stringify({ type: InputStepType.PHONE }))
+ createTypebotWithStep({ type: InputStepType.PHONE })
+ cy.signOut()
+ })
+
+ it('options should work', () => {
+ cy.signIn('test2@gmail.com')
+ cy.visit('/typebots/typebot3/edit')
+ cy.findByRole('button', { name: 'Preview' }).click()
+ getIframeBody()
+ .findByPlaceholderText('Your phone number...')
+ .should('have.attr', 'type')
+ .should('eq', 'tel')
+ getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
+ cy.findByTestId('step-step1').click({ force: true })
+ cy.findByRole('textbox', { name: 'Placeholder:' })
+ .clear()
+ .type('+33 XX XX XX XX')
+ cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
+ cy.findByRole('button', { name: 'Restart' }).click()
+ getIframeBody()
+ .findByPlaceholderText('+33 XX XX XX XX')
+ .type('+33 6 73 18 45 36')
+ getIframeBody()
+ .findByRole('img')
+ .should('have.attr', 'alt')
+ .should('eq', 'France')
+ getIframeBody().findByRole('button', { name: 'Go' }).click()
+ getIframeBody().findByText('+33673184536').should('exist')
+ })
+})
+
const createTypebotWithStep = (step: Omit) => {
cy.task(
'createTypebot',
diff --git a/packages/bot-engine/package.json b/packages/bot-engine/package.json
index 91a31128c1..0d10501377 100644
--- a/packages/bot-engine/package.json
+++ b/packages/bot-engine/package.json
@@ -10,15 +10,18 @@
"fast-equals": "^2.0.4",
"models": "*",
"react-frame-component": "^5.2.1",
+ "react-phone-number-input": "^3.1.44",
"react-scroll": "^1.8.4",
"react-transition-group": "^4.4.2",
"utils": "*"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^21.0.1",
+ "@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.1.3",
"@rollup/plugin-typescript": "^8.3.0",
"@types/react": "^17.0.38",
+ "@types/react-phone-number-input": "^3.0.13",
"@types/react-scroll": "^1.8.3",
"@types/react-transition-group": "^4.4.4",
"autoprefixer": "^10.4.1",
diff --git a/packages/bot-engine/rollup.config.js b/packages/bot-engine/rollup.config.js
index af871fe3d2..2bf4cdd3f9 100644
--- a/packages/bot-engine/rollup.config.js
+++ b/packages/bot-engine/rollup.config.js
@@ -1,6 +1,7 @@
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import typescript from '@rollup/plugin-typescript'
+import json from '@rollup/plugin-json'
import dts from 'rollup-plugin-dts'
import postcss from 'rollup-plugin-postcss'
import { terser } from 'rollup-plugin-terser'
@@ -28,6 +29,7 @@ export default [
plugins: [
peerDepsExternal(),
resolve(),
+ json(),
commonjs(),
typescript({ tsconfig: './tsconfig.json' }),
postcss({
diff --git a/packages/bot-engine/src/assets/style.css b/packages/bot-engine/src/assets/style.css
index 600aa5d133..333c3c99f6 100644
--- a/packages/bot-engine/src/assets/style.css
+++ b/packages/bot-engine/src/assets/style.css
@@ -38,6 +38,10 @@
--typebot-header-border: none;
--typebot-header-shadow: none;
--typebot-header-max-width: 1000px;
+
+ /* Phone input */
+ --PhoneInputCountryFlag-borderColor: transparent;
+ --PhoneInput-color--focus: transparent;
}
/* Hide scrollbar for Chrome, Safari and Opera */
diff --git a/packages/bot-engine/src/components/ChatBlock/ChatStep/ChatStep.tsx b/packages/bot-engine/src/components/ChatBlock/ChatStep/ChatStep.tsx
index 22ce71dde5..39c937ac98 100644
--- a/packages/bot-engine/src/components/ChatBlock/ChatStep/ChatStep.tsx
+++ b/packages/bot-engine/src/components/ChatBlock/ChatStep/ChatStep.tsx
@@ -56,6 +56,7 @@ const InputChatStep = ({
case InputStepType.NUMBER:
case InputStepType.EMAIL:
case InputStepType.URL:
+ case InputStepType.PHONE:
return
case InputStepType.DATE:
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
index c2e021b61f..137a4578c5 100644
--- a/packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/TextForm.tsx
+++ b/packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/TextForm.tsx
@@ -1,6 +1,7 @@
import {
EmailInputStep,
NumberInputStep,
+ PhoneNumberInputStep,
TextInputStep,
UrlInputStep,
} from 'models'
@@ -9,7 +10,12 @@ import { SendButton } from '../SendButton'
import { TextInput } from './TextInputContent'
type TextFormProps = {
- step: TextInputStep | EmailInputStep | NumberInputStep | UrlInputStep
+ step:
+ | TextInputStep
+ | EmailInputStep
+ | NumberInputStep
+ | UrlInputStep
+ | PhoneNumberInputStep
onSubmit: (value: string) => void
}
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
index 544e4f3aae..12b48a3705 100644
--- a/packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/TextInputContent.tsx
+++ b/packages/bot-engine/src/components/ChatBlock/ChatStep/inputs/TextForm/TextInputContent.tsx
@@ -4,6 +4,7 @@ import {
NumberInputStep,
InputStepType,
UrlInputStep,
+ PhoneNumberInputStep,
} from 'models'
import React, {
ChangeEvent,
@@ -12,14 +13,20 @@ import React, {
useEffect,
useRef,
} from 'react'
+import PhoneInput, { Value } from 'react-phone-number-input'
type TextInputProps = {
- step: TextInputStep | EmailInputStep | NumberInputStep | UrlInputStep
+ step:
+ | TextInputStep
+ | EmailInputStep
+ | NumberInputStep
+ | UrlInputStep
+ | PhoneNumberInputStep
onChange: (value: string) => void
}
export const TextInput = ({ step, onChange }: TextInputProps) => {
- const inputRef = useRef(null)
+ const inputRef = useRef(null)
useEffect(() => {
if (!inputRef.current) return
@@ -30,6 +37,10 @@ export const TextInput = ({ step, onChange }: TextInputProps) => {
e: ChangeEvent
) => onChange(e.target.value)
+ const handlePhoneNumberChange = (value?: Value | undefined) => {
+ onChange(value as string)
+ }
+
switch (step.type) {
case InputStepType.TEXT: {
return step.options?.isLong ? (
@@ -88,6 +99,17 @@ export const TextInput = ({ step, onChange }: TextInputProps) => {
/>
)
}
+ case InputStepType.PHONE: {
+ return (
+
+ )
+ }
}
}
diff --git a/packages/bot-engine/src/components/TypebotViewer.tsx b/packages/bot-engine/src/components/TypebotViewer.tsx
index 7982589aa6..83885f5bb1 100644
--- a/packages/bot-engine/src/components/TypebotViewer.tsx
+++ b/packages/bot-engine/src/components/TypebotViewer.tsx
@@ -3,6 +3,8 @@ import { TypebotContext } from '../contexts/TypebotContext'
import Frame from 'react-frame-component'
//@ts-ignore
import style from '../assets/style.css'
+//@ts-ignore
+import phoneNumberInputStyle from 'react-phone-number-input/style.css'
import { ConversationContainer } from './ConversationContainer'
import { AnswersContext } from '../contexts/AnswersContext'
import { Answer, BackgroundType, PublicTypebot } from 'models'
@@ -39,7 +41,12 @@ export const TypebotViewer = ({
return (
{style}}
+ head={
+
+ }
style={{ width: '100%', height: '100%', border: 'none' }}
>