From c1a32ce26bb0088e8af02eea544158e1bdb446da Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Wed, 4 Jan 2023 15:35:11 +0100 Subject: [PATCH] :bug: (editor) Fix variable dropdown overflow Closes #209 --- .../src/components/SearchableDropdown.tsx | 2 +- .../src/components/VariableSearchInput.tsx | 144 +++++++++--------- .../TextBubbleEditor/TextBubbleEditor.tsx | 2 +- .../WebhookSettings/WebhookSettings.tsx | 10 +- .../components/Nodes/GroupNode/GroupNode.tsx | 5 - apps/builder/src/features/graph/utils.ts | 2 +- .../embeds/modals/WordpressModal.tsx | 3 +- .../templates/components/TemplatesModal.tsx | 5 +- apps/builder/src/hooks/useOutsideClick.ts | 26 ++++ packages/scripts/fixTypebots.ts | 20 ++- 10 files changed, 125 insertions(+), 94 deletions(-) create mode 100644 apps/builder/src/hooks/useOutsideClick.ts diff --git a/apps/builder/src/components/SearchableDropdown.tsx b/apps/builder/src/components/SearchableDropdown.tsx index 922f504e6..056066172 100644 --- a/apps/builder/src/components/SearchableDropdown.tsx +++ b/apps/builder/src/components/SearchableDropdown.tsx @@ -1,6 +1,5 @@ import { useDisclosure, - useOutsideClick, Flex, Popover, Input, @@ -16,6 +15,7 @@ import { useState, useRef, useEffect, ChangeEvent, ReactNode } from 'react' import { useDebouncedCallback } from 'use-debounce' import { env, isDefined } from 'utils' import { VariablesButton } from '@/features/variables' +import { useOutsideClick } from '@/hooks/useOutsideClick' type Props = { selectedItem?: string diff --git a/apps/builder/src/components/VariableSearchInput.tsx b/apps/builder/src/components/VariableSearchInput.tsx index bac173929..3f6897a1c 100644 --- a/apps/builder/src/components/VariableSearchInput.tsx +++ b/apps/builder/src/components/VariableSearchInput.tsx @@ -10,7 +10,7 @@ import { HStack, useColorModeValue, PopoverAnchor, - useOutsideClick, + Portal, } from '@chakra-ui/react' import { EditIcon, PlusIcon, TrashIcon } from '@/components/icons' import { useTypebot } from '@/features/editor' @@ -19,6 +19,7 @@ import { Variable } from 'models' import React, { useState, useRef, ChangeEvent, useEffect } from 'react' import { useDebouncedCallback } from 'use-debounce' import { byId, env, isDefined, isNotDefined } from 'utils' +import { useOutsideClick } from '@/hooks/useOutsideClick' type Props = { initialVariableId?: string @@ -188,75 +189,78 @@ export const VariableSearchInput = ({ {...inputProps} /> - - {isCreateVariableButtonDisplayed && ( - - )} - {filteredItems.length > 0 && ( - <> - {filteredItems.map((item, idx) => { - const indexInList = isCreateVariableButtonDisplayed - ? idx + 1 - : idx - return ( - - ) - })} - - )} - + + + {isCreateVariableButtonDisplayed && ( + + )} + {filteredItems.length > 0 && ( + <> + {filteredItems.map((item, idx) => { + const indexInList = isCreateVariableButtonDisplayed + ? idx + 1 + : idx + return ( + + ) + })} + + )} + + ) diff --git a/apps/builder/src/features/blocks/bubbles/textBubble/components/TextBubbleEditor/TextBubbleEditor.tsx b/apps/builder/src/features/blocks/bubbles/textBubble/components/TextBubbleEditor/TextBubbleEditor.tsx index 4557c0666..06049d26e 100644 --- a/apps/builder/src/features/blocks/bubbles/textBubble/components/TextBubbleEditor/TextBubbleEditor.tsx +++ b/apps/builder/src/features/blocks/bubbles/textBubble/components/TextBubbleEditor/TextBubbleEditor.tsx @@ -3,7 +3,6 @@ import { Stack, useColorModeValue, useEventListener, - useOutsideClick, } from '@chakra-ui/react' import React, { useEffect, useRef, useState } from 'react' import { @@ -22,6 +21,7 @@ import { serializeHtml } from '@udecode/plate-serializer-html' import { parseHtmlStringToPlainText } from '../../utils' import { VariableSearchInput } from '@/components/VariableSearchInput' import { colors } from '@/lib/theme' +import { useOutsideClick } from '@/hooks/useOutsideClick' type TextBubbleEditorContentProps = { id: string diff --git a/apps/builder/src/features/blocks/integrations/webhook/components/WebhookSettings/WebhookSettings.tsx b/apps/builder/src/features/blocks/integrations/webhook/components/WebhookSettings/WebhookSettings.tsx index 6841b0eb7..8937e0e4e 100644 --- a/apps/builder/src/features/blocks/integrations/webhook/components/WebhookSettings/WebhookSettings.tsx +++ b/apps/builder/src/features/blocks/integrations/webhook/components/WebhookSettings/WebhookSettings.tsx @@ -193,7 +193,7 @@ export const WebhookSettings = ({ Query params - + initialItems={localWebhook.queryParams} onItemsChange={handleQueryParamsChange} @@ -208,7 +208,7 @@ export const WebhookSettings = ({ Headers - + initialItems={localWebhook.headers} onItemsChange={handleHeadersChange} @@ -223,7 +223,7 @@ export const WebhookSettings = ({ Body - + - + initialItems={ options?.variablesForTest ?? { byId: {}, allIds: [] } @@ -279,7 +279,7 @@ export const WebhookSettings = ({ Save in variables - + initialItems={options.responseVariableMapping} onItemsChange={handleResponseMappingChange} diff --git a/apps/builder/src/features/graph/components/Nodes/GroupNode/GroupNode.tsx b/apps/builder/src/features/graph/components/Nodes/GroupNode/GroupNode.tsx index ccfa1631d..9999d86a5 100644 --- a/apps/builder/src/features/graph/components/Nodes/GroupNode/GroupNode.tsx +++ b/apps/builder/src/features/graph/components/Nodes/GroupNode/GroupNode.tsx @@ -120,10 +120,6 @@ const NonMemoizedDraggableGroupNode = ({ const handleTitleSubmit = (title: string) => title.length > 0 ? updateGroup(groupIndex, { title }) : undefined - const handleMouseDown = (e: React.MouseEvent) => { - e.stopPropagation() - } - const handleMouseEnter = () => { if (isReadOnly) return if (mouseOverGroup?.id !== group.id && !isStartGroup) @@ -200,7 +196,6 @@ const NonMemoizedDraggableGroupNode = ({ currentCoordinates?.y ?? 0 }px)`, }} - onMouseDown={handleMouseDown} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} cursor={isMouseDown ? 'grabbing' : 'pointer'} diff --git a/apps/builder/src/features/graph/utils.ts b/apps/builder/src/features/graph/utils.ts index 612a3c199..8ad27f9ae 100644 --- a/apps/builder/src/features/graph/utils.ts +++ b/apps/builder/src/features/graph/utils.ts @@ -342,7 +342,7 @@ export const getEndpointTopOffset = ({ if (!endpointId) return const endpointRef = endpoints[endpointId]?.ref if (!endpointRef?.current) return - const endpointHeight = 28 * graphScale + const endpointHeight = 34 * graphScale return ( (endpointRef.current.getBoundingClientRect().y + endpointHeight / 2 - diff --git a/apps/builder/src/features/publish/components/embeds/modals/WordpressModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal.tsx index a66c12a2a..4dcbeb61b 100644 --- a/apps/builder/src/features/publish/components/embeds/modals/WordpressModal.tsx +++ b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal.tsx @@ -13,6 +13,7 @@ import { InputRightElement, ModalFooter, Link, + useColorModeValue, } from '@chakra-ui/react' import { ExternalLinkIcon } from '@/components/icons' import { env, getViewerUrl } from 'utils' @@ -44,7 +45,7 @@ export const WordpressModal = ({ the official Typebot WordPress plugin diff --git a/apps/builder/src/features/templates/components/TemplatesModal.tsx b/apps/builder/src/features/templates/components/TemplatesModal.tsx index 956f6cbe9..203c0b765 100644 --- a/apps/builder/src/features/templates/components/TemplatesModal.tsx +++ b/apps/builder/src/features/templates/components/TemplatesModal.tsx @@ -68,8 +68,8 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => { > - - + + {selectedTemplate.emoji}{' '} {selectedTemplate.name} @@ -79,6 +79,7 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => { apiHost={getViewerUrl({ isBuilder: true })} typebot={parseTypebotToPublicTypebot(typebot)} key={typebot.id} + style={{ borderRadius: '0.25rem' }} /> )} diff --git a/apps/builder/src/hooks/useOutsideClick.ts b/apps/builder/src/hooks/useOutsideClick.ts new file mode 100644 index 000000000..a4eb9dd84 --- /dev/null +++ b/apps/builder/src/hooks/useOutsideClick.ts @@ -0,0 +1,26 @@ +import { useEventListener } from '@chakra-ui/react' +import { RefObject } from 'react' + +type Handler = (event: MouseEvent) => void + +type Props = { + ref: RefObject + handler: Handler + mouseEvent?: 'mousedown' | 'mouseup' +} + +export const useOutsideClick = ({ + ref, + handler, + mouseEvent = 'mousedown', +}: Props): void => { + const triggerHandlerIfOutside = (event: MouseEvent) => { + const el = ref?.current + if (!el || el.contains(event.target as Node)) { + return + } + handler(event) + } + + useEventListener(mouseEvent, triggerHandlerIfOutside) +} diff --git a/packages/scripts/fixTypebots.ts b/packages/scripts/fixTypebots.ts index 3850233b9..28a6365ed 100644 --- a/packages/scripts/fixTypebots.ts +++ b/packages/scripts/fixTypebots.ts @@ -8,9 +8,9 @@ import { Group, InputBlockType, PublicTypebot, + publicTypebotSchema, Theme, Typebot, - typebotSchema, } from 'models' import { isDefined, isNotDefined } from 'utils' import { promptAndSetEnvironment } from './utils' @@ -125,13 +125,10 @@ const fixTypebots = async () => { log: [{ emit: 'event', level: 'query' }, 'info', 'warn', 'error'], }) - const twoDaysAgo = new Date() - twoDaysAgo.setDate(twoDaysAgo.getDate() - 2) - - const typebots = await prisma.typebot.findMany({ + const typebots = await prisma.publicTypebot.findMany({ where: { updatedAt: { - gte: twoDaysAgo, + gte: new Date('2023-01-01T00:00:00.000Z'), }, }, }) @@ -150,7 +147,7 @@ const fixTypebots = async () => { (progress / total) * 100 )}%) (${totalFixed} fixed typebots)` ) - const parser = typebotSchema.safeParse({ + const parser = publicTypebotSchema.safeParse({ ...typebot, updatedAt: new Date(typebot.updatedAt), createdAt: new Date(typebot.createdAt), @@ -161,7 +158,7 @@ const fixTypebots = async () => { updatedAt: new Date(), createdAt: new Date(typebot.createdAt), } - typebotSchema.parse(fixedTypebot) + publicTypebotSchema.parse(fixedTypebot) fixedTypebots.push(fixedTypebot) totalFixed += 1 diffs.push({ @@ -182,6 +179,13 @@ const fixTypebots = async () => { where: { id: fixedTypebot.id }, data: { ...fixedTypebot, + // theme: fixedTypebot.theme ?? undefined, + // settings: fixedTypebot.settings ?? undefined, + // resultsTablePreferences: + // 'resultsTablePreferences' in fixedTypebot && + // fixedTypebot.resultsTablePreferences + // ? fixedTypebot.resultsTablePreferences + // : undefined, } as any, }) )