From d1e2781caf57f66f330a96889eb0c7ea47bdc9c0 Mon Sep 17 00:00:00 2001 From: Baptiste Arnaud Date: Mon, 16 Mar 2026 18:36:59 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Migrate=20biome=20rules:=20inter?= =?UTF-8?q?active=20semantics=20checks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ImageUploadContent/UnsplashPicker.tsx | 11 +-- .../src/components/MultiLineEditable.tsx | 16 ++-- apps/builder/src/components/PrimitiveList.tsx | 30 +++---- .../src/components/SingleLineEditable.tsx | 25 +++--- .../components/StackWithGhostableItems.tsx | 1 + apps/builder/src/components/TableList.tsx | 36 +++----- apps/builder/src/components/TagsInput.tsx | 9 +- .../VideoUploadContent/PexelsPicker.tsx | 15 +--- .../features/editor/components/BlockCard.tsx | 4 +- .../editor/components/BlockCardLayout.tsx | 5 +- .../editor/components/BlockCardOverlay.tsx | 3 - .../editor/components/BlocksSideBar.tsx | 75 ++++++++++------ .../events/components/EventCardLayout.tsx | 6 +- .../events/components/EventCardOverlay.tsx | 3 - .../folders/components/FolderContent.tsx | 1 - .../components/TypebotButtonOverlay.tsx | 9 +- .../graph/components/edges/DropOffEdge.tsx | 67 ++++++-------- .../features/graph/components/edges/Edge.tsx | 1 + .../components/nodes/PlaceholderNode.tsx | 89 ++++++++++--------- .../components/nodes/block/BlockNode.tsx | 3 + .../components/nodes/group/GroupNode.tsx | 1 + .../graph/components/nodes/item/ItemNode.tsx | 1 + .../components/nodes/item/ItemNodesList.tsx | 1 + .../preview/components/PreviewDrawer.tsx | 17 ++-- .../preview/components/VariablesDrawer.tsx | 19 ++-- .../user/helpers/setLocaleInCookies.ts | 2 +- .../product-principles/ProductPrinciples.tsx | 36 ++++---- apps/landing-page/src/helpers/setCookie.ts | 2 +- biome.json | 4 - bun.lock | 4 +- packages/embeds/js/package.json | 2 +- .../inputs/email/components/EmailInput.tsx | 13 +-- .../inputs/number/components/NumberInput.tsx | 23 +++-- .../inputs/phone/components/PhoneInput.tsx | 13 +-- .../inputs/textInput/components/TextInput.tsx | 20 ++--- .../inputs/time/components/TimeForm.tsx | 13 +-- .../blocks/inputs/url/components/UrlInput.tsx | 13 +-- packages/embeds/react/package.json | 2 +- packages/scripts/package.json | 6 +- plans/biome-off-rules-plan.md | 20 +---- 40 files changed, 289 insertions(+), 332 deletions(-) diff --git a/apps/builder/src/components/ImageUploadContent/UnsplashPicker.tsx b/apps/builder/src/components/ImageUploadContent/UnsplashPicker.tsx index baa893eaa..36b214441 100644 --- a/apps/builder/src/components/ImageUploadContent/UnsplashPicker.tsx +++ b/apps/builder/src/components/ImageUploadContent/UnsplashPicker.tsx @@ -179,16 +179,10 @@ type UnsplashImageProps = { }; const UnsplashImage = ({ image, onClick }: UnsplashImageProps) => { - const [isImageHovered, setIsImageHovered] = useState(false); - const { user, urls, alt_description } = image; return ( -
setIsImageHovered(true)} - onMouseLeave={() => setIsImageHovered(false)} - > +
& { onValueChange?: (value: string) => void; }; - preview?: HTMLAttributes; + preview?: ButtonHTMLAttributes; defaultEdit: boolean; value: string; onValueCommit: (value: string) => void; @@ -80,14 +80,12 @@ export const MultiLineEditable = ({ autoFocus /> ) : ( - setIsEditing(true)} - onKeyDown={(event) => { - preview?.onKeyDown?.(event); + type="button" + onClick={(event) => { + preview?.onClick?.(event); if (event.defaultPrevented) return; - if (event.key !== "Enter" && event.key !== " ") return; - event.preventDefault(); setIsEditing(true); }} className={cn( @@ -96,7 +94,7 @@ export const MultiLineEditable = ({ )} > {value} - + )}
); diff --git a/apps/builder/src/components/PrimitiveList.tsx b/apps/builder/src/components/PrimitiveList.tsx index 349dbf584..68aa8ac62 100644 --- a/apps/builder/src/components/PrimitiveList.tsx +++ b/apps/builder/src/components/PrimitiveList.tsx @@ -42,7 +42,6 @@ export const PrimitiveList = ({ onItemsChange, }: Props) => { const [items, setItems] = useState[]>(); - const [showDeleteIndex, setShowDeleteIndex] = useState(null); useEffect(() => { if (items) return; @@ -79,14 +78,9 @@ export const PrimitiveList = ({ onItemsChange(removeIdFromItems([...newItems])); }; - const handleMouseEnter = (itemIndex: number) => () => - setShowDeleteIndex(itemIndex); - const handleCellChange = (itemIndex: number) => (item: T) => updateItem(itemIndex, item); - const handleMouseLeave = () => setShowDeleteIndex(null); - return (
{items?.map((item, itemIndex) => ( @@ -96,27 +90,23 @@ export const PrimitiveList = ({ )}
{children({ item: item.value as T, onItemChange: handleCellChange(itemIndex), })} - {showDeleteIndex === itemIndex && ( - - )} +
))} diff --git a/apps/builder/src/components/SingleLineEditable.tsx b/apps/builder/src/components/SingleLineEditable.tsx index c2afae75a..5475e7986 100644 --- a/apps/builder/src/components/SingleLineEditable.tsx +++ b/apps/builder/src/components/SingleLineEditable.tsx @@ -1,13 +1,19 @@ import { Input, type InputProps } from "@typebot.io/ui/components/Input"; import { cn } from "@typebot.io/ui/lib/cn"; -import { forwardRef, type HTMLAttributes, useRef, useState } from "react"; +import { + type ButtonHTMLAttributes, + forwardRef, + type HTMLAttributes, + useRef, + useState, +} from "react"; import { useOutsideClick } from "@/hooks/useOutsideClick"; export type SingleLineEditableProps = { className?: string; input?: Omit; - preview?: HTMLAttributes; - common?: HTMLAttributes; + preview?: ButtonHTMLAttributes; + common?: HTMLAttributes; defaultEdit?: boolean; value?: string; defaultValue?: string; @@ -75,17 +81,12 @@ export const SingleLineEditable = forwardRef< )} /> ) : ( - { preview?.onClick?.(e); - setIsEditing(true); - }} - onKeyDown={(event) => { - preview?.onKeyDown?.(event); - if (event.defaultPrevented) return; - if (event.key !== "Enter" && event.key !== " ") return; - event.preventDefault(); + if (e.defaultPrevented) return; setIsEditing(true); }} className={cn( @@ -95,7 +96,7 @@ export const SingleLineEditable = forwardRef< )} > {value ?? currentValue} - + )} {children}
diff --git a/apps/builder/src/components/StackWithGhostableItems.tsx b/apps/builder/src/components/StackWithGhostableItems.tsx index 69fae1496..c62bb89be 100644 --- a/apps/builder/src/components/StackWithGhostableItems.tsx +++ b/apps/builder/src/components/StackWithGhostableItems.tsx @@ -132,6 +132,7 @@ const StackWithGhostableItems = ({ ghostItemHeight={gapPixel / childrenLength} closeExpanded={onAbort} > + {/* biome-ignore lint/a11y/noStaticElementInteractions: This hover surface only expands placeholder spacing; the interactive controls are the ghost buttons inside. */}
({ addIdsIfMissing(initialItems) ?? (hasDefaultItem ? ([defaultItem] as T[]) : []), ); - const [showDeleteIndex, setShowDeleteIndex] = useState(null); useEffect(() => { if (items.length && initialItems && initialItems?.length === 0) @@ -78,14 +77,9 @@ export const TableList = ({ onItemsChange([...newItems]); }; - const handleMouseEnter = (itemIndex: number) => () => - setShowDeleteIndex(itemIndex); - const handleCellChange = (itemIndex: number) => (item: T) => updateItem(itemIndex, item); - const handleMouseLeave = () => setShowDeleteIndex(null); - return (
{items.map((item, itemIndex) => ( @@ -95,32 +89,28 @@ export const TableList = ({ )}
{children({ item, onItemChange: handleCellChange(itemIndex) })} - {showDeleteIndex === itemIndex && ( - - )} - {isOrdered && showDeleteIndex === itemIndex && ( + + {isOrdered && ( <> @@ -129,7 +119,7 @@ export const TableList = ({ aria-label={addLabel} onClick={insertItemAt(itemIndex + 1)} variant="secondary" - className="shadow-md size-6 animate-in fade-in-0 slide-in-from-top-1 absolute bottom-2" + className="shadow-md size-6 absolute bottom-2 invisible opacity-0 transition-opacity group-hover/item:visible group-hover/item:opacity-100 group-focus-within/item:visible group-focus-within/item:opacity-100" > diff --git a/apps/builder/src/components/TagsInput.tsx b/apps/builder/src/components/TagsInput.tsx index 209d82f91..2a6ad97e6 100644 --- a/apps/builder/src/components/TagsInput.tsx +++ b/apps/builder/src/components/TagsInput.tsx @@ -81,9 +81,6 @@ export const TagsInput = ({ items, placeholder, onValueChange }: Props) => { return (
inputRef.current?.focus()} - onBlur={addItem} - onKeyDown={handleKeyDown} data-focus={isFocused} > {items?.map((item, index) => ( @@ -101,8 +98,12 @@ export const TagsInput = ({ items, placeholder, onValueChange }: Props) => { value={inputValue} onValueChange={handleInputChange} onFocus={() => setIsFocused(true)} - onBlur={() => setIsFocused(false)} + onBlur={() => { + setIsFocused(false); + addItem(); + }} onKeyDown={(e) => { + handleKeyDown(e); if (e.key === "Enter") addItem(); }} placeholder={items && items.length === 0 ? placeholder : undefined} diff --git a/apps/builder/src/components/VideoUploadContent/PexelsPicker.tsx b/apps/builder/src/components/VideoUploadContent/PexelsPicker.tsx index d069d8d41..7bab74839 100644 --- a/apps/builder/src/components/VideoUploadContent/PexelsPicker.tsx +++ b/apps/builder/src/components/VideoUploadContent/PexelsPicker.tsx @@ -224,14 +224,12 @@ const PexelsVideo = ({ video, onClick }: PexelsVideoProps) => { }, [isImageHovered, imageIndex, video_pictures]); return ( -
setIsImageHovered(true)} - onMouseLeave={() => setIsImageHovered(false)} - > +
-
+
{user.name} diff --git a/apps/builder/src/features/editor/components/BlockCard.tsx b/apps/builder/src/features/editor/components/BlockCard.tsx index b0be9a359..fc108b284 100644 --- a/apps/builder/src/features/editor/components/BlockCard.tsx +++ b/apps/builder/src/features/editor/components/BlockCard.tsx @@ -53,14 +53,14 @@ export const BlockCard = ( tooltip={t("blocks.inputs.fileUpload.blockCard.tooltip")} > -
+ {isFreePlan(workspace) && ( )} -
+ ); case LogicBlockType.SCRIPT: diff --git a/apps/builder/src/features/editor/components/BlockCardLayout.tsx b/apps/builder/src/features/editor/components/BlockCardLayout.tsx index 0b40cf3a3..65d3363bb 100644 --- a/apps/builder/src/features/editor/components/BlockCardLayout.tsx +++ b/apps/builder/src/features/editor/components/BlockCardLayout.tsx @@ -32,7 +32,8 @@ export const BlockCardLayout = ({ -
{!isMouseDown ? children : null} -
+
} /> diff --git a/apps/builder/src/features/editor/components/BlockCardOverlay.tsx b/apps/builder/src/features/editor/components/BlockCardOverlay.tsx index db3e438f8..9f308dec3 100644 --- a/apps/builder/src/features/editor/components/BlockCardOverlay.tsx +++ b/apps/builder/src/features/editor/components/BlockCardOverlay.tsx @@ -7,12 +7,10 @@ export const BlockCardOverlay = ({ type, className, style, - onMouseUp, }: { type: BlockV6["type"]; className?: string; style?: React.CSSProperties; - onMouseUp: () => void; }) => { return (
diff --git a/apps/builder/src/features/editor/components/BlocksSideBar.tsx b/apps/builder/src/features/editor/components/BlocksSideBar.tsx index 18d56412e..6f52c5fb7 100644 --- a/apps/builder/src/features/editor/components/BlocksSideBar.tsx +++ b/apps/builder/src/features/editor/components/BlocksSideBar.tsx @@ -15,7 +15,7 @@ import { SquareLock01Icon } from "@typebot.io/ui/icons/SquareLock01Icon"; import { SquareUnlock01Icon } from "@typebot.io/ui/icons/SquareUnlock01Icon"; import { cx } from "@typebot.io/ui/lib/cva"; import type React from "react"; -import { useState } from "react"; +import { useRef, useState } from "react"; import { useDebouncedCallback } from "use-debounce"; import { Portal } from "@/components/Portal"; import { useBlockDnd } from "@/features/graph/providers/GraphDndProvider"; @@ -59,22 +59,36 @@ export const BlocksSideBar = () => { localStorage.getItem(leftSidebarLockedStorageKey) !== "false", ); const [searchInput, setSearchInput] = useState(""); + const sidebarRef = useRef(null); + const dockBarRef = useRef(null); const closeSideBar = useDebouncedCallback(() => setIsExtended(false), 200); const handleMouseMove = (event: MouseEvent) => { - if (!draggedBlockType && !draggedEventType) return; const { clientX, clientY } = event; - setPosition({ - ...position, - x: clientX - relativeCoordinates.x, - y: clientY - relativeCoordinates.y, - }); + if (draggedBlockType || draggedEventType) { + setPosition({ + x: clientX - relativeCoordinates.x, + y: clientY - relativeCoordinates.y, + }); + } + if (isLocked) return; + if (isMouseInElement(sidebarRef.current, clientX, clientY)) { + closeSideBar.flush(); + return; + } + if (isMouseInElement(dockBarRef.current, clientX, clientY)) { + closeSideBar.flush(); + setIsExtended(true); + return; + } + if (clientX < 100) return; + closeSideBar(); }; useEventListener("mousemove", handleMouseMove); const initBlockDragging = (e: React.MouseEvent, type: BlockV6["type"]) => { - const element = e.currentTarget as HTMLDivElement; + const element = e.currentTarget as HTMLElement; const rect = element.getBoundingClientRect(); setPosition({ x: rect.left, y: rect.top }); const x = e.clientX - rect.left; @@ -87,7 +101,7 @@ export const BlocksSideBar = () => { e: React.MouseEvent, type: TDraggableEvent["type"], ) => { - const element = e.currentTarget as HTMLDivElement; + const element = e.currentTarget as HTMLElement; const rect = element.getBoundingClientRect(); setPosition({ x: rect.left, y: rect.top }); const x = e.clientX - rect.left; @@ -116,16 +130,6 @@ export const BlocksSideBar = () => { setIsLocked(!isLocked); }; - const handleDockBarEnter = () => { - closeSideBar.flush(); - setIsExtended(true); - }; - - const handleMouseLeave = (e: React.MouseEvent) => { - if (isLocked || e.clientX < 100) return; - closeSideBar(); - }; - const handleSearchInputChange = (event: { target: { value: React.SetStateAction }; }) => { @@ -187,11 +191,11 @@ export const BlocksSideBar = () => { return (
@@ -303,7 +307,6 @@ export const BlocksSideBar = () => { { { )}
{!isLocked && ( -
{ + closeSideBar.flush(); + setIsExtended(true); + }} > -
-
+ + )}
); }; + +const isMouseInElement = ( + element: HTMLElement | null, + clientX: number, + clientY: number, +) => { + if (!element) return false; + const rect = element.getBoundingClientRect(); + return ( + clientX >= rect.left && + clientX <= rect.right && + clientY >= rect.top && + clientY <= rect.bottom + ); +}; diff --git a/apps/builder/src/features/events/components/EventCardLayout.tsx b/apps/builder/src/features/events/components/EventCardLayout.tsx index 44ddd7033..f569e2f41 100644 --- a/apps/builder/src/features/events/components/EventCardLayout.tsx +++ b/apps/builder/src/features/events/components/EventCardLayout.tsx @@ -36,7 +36,9 @@ export const EventCardLayout = ({ -
{!isMouseDown ? children : null} -
+
} /> diff --git a/apps/builder/src/features/events/components/EventCardOverlay.tsx b/apps/builder/src/features/events/components/EventCardOverlay.tsx index 04c3b2cb5..5920b5f2f 100644 --- a/apps/builder/src/features/events/components/EventCardOverlay.tsx +++ b/apps/builder/src/features/events/components/EventCardOverlay.tsx @@ -7,12 +7,10 @@ export const EventCardOverlay = ({ type, className, style, - onMouseUp, }: { type: TDraggableEvent["type"]; className?: string; style?: React.CSSProperties; - onMouseUp: () => void; }) => { return (
diff --git a/apps/builder/src/features/folders/components/FolderContent.tsx b/apps/builder/src/features/folders/components/FolderContent.tsx index 77bea79cd..6d06d4566 100644 --- a/apps/builder/src/features/folders/components/FolderContent.tsx +++ b/apps/builder/src/features/folders/components/FolderContent.tsx @@ -201,7 +201,6 @@ export const FolderContent = ({ folder }: Props) => { Promise; style?: React.CSSProperties; }; -export const TypebotButtonOverlay = ({ - typebot, - className, - onMouseUp, - style, -}: Props) => { +export const TypebotButtonOverlay = ({ typebot, className, style }: Props) => { return (
diff --git a/apps/builder/src/features/graph/components/edges/DropOffEdge.tsx b/apps/builder/src/features/graph/components/edges/DropOffEdge.tsx index 1534edd59..fe6b6f88d 100644 --- a/apps/builder/src/features/graph/components/edges/DropOffEdge.tsx +++ b/apps/builder/src/features/graph/components/edges/DropOffEdge.tsx @@ -155,48 +155,33 @@ export const DropOffEdge = ({ { - if ( - isWorkspaceProPlan || - !onUnlockProPlanClick || - (event.key !== "Enter" && event.key !== " ") - ) - return; - event.preventDefault(); - onUnlockProPlanClick(); - }} - > -

- {isWorkspaceProPlan ? ( - dropOffRate - ) : ( - X - )} - % -

- - {isWorkspaceProPlan ? ( - totalDroppedUser - ) : ( - NN - )}{" "} - user{(totalDroppedUser ?? 2) > 1 ? "s" : ""} - -
+ {dropOffRate}% + + {totalDroppedUser} user + {(totalDroppedUser ?? 2) > 1 ? "s" : ""} + +
+ ) : ( + + ) } /> diff --git a/apps/builder/src/features/graph/components/edges/Edge.tsx b/apps/builder/src/features/graph/components/edges/Edge.tsx index 7b50de601..db1e15445 100644 --- a/apps/builder/src/features/graph/components/edges/Edge.tsx +++ b/apps/builder/src/features/graph/components/edges/Edge.tsx @@ -108,6 +108,7 @@ export const Edge = ({ edge, fromElementId }: Props) => { ( <> + {/* biome-ignore lint/a11y/noStaticElementInteractions: SVG paths are graph hit areas, not HTML controls, and React Flow relies on this shape for edge selection. */} ( enabled: isDefined(onClick), }); - return ( -
{ - if (event.key !== "Enter" && event.key !== " ") return; - event.preventDefault(); - onClick(); - }, - } - : {})} > - {onClick && } -
- {isHovered && isHoverExpanded ? children : null} -
+ {isHovered && isHoverExpanded ? children : null} +
+ ); + + return ( +
+ {onClick ? ( + + ) : ( +
+ {placeholderContent} +
+ )}
); }, diff --git a/apps/builder/src/features/graph/components/nodes/block/BlockNode.tsx b/apps/builder/src/features/graph/components/nodes/block/BlockNode.tsx index bf3a2978a..b3b19a761 100644 --- a/apps/builder/src/features/graph/components/nodes/block/BlockNode.tsx +++ b/apps/builder/src/features/graph/components/nodes/block/BlockNode.tsx @@ -227,6 +227,7 @@ export const BlockNode = ({ render={(props) => ( + {/* biome-ignore lint/a11y/noStaticElementInteractions: This node container is a React Flow drag surface with nested controls, not a standalone HTML action. */}
{/* Prevent triggering parent group context menu */} + {/* biome-ignore lint/a11y/noNoninteractiveElementInteractions: This wrapper only stops context-menu propagation for nested popovers. */} + {/* biome-ignore lint/a11y/noStaticElementInteractions: This wrapper only stops context-menu propagation for nested popovers. */}
e.stopPropagation()}> {hasSettingsPopover(block) && ( { disabled={isReadOnly} > + {/* biome-ignore lint/a11y/noStaticElementInteractions: This group container is a draggable graph surface with nested controls, not a standalone HTML action. */}
+ {/* biome-ignore lint/a11y/noStaticElementInteractions: This item container is a draggable graph surface with nested controls, not a standalone HTML action. */}
{ const { t } = useTranslate(); const { setPreviewingBlock } = useGraph(); const [width, setWidth] = useState(500); - const [isResizeHandleVisible, setIsResizeHandleVisible] = useState(false); const [selectedRuntime, setSelectedRuntime] = useState< (typeof runtimes)[number] >(getDefaultRuntime(typebot?.id)); @@ -61,19 +60,13 @@ export const PreviewDrawer = () => { return (
setIsResizeHandleVisible(true)} - onMouseLeave={() => setIsResizeHandleVisible(false)} - onFocus={() => setIsResizeHandleVisible(true)} - onBlur={() => setIsResizeHandleVisible(false)} + className="group/drawer flex absolute border-l shadow-md p-6 right-0 top-0 h-full bg-gray-1 rounded-l-lg z-10" style={{ width: `${width}px` }} > - {isResizeHandleVisible && ( - - )} +
diff --git a/apps/builder/src/features/preview/components/VariablesDrawer.tsx b/apps/builder/src/features/preview/components/VariablesDrawer.tsx index 2b108c7cb..b951f6188 100644 --- a/apps/builder/src/features/preview/components/VariablesDrawer.tsx +++ b/apps/builder/src/features/preview/components/VariablesDrawer.tsx @@ -33,7 +33,6 @@ export const VariablesDrawer = ({ onClose }: Props) => { const { typebot, createVariable, updateVariable, deleteVariable } = useTypebot(); const [width, setWidth] = useState(500); - const [isResizeHandleVisible, setIsResizeHandleVisible] = useState(false); const [searchValue, setSearchValue] = useState(""); const filteredVariables = typebot?.variables.filter((v) => isNotEmpty(searchValue) @@ -71,20 +70,14 @@ export const VariablesDrawer = ({ onClose }: Props) => { return (
setIsResizeHandleVisible(true)} - onFocus={() => setIsResizeHandleVisible(true)} - onBlur={() => setIsResizeHandleVisible(false)} - onMouseLeave={() => setIsResizeHandleVisible(false)} + className="group/drawer flex absolute border-l shadow-md p-6 right-0 top-0 h-full bg-gray-1 rounded-l-lg" style={{ width: `${width}px` }} > - {isResizeHandleVisible && ( - - )} +
+ {isLastItem ? null :
}
-

{content}

+

{content}

- +
); }; diff --git a/apps/landing-page/src/helpers/setCookie.ts b/apps/landing-page/src/helpers/setCookie.ts index cc48352b9..aeeba32b4 100644 --- a/apps/landing-page/src/helpers/setCookie.ts +++ b/apps/landing-page/src/helpers/setCookie.ts @@ -11,6 +11,6 @@ export const setCookie = async (consent: "declined" | "accepted") => { value: encodeURIComponent(JSON.stringify({ consent })), domain: DEFAULT_COOKIE_DOMAIN, path: "/", - expires: new Date(Date.now() + COOKIE_EXPIRATION * 1000), + expires: Date.now() + COOKIE_EXPIRATION * 1000, }); }; diff --git a/biome.json b/biome.json index 7d7080745..f0668b281 100644 --- a/biome.json +++ b/biome.json @@ -31,10 +31,6 @@ "enabled": true, "rules": { "recommended": true, - "security": {}, - "a11y": { - "noStaticElementInteractions": "off" - }, "performance": { "noImgElement": "off" }, diff --git a/bun.lock b/bun.lock index 37d7ee856..ff640277f 100644 --- a/bun.lock +++ b/bun.lock @@ -638,7 +638,7 @@ }, "packages/embeds/js": { "name": "@typebot.io/js", - "version": "0.9.20", + "version": "0.9.21", "devDependencies": { "@ai-sdk/ui-utils": "^1.2.11", "@ark-ui/solid": "^5.19.0", @@ -675,7 +675,7 @@ }, "packages/embeds/react": { "name": "@typebot.io/react", - "version": "0.9.20", + "version": "0.9.21", "dependencies": { "@typebot.io/js": "workspace:*", "react": "^19.2.3", diff --git a/packages/embeds/js/package.json b/packages/embeds/js/package.json index f70b35f43..0bf8156ce 100644 --- a/packages/embeds/js/package.json +++ b/packages/embeds/js/package.json @@ -1,6 +1,6 @@ { "name": "@typebot.io/js", - "version": "0.9.20", + "version": "0.9.21", "description": "Javascript library to display typebots on your website", "license": "FSL-1.1-ALv2", "type": "module", diff --git a/packages/embeds/js/src/features/blocks/inputs/email/components/EmailInput.tsx b/packages/embeds/js/src/features/blocks/inputs/email/components/EmailInput.tsx index ae7021561..97fc8c39a 100644 --- a/packages/embeds/js/src/features/blocks/inputs/email/components/EmailInput.tsx +++ b/packages/embeds/js/src/features/blocks/inputs/email/components/EmailInput.tsx @@ -28,8 +28,9 @@ export const EmailInput = (props: Props) => { else inputRef?.focus(); }; - const submitWhenEnter = (e: KeyboardEvent) => { - if (e.key === "Enter") submit(); + const handleSubmit = (event: Event) => { + event.preventDefault(); + submit(); }; onMount(() => { @@ -50,9 +51,9 @@ export const EmailInput = (props: Props) => { }; return ( -
{ autocomplete="email" />
- + {props.block.options?.labels?.button} -
+ ); }; diff --git a/packages/embeds/js/src/features/blocks/inputs/number/components/NumberInput.tsx b/packages/embeds/js/src/features/blocks/inputs/number/components/NumberInput.tsx index efc2ffe49..8214873e4 100644 --- a/packages/embeds/js/src/features/blocks/inputs/number/components/NumberInput.tsx +++ b/packages/embeds/js/src/features/blocks/inputs/number/components/NumberInput.tsx @@ -49,8 +49,9 @@ export const NumberInput = (props: NumberInputProps) => { } else numberInput().focus(); }; - const submitWhenEnter = (e: KeyboardEvent) => { - if (e.key === "Enter") submit(); + const handleSubmit = (event: Event) => { + event.preventDefault(); + submit(); }; onMount(() => { @@ -71,9 +72,9 @@ export const NumberInput = (props: NumberInputProps) => { }; return ( -
{ } /> - + - + - + {props.block.options?.labels?.button ?? defaultNumberInputButtonLabel} -
+ ); }; diff --git a/packages/embeds/js/src/features/blocks/inputs/phone/components/PhoneInput.tsx b/packages/embeds/js/src/features/blocks/inputs/phone/components/PhoneInput.tsx index acc2f1f76..d42db37a2 100644 --- a/packages/embeds/js/src/features/blocks/inputs/phone/components/PhoneInput.tsx +++ b/packages/embeds/js/src/features/blocks/inputs/phone/components/PhoneInput.tsx @@ -68,8 +68,9 @@ export const PhoneInput = (props: PhoneInputProps) => { } else inputRef?.focus(); }; - const submitWhenEnter = (e: KeyboardEvent) => { - if (e.key === "Enter") submit(); + const handleSubmit = (event: Event) => { + event.preventDefault(); + submit(); }; const selectNewCountryCode = ( @@ -111,9 +112,9 @@ export const PhoneInput = (props: PhoneInputProps) => { }; return ( -
@@ -158,9 +159,9 @@ export const PhoneInput = (props: PhoneInputProps) => { autofocus={!guessDeviceIsMobile()} />
- + {props.labels?.button} -
+ ); }; diff --git a/packages/embeds/js/src/features/blocks/inputs/textInput/components/TextInput.tsx b/packages/embeds/js/src/features/blocks/inputs/textInput/components/TextInput.tsx index 1b39080c1..0e87f9d13 100644 --- a/packages/embeds/js/src/features/blocks/inputs/textInput/components/TextInput.tsx +++ b/packages/embeds/js/src/features/blocks/inputs/textInput/components/TextInput.tsx @@ -102,9 +102,9 @@ export const TextInput = (props: Props) => { } else inputRef?.focus(); }; - const submitWhenEnter = (e: KeyboardEvent) => { - if (props.block.options?.isLong) return; - if (e.key === "Enter") submit(); + const handleSubmit = (event: Event) => { + event.preventDefault(); + submit(); }; const submitIfCtrlEnter = (e: KeyboardEvent) => { @@ -261,14 +261,14 @@ export const TextInput = (props: Props) => { }; return ( -
{ } >
+ ); }; diff --git a/packages/embeds/js/src/features/blocks/inputs/time/components/TimeForm.tsx b/packages/embeds/js/src/features/blocks/inputs/time/components/TimeForm.tsx index f1cea77ed..a7e98a352 100644 --- a/packages/embeds/js/src/features/blocks/inputs/time/components/TimeForm.tsx +++ b/packages/embeds/js/src/features/blocks/inputs/time/components/TimeForm.tsx @@ -26,8 +26,9 @@ export const TimeForm = (props: Props) => { else inputRef?.focus(); }; - const submitWhenEnter = (e: KeyboardEvent) => { - if (e.key === "Enter") submit(); + const handleSubmit = (event: Event) => { + event.preventDefault(); + submit(); }; onMount(() => { @@ -48,9 +49,9 @@ export const TimeForm = (props: Props) => { }; return ( -
{ data-testid="time" />
- + {props.block?.labels?.button} -
+ ); }; diff --git a/packages/embeds/js/src/features/blocks/inputs/url/components/UrlInput.tsx b/packages/embeds/js/src/features/blocks/inputs/url/components/UrlInput.tsx index 99b5a06da..c934f695a 100644 --- a/packages/embeds/js/src/features/blocks/inputs/url/components/UrlInput.tsx +++ b/packages/embeds/js/src/features/blocks/inputs/url/components/UrlInput.tsx @@ -32,8 +32,9 @@ export const UrlInput = (props: Props) => { else inputRef?.focus(); }; - const submitWhenEnter = (e: KeyboardEvent) => { - if (e.key === "Enter") submit(); + const handleSubmit = (event: Event) => { + event.preventDefault(); + submit(); }; onMount(() => { @@ -57,9 +58,9 @@ export const UrlInput = (props: Props) => { }; return ( -
{ autocomplete="url" />
- + {props.block.options?.labels?.button} -
+ ); }; diff --git a/packages/embeds/react/package.json b/packages/embeds/react/package.json index 09b8fa1b8..b1ae16ef0 100644 --- a/packages/embeds/react/package.json +++ b/packages/embeds/react/package.json @@ -1,6 +1,6 @@ { "name": "@typebot.io/react", - "version": "0.9.20", + "version": "0.9.21", "description": "Convenient library to display typebots on your React app", "license": "FSL-1.1-ALv2", "type": "module", diff --git a/packages/scripts/package.json b/packages/scripts/package.json index c381793fe..e4373bbb2 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -15,13 +15,13 @@ "migrateSubscriptionsToUsageBased": "tsx src/migrateSubscriptionsToUsageBased.ts", "insertUsersInBrevoList": "tsx src/insertUsersInBrevoList.ts", "getUsage": "tsx src/getUsage.ts", - "suspendWorkspace": "tsx src/suspendWorkspace.ts", + "suspendWorkspace": "SKIP_ENV_CHECK=true dotenv -e ./.env.production -- tsx src/suspendWorkspace.ts", "destroyUser": "SKIP_ENV_CHECK=true dotenv -e ./.env.production -- tsx src/destroyUser.ts", "updateTypebot": "SKIP_ENV_CHECK=true dotenv -e ./.env.production -- tsx src/updateTypebot.ts", - "updateWorkspace": "tsx src/updateWorkspace.ts", + "updateWorkspace": "SKIP_ENV_CHECK=true dotenv -e ./.env.production -- tsx src/updateWorkspace.ts", "inspectTypebot": "SKIP_ENV_CHECK=true dotenv -e ./.env.production -- tsx src/inspectTypebot.ts", "inspectPublishedTypebot": "tsx src/inspectPublishedTypebot.ts", - "inspectWorkspace": "tsx src/inspectWorkspace.ts", + "inspectWorkspace": "SKIP_ENV_CHECK=true dotenv -e ./.env.production -- tsx src/inspectWorkspace.ts", "getCoupon": "tsx src/getCoupon.ts", "redeemCoupon": "tsx src/redeemCoupon.ts", "exportResults": "SKIP_ENV_CHECK=true dotenv -e ./.env.production -- tsx src/exportResults.ts", diff --git a/plans/biome-off-rules-plan.md b/plans/biome-off-rules-plan.md index e4fa469e8..b573fec58 100644 --- a/plans/biome-off-rules-plan.md +++ b/plans/biome-off-rules-plan.md @@ -143,21 +143,7 @@ Workspaces cibles: Pourquoi: demande souvent un vrai choix de markup ou de composition de composant, surtout quand il y a des boutons imbriques. -### PR 3C - politique image par workspace - -Isoler la regle image dans une PR dediee: - -- `performance/noImgElement` - -Workspaces cibles: - -- `apps/landing-page` -- autres apps Next si necessaire -- `packages/embeds/js` a traiter a part ou a exclure selon la politique retenue - -Pourquoi: la regle pousse naturellement vers `next/image`, mais ce n'est pas adapte tel quel a tous les workspaces du monorepo. - -### PR 3D - nettoyage boucles / callbacks / mutation de parametres +### PR 3C - nettoyage boucles / callbacks / mutation de parametres Regrouper les refactors surtout mecaniques et peu lies a React: @@ -172,7 +158,7 @@ Workspaces cibles: Pourquoi: diff assez reviewable si isole, risque produit faible a moyen, et bon rendement sur du code de support. -### PR 3E - durcissement de typage +### PR 3D - durcissement de typage Traiter ensuite les raccourcis de typage les plus bruyants: @@ -186,7 +172,7 @@ Workspaces cibles: Pourquoi: risque plus eleve sur les contrats de types partages; mieux vaut ne pas melanger ca avec les changements UI ou hooks. -### PR 3F - correction des hooks React +### PR 3E - correction des hooks React Finir par les regles les plus semantiques cote React: