From 4ab322290b270fb47723222eb8f54b3855b2bf11 Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Wed, 13 Aug 2025 10:23:31 -0400 Subject: [PATCH 1/3] refactor: Remove extraneous flag from package lock (#9371) Remove extraneous flag from is-unicode-supported The 'extraneous' flag was removed from the is-unicode-supported dependency in package-lock.json to reflect its correct status in the bundle. --- src/frontend/package-lock.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index f2275b480..c3717f816 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -1176,7 +1176,6 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", - "extraneous": true, "inBundle": true, "license": "MIT", "engines": { From 181606fd803f1ab6d8ace99502c5212e21274190 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Wed, 13 Aug 2025 12:54:49 -0300 Subject: [PATCH 2/3] fix: add cursor state to not skip to end on input components (#9375) * Add cursor handling to input component * add cursor handling to text area component * [autofix.ci] apply automated fixes * Modified tests to check cursor position * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../components/popover/index.tsx | 19 +++++++++++++++---- .../components/popoverObject/index.tsx | 11 +++++++++++ .../components/inputComponent/index.tsx | 9 +++++++++ .../components/textAreaComponent/index.tsx | 9 +++++++++ .../tests/core/unit/inputComponent.spec.ts | 13 +++++++++++++ .../core/unit/textAreaModalComponent.spec.ts | 15 +++++++++++++++ 6 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/components/popover/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/components/popover/index.tsx index 4613275dd..ac1b30a5d 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/components/popover/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/components/popover/index.tsx @@ -1,7 +1,7 @@ import { PopoverAnchor } from "@radix-ui/react-popover"; -import { uniqueId } from "lodash"; + import { X } from "lucide-react"; -import { type ReactNode, useMemo, useState } from "react"; +import { type ReactNode, useEffect, useMemo, useState } from "react"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import ShadTooltip from "@/components/common/shadTooltipComponent"; import { Badge } from "@/components/ui/badge"; @@ -189,12 +189,20 @@ const CustomInputPopover = ({ hasRefreshButton, }) => { const [isFocused, setIsFocused] = useState(false); + const [cursor, setCursor] = useState(null); const memoizedOptions = useMemo(() => new Set(options), [options]); const PopoverContentInput = editNode ? PopoverContent : PopoverContentWithoutPortal; + // Restore cursor position after value changes + useEffect(() => { + if (cursor !== null && refInput.current) { + refInput.current.setSelectionRange(cursor, cursor); + } + }, [cursor, value]); + const handleRemoveOption = ( optionToRemove: string, e: React.MouseEvent, @@ -270,7 +278,7 @@ const CustomInputPopover = ({ autoComplete="off" onFocus={() => setIsFocused(true)} autoFocus={autoFocus} - id={id + uniqueId()} + id={id} ref={refInput} type={!pwdVisible && password ? "password" : "text"} onBlur={() => { @@ -292,7 +300,10 @@ const CustomInputPopover = ({ ? "" : placeholder } - onChange={(e) => onChange?.(e.target.value)} + onChange={(e) => { + setCursor(e.target.selectionStart); + onChange?.(e.target.value); + }} onKeyDown={(e) => { handleKeyDown?.(e); if (blurOnEnter && e.key === "Enter") refInput.current?.blur(); diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/components/popoverObject/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/components/popoverObject/index.tsx index 191c76085..10b44aa76 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/components/popoverObject/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/components/popoverObject/index.tsx @@ -1,4 +1,5 @@ import { PopoverAnchor } from "@radix-ui/react-popover"; +import { useEffect, useState } from "react"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import { Command, @@ -40,11 +41,21 @@ const CustomInputPopoverObject = ({ handleKeyDown, showOptions, }) => { + const [cursor, setCursor] = useState(null); + const PopoverContentInput = editNode ? PopoverContent : PopoverContentWithoutPortal; + // Restore cursor position after value changes + useEffect(() => { + if (cursor !== null && refInput.current) { + refInput.current.setSelectionRange(cursor, cursor); + } + }, [cursor, value]); + const handleInputChange = (e) => { + setCursor(e.target.selectionStart); onChange && onChange(e.target.value); }; diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/index.tsx index 11889f12d..11ca476fd 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/index.tsx @@ -45,6 +45,7 @@ export default function InputComponent({ hasRefreshButton = false, }: InputComponentType): JSX.Element { const [pwdVisible, setPwdVisible] = useState(false); + const [cursor, setCursor] = useState(null); const refInput = useRef(null); const [showOptions, setShowOptions] = useState(false); @@ -54,6 +55,13 @@ export default function InputComponent({ } }, [disabled]); + // Restore cursor position after value changes + useEffect(() => { + if (cursor !== null && refInput.current) { + refInput.current.setSelectionRange(cursor, cursor); + } + }, [cursor, value]); + function onInputLostFocus(event): void { if (onBlur) onBlur(event); } @@ -83,6 +91,7 @@ export default function InputComponent({ )} placeholder={password && editNode ? "Key" : placeholder} onChange={(e) => { + setCursor(e.target.selectionStart); if (onChangeFolderName) { return onChangeFolderName(e); } diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/textAreaComponent/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/textAreaComponent/index.tsx index 2a844ac59..07a1726af 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/textAreaComponent/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/textAreaComponent/index.tsx @@ -74,6 +74,7 @@ export default function TextAreaComponent({ const inputRef = useRef(null); const [isFocused, setIsFocused] = useState(false); const [passwordVisible, setPasswordVisible] = useState(false); + const [cursor, setCursor] = useState(null); const isWebhook = useMemo( () => nodeInformationMetadata?.nodeType === "webhook", @@ -100,6 +101,13 @@ export default function TextAreaComponent({ } }, [isWebhook, value, nodeInformationMetadata, handleOnNewValue]); + // Restore cursor position after value changes + useEffect(() => { + if (cursor !== null && inputRef.current) { + inputRef.current.setSelectionRange(cursor, cursor); + } + }, [cursor, value]); + const getInputClassName = () => { return cn( inputClasses.base({ isFocused, password: password! }), @@ -111,6 +119,7 @@ export default function TextAreaComponent({ }; const handleInputChange = (e: React.ChangeEvent) => { + setCursor(e.target.selectionStart); handleOnNewValue({ value: e.target.value }); }; diff --git a/src/frontend/tests/core/unit/inputComponent.spec.ts b/src/frontend/tests/core/unit/inputComponent.spec.ts index 08d26a4f8..fb3d985c0 100644 --- a/src/frontend/tests/core/unit/inputComponent.spec.ts +++ b/src/frontend/tests/core/unit/inputComponent.spec.ts @@ -38,6 +38,19 @@ test( expect(false).toBeTruthy(); } + // Test cursor position preservation + const input = page.getByTestId("popover-anchor-input-collection_name"); + await input.click(); + await input.press("Home"); // Move cursor to start + await input.press("ArrowRight"); // Move cursor to position 1 + await input.press("ArrowRight"); // Move cursor to position 2 + await input.pressSequentially("X", { delay: 100 }); // Type at position 2 + const cursorValue = await input.inputValue(); + if (!cursorValue.startsWith("coX")) { + expect(false).toBeTruthy(); + } + await input.fill("collection_name_test_123123123!@#$&*(&%$@"); + await page.getByTestId("div-generic-node").click(); await page.getByTestId("edit-button-modal").last().click(); diff --git a/src/frontend/tests/core/unit/textAreaModalComponent.spec.ts b/src/frontend/tests/core/unit/textAreaModalComponent.spec.ts index 2621a49c0..2a6218564 100644 --- a/src/frontend/tests/core/unit/textAreaModalComponent.spec.ts +++ b/src/frontend/tests/core/unit/textAreaModalComponent.spec.ts @@ -44,6 +44,21 @@ test( "test test test test test test test test test test test !@#%*)( 123456789101010101010101111111111 !!!!!!!!!!", ); + // Test cursor position preservation + const textInput = page.getByTestId("textarea_str_text"); + await textInput.click(); + await textInput.press("Home"); // Move cursor to start + await textInput.press("ArrowRight"); // Move cursor to position 1 + await textInput.press("ArrowRight"); // Move cursor to position 2 + await textInput.pressSequentially("Y", { delay: 100 }); // Type at position 2 + const cursorValue = await textInput.inputValue(); + if (!cursorValue.startsWith("teY")) { + expect(false).toBeTruthy(); + } + await textInput.fill( + "test test test test test test test test test test test !@#%*)( 123456789101010101010101111111111 !!!!!!!!!!", + ); + await page .getByTestId("button_open_text_area_modal_textarea_str_text") .click(); From 9141005e6bd68e83d0babbe3d966de0d685f394a Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Wed, 13 Aug 2025 13:20:04 -0300 Subject: [PATCH 3/3] fix: make settings nav not enter history (#9376) * remove unused location store * make sidebar component replace route in order for back button to go back to previous page before settings * added test to back button functionality * [autofix.ci] apply automated fixes --------- Co-authored-by: Mike Fortman Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../core/sidebarComponent/index.tsx | 2 +- src/frontend/src/stores/locationStore.ts | 21 --------- .../extended/features/userSettings.spec.ts | 45 +++++++++++++++++++ 3 files changed, 46 insertions(+), 22 deletions(-) delete mode 100644 src/frontend/src/stores/locationStore.ts diff --git a/src/frontend/src/components/core/sidebarComponent/index.tsx b/src/frontend/src/components/core/sidebarComponent/index.tsx index 9feae5c7f..1ba31000a 100644 --- a/src/frontend/src/components/core/sidebarComponent/index.tsx +++ b/src/frontend/src/components/core/sidebarComponent/index.tsx @@ -34,7 +34,7 @@ const SideBarButtonsComponent = ({ items }: SideBarButtonsComponentProps) => { {items.map((item, index) => ( - + ((set, get) => ({ - routeHistory: [], - setRouteHistory: (location) => { - const routeHistoryArray = get().routeHistory; - routeHistoryArray.push(location); - - if (routeHistoryArray?.length > 100) { - routeHistoryArray.shift(); - set({ - routeHistory: routeHistoryArray, - }); - } - - set({ - routeHistory: routeHistoryArray, - }); - }, -})); diff --git a/src/frontend/tests/extended/features/userSettings.spec.ts b/src/frontend/tests/extended/features/userSettings.spec.ts index a7cb26656..a4df73cb3 100644 --- a/src/frontend/tests/extended/features/userSettings.spec.ts +++ b/src/frontend/tests/extended/features/userSettings.spec.ts @@ -1,4 +1,5 @@ import { expect, test } from "@playwright/test"; +import { awaitBootstrapTest } from "../../utils/await-bootstrap-test"; test.beforeAll(async () => { await new Promise((resolve) => setTimeout(resolve, 7000)); @@ -200,3 +201,47 @@ test( await page.getByText(randomName).isVisible(); }, ); + +test( + "should navigate back to flow from global variables", + { tag: ["@release", "@workspace"] }, + async ({ page }) => { + await awaitBootstrapTest(page); + + await page.getByTestId("side_nav_options_all-templates").click(); + await page.getByRole("heading", { name: "Basic Prompting" }).click(); + await page.waitForSelector('[data-testid="fit_view"]', { + timeout: 100000, + }); + // Now navigate to user settings + await page.getByTestId("user-profile-settings").click(); + await page.getByTestId("menu_settings_button").click(); + + // Verify we're on the settings page + await expect(page.getByText("General").nth(2)).toBeVisible({ + timeout: 4000, + }); + + // Navigate to Global Variables + await page.getByText("Global Variables").click(); + await page.getByText("Global Variables").nth(2); + await page + .getByText("Global Variables", { exact: true }) + .nth(1) + .isVisible(); + + // Click the back button - this should take us back to the flow, not to the main settings page + await page.getByTestId("back_page_button").click(); + + // Verify we're back on the flow page, not the settings main page + await page.waitForSelector('[data-testid="sidebar-search-input"]', { + timeout: 5000, + }); + + // Additional verification that we're on the flow page + expect(page.url()).toMatch(/\/flow\//); + + // Verify we can see flow-specific elements + await expect(page.getByTestId("sidebar-search-input")).toBeVisible(); + }, +);