Skip to content

Commit

Permalink
feat(inputs): ✨ Add text options
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Jan 6, 2022
1 parent eea522f commit f712c7a
Show file tree
Hide file tree
Showing 16 changed files with 356 additions and 75 deletions.
2 changes: 1 addition & 1 deletion apps/builder/components/board/Board.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export const Board = () => {
<StepTypesList />
<GraphProvider>
<Graph flex="1" />
{rightPanel === RightPanel.PREVIEW && <PreviewDrawer />}
</GraphProvider>
{rightPanel === RightPanel.PREVIEW && <PreviewDrawer />}
</DndContext>
</Flex>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { PopoverContent, PopoverArrow, PopoverBody } from '@chakra-ui/react'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import { Step, StepType, TextInputOptions } from 'models'
import { TextInputSettingsBody } from './TextInputSettingsBody'

type Props = {
step: Step
}
export const SettingsPopoverContent = ({ step }: Props) => {
const handleMouseDown = (e: React.MouseEvent) => e.stopPropagation()

return (
<PopoverContent onMouseDown={handleMouseDown}>
<PopoverArrow />
<PopoverBody p="6">
<SettingsPopoverBodyContent step={step} />
</PopoverBody>
</PopoverContent>
)
}

const SettingsPopoverBodyContent = ({ step }: Props) => {
const { updateStep } = useTypebot()
const handleOptionsChange = (options: TextInputOptions) =>
updateStep(step.id, { options } as Partial<Step>)

switch (step.type) {
case StepType.TEXT_INPUT: {
return (
<TextInputSettingsBody
options={step.options}
onOptionsChange={handleOptionsChange}
/>
)
}
default: {
return <></>
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { FormLabel, Stack } from '@chakra-ui/react'
import { DebouncedInput } from 'components/shared/DebouncedInput'
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
import { TextInputOptions } from 'models'
import React from 'react'

type TextInputSettingsBodyProps = {
options?: TextInputOptions
onOptionsChange: (options: TextInputOptions) => void
}

export const TextInputSettingsBody = ({
options,
onOptionsChange,
}: TextInputSettingsBodyProps) => {
const handlePlaceholderChange = (placeholder: string) =>
onOptionsChange({ ...options, labels: { ...options?.labels, placeholder } })
const handleButtonLabelChange = (button: string) =>
onOptionsChange({ ...options, labels: { ...options?.labels, button } })
const handleLongChange = (isLong: boolean) =>
onOptionsChange({ ...options, isLong })

return (
<Stack spacing={4}>
<SwitchWithLabel
id="switch"
label="Long text?"
initialValue={options?.isLong ?? false}
onCheckChange={handleLongChange}
/>
<Stack>
<FormLabel mb="0" htmlFor="placeholder">
Placeholder:
</FormLabel>
<DebouncedInput
id="placeholder"
initialValue={options?.labels?.placeholder ?? 'Type your answer...'}
delay={100}
onChange={handlePlaceholderChange}
/>
</Stack>
<Stack>
<FormLabel mb="0" htmlFor="button">
Button label:
</FormLabel>
<DebouncedInput
id="button"
initialValue={options?.labels?.button ?? 'Send'}
delay={100}
onChange={handleButtonLabelChange}
/>
</Stack>
</Stack>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SettingsPopoverContent } from './SettingsPopoverContent'
118 changes: 66 additions & 52 deletions apps/builder/components/board/graph/BlockNode/StepNode/StepNode.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Box, Flex, HStack, useEventListener } from '@chakra-ui/react'
import {
Box,
Flex,
HStack,
Popover,
PopoverTrigger,
useEventListener,
} from '@chakra-ui/react'
import React, { useEffect, useMemo, useState } from 'react'
import { Block, Step, StepType } from 'models'
import { SourceEndpoint } from './SourceEndpoint'
Expand All @@ -11,6 +18,7 @@ import { StepContent } from './StepContent'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import { ContextMenu } from 'components/shared/ContextMenu'
import { StepNodeContextMenu } from './RightClickMenu'
import { SettingsPopoverContent } from './SettingsPopoverContent'

export const StepNode = ({
step,
Expand Down Expand Up @@ -144,58 +152,64 @@ export const StepNode = ({
renderMenu={() => <StepNodeContextMenu stepId={step.id} />}
>
{(ref, isOpened) => (
<Flex
pos="relative"
ref={ref}
onMouseMove={handleMouseMove}
onMouseDown={handleMouseDown}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onMouseUp={handleMouseUp}
>
{connectedStubPosition === 'left' && (
<Box
h="2px"
pos="absolute"
left="-18px"
top="25px"
w="18px"
bgColor="blue.500"
/>
)}
<HStack
flex="1"
userSelect="none"
p="3"
borderWidth="2px"
borderColor={isConnecting || isOpened ? 'blue.400' : 'gray.400'}
rounded="lg"
cursor={'pointer'}
bgColor="white"
>
<StepIcon type={step.type} />
<StepContent {...step} />
{isConnectable && (
<SourceEndpoint
onConnectionDragStart={handleConnectionDragStart}
pos="absolute"
right="20px"
/>
)}
</HStack>
<Popover placement="left" isLazy>
<PopoverTrigger>
<Flex
pos="relative"
ref={ref}
onMouseMove={handleMouseMove}
onMouseDown={handleMouseDown}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onMouseUp={handleMouseUp}
data-testid={`step-${step.id}`}
>
{connectedStubPosition === 'left' && (
<Box
h="2px"
pos="absolute"
left="-18px"
top="25px"
w="18px"
bgColor="blue.500"
/>
)}
<HStack
flex="1"
userSelect="none"
p="3"
borderWidth="2px"
borderColor={isConnecting || isOpened ? 'blue.400' : 'gray.400'}
rounded="lg"
cursor={'pointer'}
bgColor="white"
>
<StepIcon type={step.type} />
<StepContent {...step} />
{isConnectable && (
<SourceEndpoint
onConnectionDragStart={handleConnectionDragStart}
pos="absolute"
right="20px"
/>
)}
</HStack>

{isDefined(connectedStubPosition) && (
<Box
h="2px"
pos="absolute"
right={connectedStubPosition === 'left' ? undefined : '-18px'}
left={connectedStubPosition === 'left' ? '-18px' : undefined}
top="25px"
w="18px"
bgColor="gray.500"
/>
)}
</Flex>
{isDefined(connectedStubPosition) && (
<Box
h="2px"
pos="absolute"
right={connectedStubPosition === 'left' ? undefined : '-18px'}
left={connectedStubPosition === 'left' ? '-18px' : undefined}
top="25px"
w="18px"
bgColor="gray.500"
/>
)}
</Flex>
</PopoverTrigger>
<SettingsPopoverContent step={step} />
</Popover>
)}
</ContextMenu>
)
Expand Down
10 changes: 7 additions & 3 deletions apps/builder/components/board/preview/PreviewDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ export const PreviewDrawer = () => {
const { setRightPanel } = useEditor()
const { previewingIds, setPreviewingIds } = useGraph()
const [isResizing, setIsResizing] = useState(false)
const [width, setWidth] = useState(400)
const [width, setWidth] = useState(500)
const [isResizeHandleVisible, setIsResizeHandleVisible] = useState(false)
const [restartKey, setRestartKey] = useState(0)

const publicTypebot = useMemo(
() => (typebot ? parseTypebotToPublicTypebot(typebot) : undefined),
Expand All @@ -47,11 +48,13 @@ export const PreviewDrawer = () => {
const handleNewBlockVisible = (targetId: string) =>
setPreviewingIds({
sourceId: !previewingIds.sourceId
? 'start-block'
? typebot?.blocks.allIds[0]
: previewingIds.targetId,
targetId: targetId,
})

const handleRestartClick = () => setRestartKey((key) => key + 1)

return (
<Flex
pos="absolute"
Expand All @@ -77,7 +80,7 @@ export const PreviewDrawer = () => {

<VStack w="full" spacing={4}>
<Flex justifyContent={'space-between'} w="full">
<Button>Restart</Button>
<Button onClick={handleRestartClick}>Restart</Button>
<CloseButton onClick={() => setRightPanel(undefined)} />
</Flex>

Expand All @@ -87,6 +90,7 @@ export const PreviewDrawer = () => {
borderRadius={'lg'}
h="full"
w="full"
key={restartKey}
pointerEvents={isResizing ? 'none' : 'auto'}
>
<TypebotViewer
Expand Down
31 changes: 31 additions & 0 deletions apps/builder/components/shared/DebouncedInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Input, InputProps } from '@chakra-ui/react'
import { ChangeEvent, useEffect, useState } from 'react'
import { useDebounce } from 'use-debounce'

type Props = Omit<InputProps, 'onChange' | 'value'> & {
delay: number
initialValue: string
onChange: (debouncedValue: string) => void
}

export const DebouncedInput = ({
delay,
onChange,
initialValue,
...props
}: Props) => {
const [currentValue, setCurrentValue] = useState(initialValue)
const [currentValueDebounced] = useDebounce(currentValue, delay)

useEffect(() => {
if (currentValueDebounced === initialValue) return
onChange(currentValueDebounced)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentValueDebounced])

const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setCurrentValue(e.target.value)
}

return <Input {...props} value={currentValue} onChange={handleChange} />
}
30 changes: 30 additions & 0 deletions apps/builder/components/shared/SwitchWithLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { FormLabel, HStack, Switch, SwitchProps } from '@chakra-ui/react'
import React, { useState } from 'react'

type SwitchWithLabelProps = {
label: string
initialValue: boolean
onCheckChange: (isChecked: boolean) => void
} & SwitchProps

export const SwitchWithLabel = ({
label,
initialValue,
onCheckChange,
...props
}: SwitchWithLabelProps) => {
const [isChecked, setIsChecked] = useState(initialValue)

const handleChange = () => {
setIsChecked(!isChecked)
onCheckChange(!isChecked)
}
return (
<HStack justifyContent="space-between">
<FormLabel htmlFor={props.id} mb="0">
{label}
</FormLabel>
<Switch isChecked={isChecked} onChange={handleChange} {...props} />
</HStack>
)
}
3 changes: 3 additions & 0 deletions apps/builder/cypress/plugins/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export const seedDb = async () => {
return createAnswers()
}

export const createTypebot = (typebot: Typebot) =>
prisma.typebot.create({ data: typebot as any })

const createUsers = () =>
prisma.user.createMany({
data: [
Expand Down
3 changes: 2 additions & 1 deletion apps/builder/cypress/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
FacebookSocialLogin,
GoogleSocialLogin,
} from 'cypress-social-logins/src/Plugins'
import { seedDb } from './database'
import { createTypebot, seedDb } from './database'
/// <reference types="cypress" />

/**
Expand All @@ -16,6 +16,7 @@ const handler = (on: any) => {
FacebookSocialLogin: FacebookSocialLogin,
GitHubSocialLogin: GitHubSocialLogin,
seed: seedDb,
createTypebot,
})
}

Expand Down
Loading

2 comments on commit f712c7a

@vercel
Copy link

@vercel vercel bot commented on f712c7a Jan 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2 – ./apps/viewer

viewer-v2-git-main-typebot-io.vercel.app
typebot-io.vercel.app
viewer-v2-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on f712c7a Jan 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

builder-v2-typebot-io.vercel.app
builder-v2-git-main-typebot-io.vercel.app
next.typebot.io

Please sign in to comment.