2
0

feat(editor): 🚸 Improve and unify inputs

This commit is contained in:
Baptiste Arnaud
2022-03-10 11:34:55 +01:00
parent 36838f05d3
commit 2c1f69439b
32 changed files with 187 additions and 251 deletions

View File

@ -9,10 +9,7 @@ import {
PopoverContent, PopoverContent,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { ImageUploadContent } from 'components/shared/ImageUploadContent' import { ImageUploadContent } from 'components/shared/ImageUploadContent'
import { import { Input, Textarea } from 'components/shared/Textbox'
InputWithVariableButton,
TextareaWithVariableButton,
} from 'components/shared/TextboxWithVariableButton'
type Props = { type Props = {
typebotName: string typebotName: string
@ -89,9 +86,9 @@ export const MetadataForm = ({
<FormLabel mb="0" htmlFor="title"> <FormLabel mb="0" htmlFor="title">
Title: Title:
</FormLabel> </FormLabel>
<InputWithVariableButton <Input
id="title" id="title"
initialValue={metadata.title ?? typebotName} defaultValue={metadata.title ?? typebotName}
onChange={handleTitleChange} onChange={handleTitleChange}
/> />
</Stack> </Stack>
@ -99,9 +96,9 @@ export const MetadataForm = ({
<FormLabel mb="0" htmlFor="description"> <FormLabel mb="0" htmlFor="description">
Description: Description:
</FormLabel> </FormLabel>
<TextareaWithVariableButton <Textarea
id="description" id="description"
initialValue={metadata.description} defaultValue={metadata.description}
onChange={handleDescriptionChange} onChange={handleDescriptionChange}
/> />
</Stack> </Stack>

View File

@ -1,45 +0,0 @@
import { Input, InputProps } from '@chakra-ui/react'
import {
ChangeEvent,
ForwardedRef,
forwardRef,
useEffect,
useState,
} from 'react'
import { useDebounce } from 'use-debounce'
type Props = Omit<InputProps, 'onChange' | 'value'> & {
initialValue: string
onChange: (debouncedValue: string) => void
}
export const DebouncedInput = forwardRef(
(
{ onChange, initialValue, ...props }: Props,
ref: ForwardedRef<HTMLInputElement>
) => {
const [currentValue, setCurrentValue] = useState(initialValue)
const [debouncedValue] = useDebounce(
currentValue,
process.env.NEXT_PUBLIC_E2E_TEST ? 0 : 1000
)
useEffect(() => {
if (debouncedValue === initialValue) return
onChange(debouncedValue)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedValue])
const handleChange = (e: ChangeEvent<HTMLInputElement>) =>
setCurrentValue(e.target.value)
return (
<Input
{...props}
ref={ref}
value={currentValue}
onChange={handleChange}
/>
)
}
)

View File

@ -1,39 +0,0 @@
import { Textarea, TextareaProps } from '@chakra-ui/react'
import { ChangeEvent, useEffect, useState } from 'react'
import { useDebounce } from 'use-debounce'
type Props = Omit<TextareaProps, 'onChange' | 'value'> & {
initialValue: string
onChange: (debouncedValue: string) => void
}
export const DebouncedTextarea = ({
onChange,
initialValue,
...props
}: Props) => {
const [currentValue, setCurrentValue] = useState(initialValue)
const [debouncedValue] = useDebounce(
currentValue,
process.env.NEXT_PUBLIC_E2E_TEST ? 0 : 1000
)
useEffect(() => {
if (debouncedValue === initialValue) return
onChange(debouncedValue)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedValue])
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
setCurrentValue(e.target.value)
}
return (
<Textarea
{...props}
value={currentValue}
onChange={handleChange}
resize={'vertical'}
/>
)
}

View File

@ -1,5 +1,5 @@
import { Stack, Text } from '@chakra-ui/react' import { Stack, Text } from '@chakra-ui/react'
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton/InputWithVariableButton' import { Input } from 'components/shared/Textbox/Input'
import { VideoBubbleContent, VideoBubbleContentType } from 'models' import { VideoBubbleContent, VideoBubbleContentType } from 'models'
import urlParser from 'js-video-url-parser/lib/base' import urlParser from 'js-video-url-parser/lib/base'
import 'js-video-url-parser/lib/provider/vimeo' import 'js-video-url-parser/lib/provider/vimeo'
@ -24,9 +24,9 @@ export const VideoUploadContent = ({ content, onSubmit }: Props) => {
} }
return ( return (
<Stack p="2"> <Stack p="2">
<InputWithVariableButton <Input
placeholder="Paste the video link..." placeholder="Paste the video link..."
initialValue={content?.url ?? ''} defaultValue={content?.url ?? ''}
onChange={handleUrlChange} onChange={handleUrlChange}
/> />
<Text fontSize="sm" color="gray.400" textAlign="center"> <Text fontSize="sm" color="gray.400" textAlign="center">

View File

@ -1,6 +1,6 @@
import { FormLabel, Stack } from '@chakra-ui/react' import { FormLabel, Stack } from '@chakra-ui/react'
import { DebouncedInput } from 'components/shared/DebouncedInput'
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel' import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
import { Input } from 'components/shared/Textbox'
import { VariableSearchInput } from 'components/shared/VariableSearchInput' import { VariableSearchInput } from 'components/shared/VariableSearchInput'
import { ChoiceInputOptions, Variable } from 'models' import { ChoiceInputOptions, Variable } from 'models'
import React from 'react' import React from 'react'
@ -34,9 +34,9 @@ export const ChoiceInputSettingsBody = ({
<FormLabel mb="0" htmlFor="button"> <FormLabel mb="0" htmlFor="button">
Button label: Button label:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="button" id="button"
initialValue={options?.buttonLabel ?? 'Send'} defaultValue={options?.buttonLabel ?? 'Send'}
onChange={handleButtonLabelChange} onChange={handleButtonLabelChange}
/> />
</Stack> </Stack>

View File

@ -1,6 +1,6 @@
import { FormLabel, Stack, Text } from '@chakra-ui/react' import { FormLabel, Stack, Text } from '@chakra-ui/react'
import { CodeEditor } from 'components/shared/CodeEditor' import { CodeEditor } from 'components/shared/CodeEditor'
import { DebouncedInput } from 'components/shared/DebouncedInput' import { Input } from 'components/shared/Textbox'
import { CodeOptions } from 'models' import { CodeOptions } from 'models'
import React from 'react' import React from 'react'
@ -20,10 +20,11 @@ export const CodeSettings = ({ options, onOptionsChange }: Props) => {
<FormLabel mb="0" htmlFor="name"> <FormLabel mb="0" htmlFor="name">
Name: Name:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="name" id="name"
initialValue={options.name} defaultValue={options.name}
onChange={handleNameChange} onChange={handleNameChange}
withVariableButton={false}
/> />
</Stack> </Stack>
<Stack> <Stack>

View File

@ -1,6 +1,6 @@
import { Stack } from '@chakra-ui/react' import { Stack } from '@chakra-ui/react'
import { DropdownList } from 'components/shared/DropdownList' import { DropdownList } from 'components/shared/DropdownList'
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton/InputWithVariableButton' import { Input } from 'components/shared/Textbox/Input'
import { TableListItemProps } from 'components/shared/TableList' import { TableListItemProps } from 'components/shared/TableList'
import { VariableSearchInput } from 'components/shared/VariableSearchInput' import { VariableSearchInput } from 'components/shared/VariableSearchInput'
import { Comparison, Variable, ComparisonOperators } from 'models' import { Comparison, Variable, ComparisonOperators } from 'models'
@ -39,8 +39,8 @@ export const ComparisonItem = ({
placeholder="Select an operator" placeholder="Select an operator"
/> />
{item.comparisonOperator !== ComparisonOperators.IS_SET && ( {item.comparisonOperator !== ComparisonOperators.IS_SET && (
<InputWithVariableButton <Input
initialValue={item.value ?? ''} defaultValue={item.value ?? ''}
onChange={handleChangeValue} onChange={handleChangeValue}
placeholder="Type a value..." placeholder="Type a value..."
/> />

View File

@ -1,6 +1,6 @@
import { FormLabel, Stack } from '@chakra-ui/react' import { FormLabel, Stack } from '@chakra-ui/react'
import { DebouncedInput } from 'components/shared/DebouncedInput'
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel' import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
import { Input } from 'components/shared/Textbox'
import { VariableSearchInput } from 'components/shared/VariableSearchInput' import { VariableSearchInput } from 'components/shared/VariableSearchInput'
import { DateInputOptions, Variable } from 'models' import { DateInputOptions, Variable } from 'models'
import React from 'react' import React from 'react'
@ -46,9 +46,9 @@ export const DateInputSettingsBody = ({
<FormLabel mb="0" htmlFor="from"> <FormLabel mb="0" htmlFor="from">
From label: From label:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="from" id="from"
initialValue={options.labels.from} defaultValue={options.labels.from}
onChange={handleFromChange} onChange={handleFromChange}
/> />
</Stack> </Stack>
@ -58,9 +58,9 @@ export const DateInputSettingsBody = ({
<FormLabel mb="0" htmlFor="to"> <FormLabel mb="0" htmlFor="to">
To label: To label:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="to" id="to"
initialValue={options.labels.to} defaultValue={options.labels.to}
onChange={handleToChange} onChange={handleToChange}
/> />
</Stack> </Stack>
@ -69,9 +69,9 @@ export const DateInputSettingsBody = ({
<FormLabel mb="0" htmlFor="button"> <FormLabel mb="0" htmlFor="button">
Button label: Button label:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="button" id="button"
initialValue={options.labels.button} defaultValue={options.labels.button}
onChange={handleButtonLabelChange} onChange={handleButtonLabelChange}
/> />
</Stack> </Stack>

View File

@ -1,6 +1,5 @@
import { FormLabel, Stack } from '@chakra-ui/react' import { FormLabel, Stack } from '@chakra-ui/react'
import { DebouncedInput } from 'components/shared/DebouncedInput' import { Input } from 'components/shared/Textbox'
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton'
import { VariableSearchInput } from 'components/shared/VariableSearchInput' import { VariableSearchInput } from 'components/shared/VariableSearchInput'
import { EmailInputOptions, Variable } from 'models' import { EmailInputOptions, Variable } from 'models'
import React from 'react' import React from 'react'
@ -29,9 +28,9 @@ export const EmailInputSettingsBody = ({
<FormLabel mb="0" htmlFor="placeholder"> <FormLabel mb="0" htmlFor="placeholder">
Placeholder: Placeholder:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="placeholder" id="placeholder"
initialValue={options.labels.placeholder} defaultValue={options.labels.placeholder}
onChange={handlePlaceholderChange} onChange={handlePlaceholderChange}
/> />
</Stack> </Stack>
@ -39,9 +38,9 @@ export const EmailInputSettingsBody = ({
<FormLabel mb="0" htmlFor="button"> <FormLabel mb="0" htmlFor="button">
Button label: Button label:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="button" id="button"
initialValue={options.labels.button} defaultValue={options.labels.button}
onChange={handleButtonLabelChange} onChange={handleButtonLabelChange}
/> />
</Stack> </Stack>
@ -49,9 +48,9 @@ export const EmailInputSettingsBody = ({
<FormLabel mb="0" htmlFor="retry"> <FormLabel mb="0" htmlFor="retry">
Retry message: Retry message:
</FormLabel> </FormLabel>
<InputWithVariableButton <Input
id="retry" id="retry"
initialValue={options.retryMessageContent} defaultValue={options.retryMessageContent}
onChange={handleRetryMessageChange} onChange={handleRetryMessageChange}
/> />
</Stack> </Stack>

View File

@ -9,8 +9,7 @@ import {
Stack, Stack,
Tag, Tag,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { DebouncedInput } from 'components/shared/DebouncedInput' import { Input } from 'components/shared/Textbox'
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton/InputWithVariableButton'
import { GoogleAnalyticsOptions } from 'models' import { GoogleAnalyticsOptions } from 'models'
import React from 'react' import React from 'react'
@ -47,9 +46,9 @@ export const GoogleAnalyticsSettings = ({
<FormLabel mb="0" htmlFor="tracking-id"> <FormLabel mb="0" htmlFor="tracking-id">
Tracking ID: Tracking ID:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="tracking-id" id="tracking-id"
initialValue={options?.trackingId ?? ''} defaultValue={options?.trackingId ?? ''}
placeholder="G-123456..." placeholder="G-123456..."
onChange={handleTrackingIdChange} onChange={handleTrackingIdChange}
/> />
@ -58,9 +57,9 @@ export const GoogleAnalyticsSettings = ({
<FormLabel mb="0" htmlFor="category"> <FormLabel mb="0" htmlFor="category">
Event category: Event category:
</FormLabel> </FormLabel>
<InputWithVariableButton <Input
id="category" id="category"
initialValue={options?.category ?? ''} defaultValue={options?.category ?? ''}
placeholder="Example: Typebot" placeholder="Example: Typebot"
onChange={handleCategoryChange} onChange={handleCategoryChange}
/> />
@ -69,9 +68,9 @@ export const GoogleAnalyticsSettings = ({
<FormLabel mb="0" htmlFor="action"> <FormLabel mb="0" htmlFor="action">
Event action: Event action:
</FormLabel> </FormLabel>
<InputWithVariableButton <Input
id="action" id="action"
initialValue={options?.action ?? ''} defaultValue={options?.action ?? ''}
placeholder="Example: Submit email" placeholder="Example: Submit email"
onChange={handleActionChange} onChange={handleActionChange}
/> />
@ -91,9 +90,9 @@ export const GoogleAnalyticsSettings = ({
<FormLabel mb="0" htmlFor="label"> <FormLabel mb="0" htmlFor="label">
Event label <Tag>Optional</Tag>: Event label <Tag>Optional</Tag>:
</FormLabel> </FormLabel>
<InputWithVariableButton <Input
id="label" id="label"
initialValue={options?.label ?? ''} defaultValue={options?.label ?? ''}
placeholder="Example: Campaign Z" placeholder="Example: Campaign Z"
onChange={handleLabelChange} onChange={handleLabelChange}
/> />
@ -102,9 +101,9 @@ export const GoogleAnalyticsSettings = ({
<FormLabel mb="0" htmlFor="value"> <FormLabel mb="0" htmlFor="value">
Event value <Tag>Optional</Tag>: Event value <Tag>Optional</Tag>:
</FormLabel> </FormLabel>
<InputWithVariableButton <Input
id="value" id="value"
initialValue={options?.value?.toString() ?? ''} defaultValue={options?.value?.toString() ?? ''}
placeholder="Example: 0" placeholder="Example: 0"
onChange={handleValueChange} onChange={handleValueChange}
/> />

View File

@ -1,6 +1,6 @@
import { Stack } from '@chakra-ui/react' import { Stack } from '@chakra-ui/react'
import { DropdownList } from 'components/shared/DropdownList' import { DropdownList } from 'components/shared/DropdownList'
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton/InputWithVariableButton' import { Input } from 'components/shared/Textbox/Input'
import { TableListItemProps } from 'components/shared/TableList' import { TableListItemProps } from 'components/shared/TableList'
import { Cell } from 'models' import { Cell } from 'models'
@ -25,8 +25,8 @@ export const CellWithValueStack = ({
items={columns} items={columns}
placeholder="Select a column" placeholder="Select a column"
/> />
<InputWithVariableButton <Input
initialValue={item.value ?? ''} defaultValue={item.value ?? ''}
onChange={handleValueChange} onChange={handleValueChange}
placeholder="Type a value..." placeholder="Type a value..."
/> />

View File

@ -1,6 +1,6 @@
import { FormLabel, HStack, Stack } from '@chakra-ui/react' import { FormLabel, HStack, Stack } from '@chakra-ui/react'
import { SmartNumberInput } from 'components/shared/SmartNumberInput' import { SmartNumberInput } from 'components/shared/SmartNumberInput'
import { DebouncedInput } from 'components/shared/DebouncedInput' import { Input } from 'components/shared/Textbox'
import { VariableSearchInput } from 'components/shared/VariableSearchInput' import { VariableSearchInput } from 'components/shared/VariableSearchInput'
import { NumberInputOptions, Variable } from 'models' import { NumberInputOptions, Variable } from 'models'
import React from 'react' import React from 'react'
@ -35,9 +35,9 @@ export const NumberInputSettingsBody = ({
<FormLabel mb="0" htmlFor="placeholder"> <FormLabel mb="0" htmlFor="placeholder">
Placeholder: Placeholder:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="placeholder" id="placeholder"
initialValue={options.labels.placeholder} defaultValue={options.labels.placeholder}
onChange={handlePlaceholderChange} onChange={handlePlaceholderChange}
/> />
</Stack> </Stack>
@ -45,9 +45,9 @@ export const NumberInputSettingsBody = ({
<FormLabel mb="0" htmlFor="button"> <FormLabel mb="0" htmlFor="button">
Button label: Button label:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="button" id="button"
initialValue={options?.labels?.button ?? 'Send'} defaultValue={options?.labels?.button ?? 'Send'}
onChange={handleButtonLabelChange} onChange={handleButtonLabelChange}
/> />
</Stack> </Stack>

View File

@ -1,6 +1,5 @@
import { FormLabel, Stack } from '@chakra-ui/react' import { FormLabel, Stack } from '@chakra-ui/react'
import { DebouncedInput } from 'components/shared/DebouncedInput' import { Input } from 'components/shared/Textbox'
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton'
import { VariableSearchInput } from 'components/shared/VariableSearchInput' import { VariableSearchInput } from 'components/shared/VariableSearchInput'
import { PhoneNumberInputOptions, Variable } from 'models' import { PhoneNumberInputOptions, Variable } from 'models'
import React from 'react' import React from 'react'
@ -32,9 +31,9 @@ export const PhoneNumberSettingsBody = ({
<FormLabel mb="0" htmlFor="placeholder"> <FormLabel mb="0" htmlFor="placeholder">
Placeholder: Placeholder:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="placeholder" id="placeholder"
initialValue={options.labels.placeholder} defaultValue={options.labels.placeholder}
onChange={handlePlaceholderChange} onChange={handlePlaceholderChange}
/> />
</Stack> </Stack>
@ -42,9 +41,9 @@ export const PhoneNumberSettingsBody = ({
<FormLabel mb="0" htmlFor="button"> <FormLabel mb="0" htmlFor="button">
Button label: Button label:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="button" id="button"
initialValue={options.labels.button} defaultValue={options.labels.button}
onChange={handleButtonLabelChange} onChange={handleButtonLabelChange}
/> />
</Stack> </Stack>
@ -61,9 +60,9 @@ export const PhoneNumberSettingsBody = ({
<FormLabel mb="0" htmlFor="retry"> <FormLabel mb="0" htmlFor="retry">
Retry message: Retry message:
</FormLabel> </FormLabel>
<InputWithVariableButton <Input
id="retry" id="retry"
initialValue={options.retryMessageContent} defaultValue={options.retryMessageContent}
onChange={handleRetryMessageChange} onChange={handleRetryMessageChange}
/> />
</Stack> </Stack>

View File

@ -1,6 +1,6 @@
import { FormLabel, Stack } from '@chakra-ui/react' import { FormLabel, Stack } from '@chakra-ui/react'
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel' import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton' import { Input } from 'components/shared/Textbox'
import { RedirectOptions } from 'models' import { RedirectOptions } from 'models'
import React from 'react' import React from 'react'
@ -21,9 +21,9 @@ export const RedirectSettings = ({ options, onOptionsChange }: Props) => {
<FormLabel mb="0" htmlFor="tracking-id"> <FormLabel mb="0" htmlFor="tracking-id">
Url: Url:
</FormLabel> </FormLabel>
<InputWithVariableButton <Input
id="tracking-id" id="tracking-id"
initialValue={options.url ?? ''} defaultValue={options.url ?? ''}
placeholder="Type a URL..." placeholder="Type a URL..."
onChange={handleUrlChange} onChange={handleUrlChange}
/> />

View File

@ -1,9 +1,6 @@
import { Stack, useDisclosure, Text } from '@chakra-ui/react' import { Stack, useDisclosure, Text } from '@chakra-ui/react'
import { CredentialsDropdown } from 'components/shared/CredentialsDropdown' import { CredentialsDropdown } from 'components/shared/CredentialsDropdown'
import { import { Input, Textarea } from 'components/shared/Textbox'
InputWithVariableButton,
TextareaWithVariableButton,
} from 'components/shared/TextboxWithVariableButton'
import { CredentialsType, SendEmailOptions } from 'models' import { CredentialsType, SendEmailOptions } from 'models'
import React, { useState } from 'react' import React, { useState } from 'react'
import { SmtpConfigModal } from './SmtpConfigModal' import { SmtpConfigModal } from './SmtpConfigModal'
@ -80,43 +77,43 @@ export const SendEmailSettings = ({ options, onOptionsChange }: Props) => {
</Stack> </Stack>
<Stack> <Stack>
<Text>To: </Text> <Text>To: </Text>
<InputWithVariableButton <Input
onChange={handleToChange} onChange={handleToChange}
initialValue={options.recipients.join(', ')} defaultValue={options.recipients.join(', ')}
placeholder="email1@gmail.com, email2@gmail.com" placeholder="email1@gmail.com, email2@gmail.com"
/> />
</Stack> </Stack>
<Stack> <Stack>
<Text>Cc: </Text> <Text>Cc: </Text>
<InputWithVariableButton <Input
onChange={handleCcChange} onChange={handleCcChange}
initialValue={options.cc?.join(', ') ?? ''} defaultValue={options.cc?.join(', ') ?? ''}
placeholder="email1@gmail.com, email2@gmail.com" placeholder="email1@gmail.com, email2@gmail.com"
/> />
</Stack> </Stack>
<Stack> <Stack>
<Text>Bcc: </Text> <Text>Bcc: </Text>
<InputWithVariableButton <Input
onChange={handleBccChange} onChange={handleBccChange}
initialValue={options.bcc?.join(', ') ?? ''} defaultValue={options.bcc?.join(', ') ?? ''}
placeholder="email1@gmail.com, email2@gmail.com" placeholder="email1@gmail.com, email2@gmail.com"
/> />
</Stack> </Stack>
<Stack> <Stack>
<Text>Subject: </Text> <Text>Subject: </Text>
<InputWithVariableButton <Input
data-testid="subject-input" data-testid="subject-input"
onChange={handleSubjectChange} onChange={handleSubjectChange}
initialValue={options.subject ?? ''} defaultValue={options.subject ?? ''}
/> />
</Stack> </Stack>
<Stack> <Stack>
<Text>Body: </Text> <Text>Body: </Text>
<TextareaWithVariableButton <Textarea
data-testid="body-input" data-testid="body-input"
minH="300px" minH="300px"
onChange={handleBodyChange} onChange={handleBodyChange}
initialValue={options.body ?? ''} defaultValue={options.body ?? ''}
/> />
</Stack> </Stack>
<SmtpConfigModal <SmtpConfigModal

View File

@ -1,8 +1,8 @@
import { FormControl, FormLabel, HStack, Stack } from '@chakra-ui/react' import { FormControl, FormLabel, HStack, Stack } from '@chakra-ui/react'
import { isDefined } from '@udecode/plate-common' import { isDefined } from '@udecode/plate-common'
import { DebouncedInput } from 'components/shared/DebouncedInput'
import { SmartNumberInput } from 'components/shared/SmartNumberInput' import { SmartNumberInput } from 'components/shared/SmartNumberInput'
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel' import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
import { Input } from 'components/shared/Textbox'
import { SmtpCredentialsData } from 'models' import { SmtpCredentialsData } from 'models'
import React from 'react' import React from 'react'
@ -30,43 +30,48 @@ export const SmtpConfigForm = ({ config, onConfigChange }: Props) => {
<Stack as="form" spacing={4}> <Stack as="form" spacing={4}>
<FormControl isRequired> <FormControl isRequired>
<FormLabel>From email:</FormLabel> <FormLabel>From email:</FormLabel>
<DebouncedInput <Input
initialValue={config.from.email ?? ''} defaultValue={config.from.email ?? ''}
onChange={handleFromEmailChange} onChange={handleFromEmailChange}
placeholder="notifications@provider.com" placeholder="notifications@provider.com"
withVariableButton={false}
/> />
</FormControl> </FormControl>
<FormControl isRequired> <FormControl isRequired>
<FormLabel>From name:</FormLabel> <FormLabel>From name:</FormLabel>
<DebouncedInput <Input
initialValue={config.from.name ?? ''} defaultValue={config.from.name ?? ''}
onChange={handleFromNameChange} onChange={handleFromNameChange}
placeholder="John Smith" placeholder="John Smith"
withVariableButton={false}
/> />
</FormControl> </FormControl>
<FormControl isRequired> <FormControl isRequired>
<FormLabel>Host:</FormLabel> <FormLabel>Host:</FormLabel>
<DebouncedInput <Input
initialValue={config.host ?? ''} defaultValue={config.host ?? ''}
onChange={handleHostChange} onChange={handleHostChange}
placeholder="mail.provider.com" placeholder="mail.provider.com"
withVariableButton={false}
/> />
</FormControl> </FormControl>
<FormControl isRequired> <FormControl isRequired>
<FormLabel>Username / Email:</FormLabel> <FormLabel>Username / Email:</FormLabel>
<DebouncedInput <Input
type="email" type="email"
initialValue={config.username ?? ''} defaultValue={config.username ?? ''}
onChange={handleUsernameChange} onChange={handleUsernameChange}
placeholder="user@provider.com" placeholder="user@provider.com"
withVariableButton={false}
/> />
</FormControl> </FormControl>
<FormControl isRequired> <FormControl isRequired>
<FormLabel>Password:</FormLabel> <FormLabel>Password:</FormLabel>
<DebouncedInput <Input
type="password" type="password"
initialValue={config.password ?? ''} defaultValue={config.password ?? ''}
onChange={handlePasswordChange} onChange={handlePasswordChange}
withVariableButton={false}
/> />
</FormControl> </FormControl>
<SwitchWithLabel <SwitchWithLabel

View File

@ -1,5 +1,5 @@
import { FormLabel, Stack } from '@chakra-ui/react' import { FormLabel, Stack } from '@chakra-ui/react'
import { DebouncedTextarea } from 'components/shared/DebouncedTextarea' import { Textarea } from 'components/shared/Textbox'
import { VariableSearchInput } from 'components/shared/VariableSearchInput' import { VariableSearchInput } from 'components/shared/VariableSearchInput'
import { SetVariableOptions, Variable } from 'models' import { SetVariableOptions, Variable } from 'models'
import React from 'react' import React from 'react'
@ -31,9 +31,9 @@ export const SetVariableSettings = ({ options, onOptionsChange }: Props) => {
<FormLabel mb="0" htmlFor="expression"> <FormLabel mb="0" htmlFor="expression">
Value / Expression: Value / Expression:
</FormLabel> </FormLabel>
<DebouncedTextarea <Textarea
id="expression" id="expression"
initialValue={options.expressionToEvaluate ?? ''} defaultValue={options.expressionToEvaluate ?? ''}
onChange={handleExpressionChange} onChange={handleExpressionChange}
/> />
</Stack> </Stack>

View File

@ -1,6 +1,6 @@
import { FormLabel, Stack } from '@chakra-ui/react' import { FormLabel, Stack } from '@chakra-ui/react'
import { DebouncedInput } from 'components/shared/DebouncedInput'
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel' import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
import { Input } from 'components/shared/Textbox'
import { VariableSearchInput } from 'components/shared/VariableSearchInput' import { VariableSearchInput } from 'components/shared/VariableSearchInput'
import { TextInputOptions, Variable } from 'models' import { TextInputOptions, Variable } from 'models'
import React from 'react' import React from 'react'
@ -35,9 +35,9 @@ export const TextInputSettingsBody = ({
<FormLabel mb="0" htmlFor="placeholder"> <FormLabel mb="0" htmlFor="placeholder">
Placeholder: Placeholder:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="placeholder" id="placeholder"
initialValue={options.labels.placeholder} defaultValue={options.labels.placeholder}
onChange={handlePlaceholderChange} onChange={handlePlaceholderChange}
/> />
</Stack> </Stack>
@ -45,9 +45,9 @@ export const TextInputSettingsBody = ({
<FormLabel mb="0" htmlFor="button"> <FormLabel mb="0" htmlFor="button">
Button label: Button label:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="button" id="button"
initialValue={options.labels.button} defaultValue={options.labels.button}
onChange={handleButtonLabelChange} onChange={handleButtonLabelChange}
/> />
</Stack> </Stack>

View File

@ -1,6 +1,5 @@
import { FormLabel, Stack } from '@chakra-ui/react' import { FormLabel, Stack } from '@chakra-ui/react'
import { DebouncedInput } from 'components/shared/DebouncedInput' import { Input } from 'components/shared/Textbox'
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton'
import { VariableSearchInput } from 'components/shared/VariableSearchInput' import { VariableSearchInput } from 'components/shared/VariableSearchInput'
import { UrlInputOptions, Variable } from 'models' import { UrlInputOptions, Variable } from 'models'
import React from 'react' import React from 'react'
@ -29,9 +28,9 @@ export const UrlInputSettingsBody = ({
<FormLabel mb="0" htmlFor="placeholder"> <FormLabel mb="0" htmlFor="placeholder">
Placeholder: Placeholder:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="placeholder" id="placeholder"
initialValue={options.labels.placeholder} defaultValue={options.labels.placeholder}
onChange={handlePlaceholderChange} onChange={handlePlaceholderChange}
/> />
</Stack> </Stack>
@ -39,9 +38,9 @@ export const UrlInputSettingsBody = ({
<FormLabel mb="0" htmlFor="button"> <FormLabel mb="0" htmlFor="button">
Button label: Button label:
</FormLabel> </FormLabel>
<DebouncedInput <Input
id="button" id="button"
initialValue={options.labels.button} defaultValue={options.labels.button}
onChange={handleButtonLabelChange} onChange={handleButtonLabelChange}
/> />
</Stack> </Stack>
@ -49,9 +48,9 @@ export const UrlInputSettingsBody = ({
<FormLabel mb="0" htmlFor="retry"> <FormLabel mb="0" htmlFor="retry">
Retry message: Retry message:
</FormLabel> </FormLabel>
<InputWithVariableButton <Input
id="retry" id="retry"
initialValue={options.retryMessageContent} defaultValue={options.retryMessageContent}
onChange={handleRetryMessageChange} onChange={handleRetryMessageChange}
/> />
</Stack> </Stack>

View File

@ -1,5 +1,5 @@
import { Stack, FormControl, FormLabel } from '@chakra-ui/react' import { Stack, FormControl, FormLabel } from '@chakra-ui/react'
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton/InputWithVariableButton' import { Input } from 'components/shared/Textbox'
import { TableListItemProps } from 'components/shared/TableList' import { TableListItemProps } from 'components/shared/TableList'
import { KeyValue } from 'models' import { KeyValue } from 'models'
@ -40,18 +40,18 @@ export const KeyValueInputs = ({
<Stack p="4" rounded="md" flex="1" borderWidth="1px"> <Stack p="4" rounded="md" flex="1" borderWidth="1px">
<FormControl> <FormControl>
<FormLabel htmlFor={'key' + item.id}>Key:</FormLabel> <FormLabel htmlFor={'key' + item.id}>Key:</FormLabel>
<InputWithVariableButton <Input
id={'key' + item.id} id={'key' + item.id}
initialValue={item.key ?? ''} defaultValue={item.key ?? ''}
onChange={handleKeyChange} onChange={handleKeyChange}
placeholder={keyPlaceholder} placeholder={keyPlaceholder}
/> />
</FormControl> </FormControl>
<FormControl> <FormControl>
<FormLabel htmlFor={'value' + item.id}>Value:</FormLabel> <FormLabel htmlFor={'value' + item.id}>Value:</FormLabel>
<InputWithVariableButton <Input
id={'value' + item.id} id={'value' + item.id}
initialValue={item.value ?? ''} defaultValue={item.value ?? ''}
onChange={handleValueChange} onChange={handleValueChange}
placeholder={valuePlaceholder} placeholder={valuePlaceholder}
/> />

View File

@ -1,6 +1,6 @@
import { Stack, FormControl, FormLabel } from '@chakra-ui/react' import { Stack, FormControl, FormLabel } from '@chakra-ui/react'
import { DebouncedInput } from 'components/shared/DebouncedInput'
import { TableListItemProps } from 'components/shared/TableList' import { TableListItemProps } from 'components/shared/TableList'
import { Input } from 'components/shared/Textbox'
import { VariableSearchInput } from 'components/shared/VariableSearchInput' import { VariableSearchInput } from 'components/shared/VariableSearchInput'
import { VariableForTest, Variable } from 'models' import { VariableForTest, Variable } from 'models'
@ -26,9 +26,9 @@ export const VariableForTestInputs = ({
</FormControl> </FormControl>
<FormControl> <FormControl>
<FormLabel htmlFor={'value' + item.id}>Test value:</FormLabel> <FormLabel htmlFor={'value' + item.id}>Test value:</FormLabel>
<DebouncedInput <Input
id={'value' + item.id} id={'value' + item.id}
initialValue={item.value ?? ''} defaultValue={item.value ?? ''}
onChange={handleValueChange} onChange={handleValueChange}
/> />
</FormControl> </FormControl>

View File

@ -12,7 +12,7 @@ import {
useToast, useToast,
Text, Text,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton/InputWithVariableButton' import { Input } from 'components/shared/Textbox'
import { useTypebot } from 'contexts/TypebotContext' import { useTypebot } from 'contexts/TypebotContext'
import { import {
HttpMethod, HttpMethod,
@ -144,9 +144,9 @@ export const WebhookSettings = ({
if (!localWebhook) return <Spinner /> if (!localWebhook) return <Spinner />
return ( return (
<Stack spacing={4}> <Stack spacing={4}>
<InputWithVariableButton <Input
placeholder="Your Webhook URL..." placeholder="Your Webhook URL..."
initialValue={localWebhook.url ?? ''} defaultValue={localWebhook.url ?? ''}
onChange={handleUrlChange} onChange={handleUrlChange}
/> />
<SwitchWithLabel <SwitchWithLabel

View File

@ -5,7 +5,7 @@ import { UploadButton } from '../buttons/UploadButton'
import { GiphySearch } from './GiphySearch' import { GiphySearch } from './GiphySearch'
import { useTypebot } from 'contexts/TypebotContext' import { useTypebot } from 'contexts/TypebotContext'
import { useDebounce } from 'use-debounce' import { useDebounce } from 'use-debounce'
import { InputWithVariableButton } from '../TextboxWithVariableButton' import { Input } from '../Textbox'
type Props = { type Props = {
url?: string url?: string
@ -105,10 +105,10 @@ const EmbedLinkContent = ({ initialUrl, onNewUrl }: ContentProps) => {
return ( return (
<Stack py="2"> <Stack py="2">
<InputWithVariableButton <Input
placeholder={'Paste the image link...'} placeholder={'Paste the image link...'}
onChange={setImageUrl} onChange={setImageUrl}
initialValue={imageUrl} defaultValue={imageUrl}
/> />
</Stack> </Stack>
) )

View File

@ -0,0 +1,7 @@
import { Input as ChakraInput } from '@chakra-ui/react'
import React from 'react'
import { TextBox, TextBoxProps } from './TextBox'
export const Input = (props: Omit<TextBoxProps, 'TextBox'>) => (
<TextBox TextBox={ChakraInput} {...props} />
)

View File

@ -12,38 +12,49 @@ import {
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { UserIcon } from 'assets/icons' import { UserIcon } from 'assets/icons'
import { Variable } from 'models' import { Variable } from 'models'
import React, { useEffect, useRef, useState } from 'react' import React, { ChangeEvent, useEffect, useRef, useState } from 'react'
import { useDebounce } from 'use-debounce' import { useDebouncedCallback } from 'use-debounce'
import { VariableSearchInput } from '../VariableSearchInput' import { VariableSearchInput } from '../VariableSearchInput'
export type TextBoxWithVariableButtonProps = { export type TextBoxProps = {
initialValue: string
onChange: (value: string) => void onChange: (value: string) => void
TextBox: TextBox:
| ComponentWithAs<'textarea', TextareaProps> | ComponentWithAs<'textarea', TextareaProps>
| ComponentWithAs<'input', InputProps> | ComponentWithAs<'input', InputProps>
withVariableButton?: boolean
} & Omit<InputProps & TextareaProps, 'onChange'> } & Omit<InputProps & TextareaProps, 'onChange'>
export const TextBoxWithVariableButton = ({ export const TextBox = ({
initialValue,
onChange, onChange,
TextBox, TextBox,
withVariableButton = true,
...props ...props
}: TextBoxWithVariableButtonProps) => { }: TextBoxProps) => {
const textBoxRef = useRef<(HTMLInputElement & HTMLTextAreaElement) | null>( const textBoxRef = useRef<(HTMLInputElement & HTMLTextAreaElement) | null>(
null null
) )
const [value, setValue] = useState(initialValue) const [carretPosition, setCarretPosition] = useState<number>(0)
const [debouncedValue] = useDebounce( const [value, setValue] = useState(props.defaultValue)
value, const debounced = useDebouncedCallback(
(value) => {
onChange(value)
},
process.env.NEXT_PUBLIC_E2E_TEST ? 0 : 1000 process.env.NEXT_PUBLIC_E2E_TEST ? 0 : 1000
) )
const [carretPosition, setCarretPosition] = useState<number>(0)
useEffect(() => { useEffect(
onChange(value) () => () => {
// eslint-disable-next-line react-hooks/exhaustive-deps debounced.flush()
}, [debouncedValue]) },
[debounced]
)
const handleChange = (
e: ChangeEvent<HTMLInputElement & HTMLTextAreaElement>
) => {
setValue(e.target.value)
debounced(e.target.value)
}
const handleVariableSelected = (variable?: Variable) => { const handleVariableSelected = (variable?: Variable) => {
if (!textBoxRef.current || !variable) return if (!textBoxRef.current || !variable) return
@ -56,11 +67,12 @@ export const TextBoxWithVariableButton = ({
cursorPosition, cursorPosition,
textBoxRef.current.value.length textBoxRef.current.value.length
) )
setValue( const newValue =
textBeforeCursorPosition + textBeforeCursorPosition +
`{{${variable.name}}}` + `{{${variable.name}}}` +
textAfterCursorPosition textAfterCursorPosition
) setValue(newValue)
debounced(newValue)
textBoxRef.current.focus() textBoxRef.current.focus()
setTimeout(() => { setTimeout(() => {
if (!textBoxRef.current) return if (!textBoxRef.current) return
@ -75,17 +87,24 @@ export const TextBoxWithVariableButton = ({
setCarretPosition(textBoxRef.current.selectionStart) setCarretPosition(textBoxRef.current.selectionStart)
} }
const handleChange = ( if (!withVariableButton) {
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> return (
) => setValue(e.target.value) <TextBox
ref={textBoxRef}
onChange={handleChange}
bgColor={'white'}
value={value}
{...props}
/>
)
}
return ( return (
<HStack spacing={0} align={'flex-end'}> <HStack spacing={0} align={'flex-end'}>
<TextBox <TextBox
ref={textBoxRef} ref={textBoxRef}
value={value}
onKeyUp={handleKeyUp} onKeyUp={handleKeyUp}
onClick={handleKeyUp} onClick={handleKeyUp}
value={value}
onChange={handleChange} onChange={handleChange}
bgColor={'white'} bgColor={'white'}
{...props} {...props}

View File

@ -0,0 +1,7 @@
import { Textarea as ChakraTextarea } from '@chakra-ui/react'
import React from 'react'
import { TextBox, TextBoxProps } from './TextBox'
export const Textarea = (props: Omit<TextBoxProps, 'TextBox'>) => (
<TextBox TextBox={ChakraTextarea} {...props} />
)

View File

@ -0,0 +1,2 @@
export { Input } from './Input'
export { Textarea } from './Textarea'

View File

@ -1,10 +0,0 @@
import { Input } from '@chakra-ui/react'
import React from 'react'
import {
TextBoxWithVariableButton,
TextBoxWithVariableButtonProps,
} from './TextboxWithVariableButton'
export const InputWithVariableButton = (
props: Omit<TextBoxWithVariableButtonProps, 'TextBox'>
) => <TextBoxWithVariableButton TextBox={Input} {...props} />

View File

@ -1,10 +0,0 @@
import { Textarea } from '@chakra-ui/react'
import React from 'react'
import {
TextBoxWithVariableButton,
TextBoxWithVariableButtonProps,
} from './TextboxWithVariableButton'
export const TextareaWithVariableButton = (
props: Omit<TextBoxWithVariableButtonProps, 'TextBox'>
) => <TextBoxWithVariableButton TextBox={Textarea} {...props} />

View File

@ -1,2 +0,0 @@
export { InputWithVariableButton } from './InputWithVariableButton'
export { TextareaWithVariableButton } from './TextareaWithVariableButton'

View File

@ -1,2 +1,3 @@
export * from './utils' export * from './utils'
export * from './useUndo' export * from './useUndo'
export * from './useRefState'

View File

@ -0,0 +1,10 @@
import { useEffect, useRef, useState } from 'react'
export const useRefState = (initialValue: string) => {
const [state, setState] = useState(initialValue)
const stateRef = useRef<string>(state)
useEffect(() => {
stateRef.current = state
}, [state])
return [stateRef, setState]
}