Merge branch 'main' into fix_starter_windows
This commit is contained in:
commit
577bece3a0
10 changed files with 118 additions and 27 deletions
1
src/frontend/package-lock.json
generated
1
src/frontend/package-lock.json
generated
|
|
@ -1176,7 +1176,6 @@
|
||||||
},
|
},
|
||||||
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"extraneous": true,
|
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { PopoverAnchor } from "@radix-ui/react-popover";
|
import { PopoverAnchor } from "@radix-ui/react-popover";
|
||||||
import { uniqueId } from "lodash";
|
|
||||||
import { X } from "lucide-react";
|
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 ForwardedIconComponent from "@/components/common/genericIconComponent";
|
||||||
import ShadTooltip from "@/components/common/shadTooltipComponent";
|
import ShadTooltip from "@/components/common/shadTooltipComponent";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
@ -189,12 +189,20 @@ const CustomInputPopover = ({
|
||||||
hasRefreshButton,
|
hasRefreshButton,
|
||||||
}) => {
|
}) => {
|
||||||
const [isFocused, setIsFocused] = useState(false);
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
|
const [cursor, setCursor] = useState<number | null>(null);
|
||||||
const memoizedOptions = useMemo(() => new Set<string>(options), [options]);
|
const memoizedOptions = useMemo(() => new Set<string>(options), [options]);
|
||||||
|
|
||||||
const PopoverContentInput = editNode
|
const PopoverContentInput = editNode
|
||||||
? PopoverContent
|
? PopoverContent
|
||||||
: PopoverContentWithoutPortal;
|
: PopoverContentWithoutPortal;
|
||||||
|
|
||||||
|
// Restore cursor position after value changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (cursor !== null && refInput.current) {
|
||||||
|
refInput.current.setSelectionRange(cursor, cursor);
|
||||||
|
}
|
||||||
|
}, [cursor, value]);
|
||||||
|
|
||||||
const handleRemoveOption = (
|
const handleRemoveOption = (
|
||||||
optionToRemove: string,
|
optionToRemove: string,
|
||||||
e: React.MouseEvent<HTMLButtonElement>,
|
e: React.MouseEvent<HTMLButtonElement>,
|
||||||
|
|
@ -270,7 +278,7 @@ const CustomInputPopover = ({
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
onFocus={() => setIsFocused(true)}
|
onFocus={() => setIsFocused(true)}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
id={id + uniqueId()}
|
id={id}
|
||||||
ref={refInput}
|
ref={refInput}
|
||||||
type={!pwdVisible && password ? "password" : "text"}
|
type={!pwdVisible && password ? "password" : "text"}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
|
|
@ -292,7 +300,10 @@ const CustomInputPopover = ({
|
||||||
? ""
|
? ""
|
||||||
: placeholder
|
: placeholder
|
||||||
}
|
}
|
||||||
onChange={(e) => onChange?.(e.target.value)}
|
onChange={(e) => {
|
||||||
|
setCursor(e.target.selectionStart);
|
||||||
|
onChange?.(e.target.value);
|
||||||
|
}}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
handleKeyDown?.(e);
|
handleKeyDown?.(e);
|
||||||
if (blurOnEnter && e.key === "Enter") refInput.current?.blur();
|
if (blurOnEnter && e.key === "Enter") refInput.current?.blur();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { PopoverAnchor } from "@radix-ui/react-popover";
|
import { PopoverAnchor } from "@radix-ui/react-popover";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
import ForwardedIconComponent from "@/components/common/genericIconComponent";
|
import ForwardedIconComponent from "@/components/common/genericIconComponent";
|
||||||
import {
|
import {
|
||||||
Command,
|
Command,
|
||||||
|
|
@ -40,11 +41,21 @@ const CustomInputPopoverObject = ({
|
||||||
handleKeyDown,
|
handleKeyDown,
|
||||||
showOptions,
|
showOptions,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [cursor, setCursor] = useState<number | null>(null);
|
||||||
|
|
||||||
const PopoverContentInput = editNode
|
const PopoverContentInput = editNode
|
||||||
? PopoverContent
|
? PopoverContent
|
||||||
: PopoverContentWithoutPortal;
|
: PopoverContentWithoutPortal;
|
||||||
|
|
||||||
|
// Restore cursor position after value changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (cursor !== null && refInput.current) {
|
||||||
|
refInput.current.setSelectionRange(cursor, cursor);
|
||||||
|
}
|
||||||
|
}, [cursor, value]);
|
||||||
|
|
||||||
const handleInputChange = (e) => {
|
const handleInputChange = (e) => {
|
||||||
|
setCursor(e.target.selectionStart);
|
||||||
onChange && onChange(e.target.value);
|
onChange && onChange(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ export default function InputComponent({
|
||||||
hasRefreshButton = false,
|
hasRefreshButton = false,
|
||||||
}: InputComponentType): JSX.Element {
|
}: InputComponentType): JSX.Element {
|
||||||
const [pwdVisible, setPwdVisible] = useState(false);
|
const [pwdVisible, setPwdVisible] = useState(false);
|
||||||
|
const [cursor, setCursor] = useState<number | null>(null);
|
||||||
const refInput = useRef<HTMLInputElement>(null);
|
const refInput = useRef<HTMLInputElement>(null);
|
||||||
const [showOptions, setShowOptions] = useState<boolean>(false);
|
const [showOptions, setShowOptions] = useState<boolean>(false);
|
||||||
|
|
||||||
|
|
@ -54,6 +55,13 @@ export default function InputComponent({
|
||||||
}
|
}
|
||||||
}, [disabled]);
|
}, [disabled]);
|
||||||
|
|
||||||
|
// Restore cursor position after value changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (cursor !== null && refInput.current) {
|
||||||
|
refInput.current.setSelectionRange(cursor, cursor);
|
||||||
|
}
|
||||||
|
}, [cursor, value]);
|
||||||
|
|
||||||
function onInputLostFocus(event): void {
|
function onInputLostFocus(event): void {
|
||||||
if (onBlur) onBlur(event);
|
if (onBlur) onBlur(event);
|
||||||
}
|
}
|
||||||
|
|
@ -83,6 +91,7 @@ export default function InputComponent({
|
||||||
)}
|
)}
|
||||||
placeholder={password && editNode ? "Key" : placeholder}
|
placeholder={password && editNode ? "Key" : placeholder}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
setCursor(e.target.selectionStart);
|
||||||
if (onChangeFolderName) {
|
if (onChangeFolderName) {
|
||||||
return onChangeFolderName(e);
|
return onChangeFolderName(e);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ export default function TextAreaComponent({
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const [isFocused, setIsFocused] = useState(false);
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
const [passwordVisible, setPasswordVisible] = useState(false);
|
const [passwordVisible, setPasswordVisible] = useState(false);
|
||||||
|
const [cursor, setCursor] = useState<number | null>(null);
|
||||||
|
|
||||||
const isWebhook = useMemo(
|
const isWebhook = useMemo(
|
||||||
() => nodeInformationMetadata?.nodeType === "webhook",
|
() => nodeInformationMetadata?.nodeType === "webhook",
|
||||||
|
|
@ -100,6 +101,13 @@ export default function TextAreaComponent({
|
||||||
}
|
}
|
||||||
}, [isWebhook, value, nodeInformationMetadata, handleOnNewValue]);
|
}, [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 = () => {
|
const getInputClassName = () => {
|
||||||
return cn(
|
return cn(
|
||||||
inputClasses.base({ isFocused, password: password! }),
|
inputClasses.base({ isFocused, password: password! }),
|
||||||
|
|
@ -111,6 +119,7 @@ export default function TextAreaComponent({
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setCursor(e.target.selectionStart);
|
||||||
handleOnNewValue({ value: e.target.value });
|
handleOnNewValue({ value: e.target.value });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ const SideBarButtonsComponent = ({ items }: SideBarButtonsComponentProps) => {
|
||||||
<SidebarMenu>
|
<SidebarMenu>
|
||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
<SidebarMenuItem key={index}>
|
<SidebarMenuItem key={index}>
|
||||||
<CustomLink to={item.href!}>
|
<CustomLink to={item.href!} replace>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
size="md"
|
size="md"
|
||||||
isActive={
|
isActive={
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import { create } from "zustand";
|
|
||||||
import type { LocationStoreType } from "../types/zustand/location";
|
|
||||||
|
|
||||||
export const useLocationStore = create<LocationStoreType>((set, get) => ({
|
|
||||||
routeHistory: [],
|
|
||||||
setRouteHistory: (location) => {
|
|
||||||
const routeHistoryArray = get().routeHistory;
|
|
||||||
routeHistoryArray.push(location);
|
|
||||||
|
|
||||||
if (routeHistoryArray?.length > 100) {
|
|
||||||
routeHistoryArray.shift();
|
|
||||||
set({
|
|
||||||
routeHistory: routeHistoryArray,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
set({
|
|
||||||
routeHistory: routeHistoryArray,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
@ -38,6 +38,19 @@ test(
|
||||||
expect(false).toBeTruthy();
|
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("div-generic-node").click();
|
||||||
|
|
||||||
await page.getByTestId("edit-button-modal").last().click();
|
await page.getByTestId("edit-button-modal").last().click();
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,21 @@ test(
|
||||||
"test test test test test test test test test test test !@#%*)( 123456789101010101010101111111111 !!!!!!!!!!",
|
"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
|
await page
|
||||||
.getByTestId("button_open_text_area_modal_textarea_str_text")
|
.getByTestId("button_open_text_area_modal_textarea_str_text")
|
||||||
.click();
|
.click();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
|
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||||
|
|
||||||
test.beforeAll(async () => {
|
test.beforeAll(async () => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 7000));
|
await new Promise((resolve) => setTimeout(resolve, 7000));
|
||||||
|
|
@ -200,3 +201,47 @@ test(
|
||||||
await page.getByText(randomName).isVisible();
|
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();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue