feat: Revamp GlobalVariableModal (#5512)
* 📝 (GlobalVariableModal.tsx): Refactor GlobalVariableModal component to use Tabs component instead of Select for type selection and improve layout and styling of input fields and labels 🔧 (index.tsx): Add popoverWidth prop to InputComponent to allow setting the width of the popover in CustomInputPopover component 🔧 (index.tsx): Add popoverWidth prop to InputComponent in InputGlobalComponent to set the width of the popover to 315px ✨ (tabs-button.tsx): introduce new TabsButton component to handle tab functionality in UI components 📝 (tabs-button.tsx): add documentation for Tabs, TabsContent, TabsList, TabsTrigger components 📝 (components/index.ts): add popoverWidth property to InputComponentType to control the width of the popover in UI components * ✨ (GlobalVariableModal.tsx): Add ForwardedIconComponent to display an icon next to the modal header text for better visual representation 📝 (GlobalVariableModal.tsx): Update TabsTrigger components to include data-testid attribute for testing purposes ✨ (index.tsx): Introduce new components OptionBadge, CommandItemContent, and SelectionIndicator to improve code organization and reusability ♻️ (index.tsx): Refactor handleRemoveOption function to have a more descriptive parameter signature and improve readability 📝 (index.tsx): Add comments to clarify the purpose of handleOptionSelect function and improve code documentation 📝 (index.tsx): Add comments to describe the purpose of getInputClassName and getAnchorClassName functions for better code understanding ✨ (globalVariables.spec.ts): improve placeholder text for variable name and value fields for better user understanding and experience * 🐛 (GlobalVariableModal.tsx): Fix disabled state logic for TabsTrigger components to correctly reflect initialData type 📝 (GlobalVariableModal.tsx): Update label for submit button to dynamically change based on the presence of initialData * 🐛 (index.tsx): Fix issue where options were not being correctly memoized as a Set to prevent unnecessary re-renders. Update memoizedOptions to correctly memoize options as a Set. * 📝 (userSettings.spec.ts): Update placeholder text for variable name and value fields to improve clarity and user experience.
This commit is contained in:
parent
a142b45160
commit
af4fb3774e
8 changed files with 327 additions and 194 deletions
|
|
@ -7,17 +7,10 @@ import getUnavailableFields from "@/stores/globalVariablesStore/utils/get-unavai
|
|||
import { GlobalVariable } from "@/types/global_variables";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import ForwardedIconComponent from "@/components/common/genericIconComponent";
|
||||
import { ForwardedIconComponent } from "@/components/common/genericIconComponent";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs-button";
|
||||
import BaseModal from "@/modals/baseModal";
|
||||
import useAlertStore from "@/stores/alertStore";
|
||||
import { useTypesStore } from "@/stores/typesStore";
|
||||
|
|
@ -134,88 +127,89 @@ export default function GlobalVariableModal({
|
|||
onSubmit={submitForm}
|
||||
disable={disabled}
|
||||
>
|
||||
<BaseModal.Header
|
||||
description={
|
||||
"This variable will be available for you to use in any of your projects."
|
||||
}
|
||||
>
|
||||
<span className="pr-2">
|
||||
{" "}
|
||||
{initialData ? "Update" : "Create"} Variable{" "}
|
||||
</span>
|
||||
<BaseModal.Header description="This variable will be available for use across your flows.">
|
||||
<ForwardedIconComponent
|
||||
name="Globe"
|
||||
className="h-6 w-6 pl-1 text-primary"
|
||||
className="h-6 w-6 pr-1 text-primary"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{initialData ? "Update Variable" : "Create Variable"}
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Trigger disable={disabled} asChild={asChild}>
|
||||
{children}
|
||||
</BaseModal.Trigger>
|
||||
<BaseModal.Content>
|
||||
<div className="flex h-full w-full flex-col gap-4 align-middle">
|
||||
<Label>Variable Name</Label>
|
||||
<Input
|
||||
value={key}
|
||||
onChange={(e) => {
|
||||
setKey(e.target.value);
|
||||
}}
|
||||
placeholder="Insert a name for the variable..."
|
||||
></Input>
|
||||
<Label>Type (optional)</Label>
|
||||
|
||||
<Select
|
||||
disabled={disabled}
|
||||
onValueChange={setType}
|
||||
value={type}
|
||||
defaultValue={type}
|
||||
>
|
||||
<SelectTrigger
|
||||
className="h-full w-full"
|
||||
data-testid="select-type-global-variables"
|
||||
<div className="flex h-full w-full flex-col gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Type*</Label>
|
||||
<Tabs
|
||||
defaultValue={type}
|
||||
onValueChange={setType}
|
||||
className="w-full"
|
||||
>
|
||||
<SelectValue placeholder="Choose a type for the variable..." />
|
||||
</SelectTrigger>
|
||||
<SelectContent id="type-global-variables">
|
||||
{["Generic", "Credential"].map((option) => (
|
||||
<SelectItem key={option} value={option}>
|
||||
{option}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger
|
||||
disabled={!!initialData?.type}
|
||||
data-testid="credential-tab"
|
||||
value="Credential"
|
||||
>
|
||||
Credential
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
disabled={!!initialData?.type}
|
||||
data-testid="generic-tab"
|
||||
value="Generic"
|
||||
>
|
||||
Generic
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
<Label>Value</Label>
|
||||
{type === "Credential" ? (
|
||||
<div className="space-y-2">
|
||||
<Label>Name*</Label>
|
||||
<Input
|
||||
value={key}
|
||||
onChange={(e) => setKey(e.target.value)}
|
||||
placeholder="Enter a name for the variable..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Value*</Label>
|
||||
{type === "Credential" ? (
|
||||
<InputComponent
|
||||
password
|
||||
value={value}
|
||||
onChange={(e) => setValue(e)}
|
||||
placeholder="Enter a value for the variable..."
|
||||
nodeStyle
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
placeholder="Enter a value for the variable..."
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Apply to fields</Label>
|
||||
<InputComponent
|
||||
password
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
setValue(e);
|
||||
}}
|
||||
placeholder="Insert a value for the variable..."
|
||||
nodeStyle
|
||||
setSelectedOptions={(value) => setFields(value)}
|
||||
selectedOptions={fields}
|
||||
options={availableFields}
|
||||
password={false}
|
||||
placeholder="Choose a field for the variable..."
|
||||
id="apply-to-fields"
|
||||
popoverWidth="520px"
|
||||
optionsPlaceholder="Fields"
|
||||
/>
|
||||
) : (
|
||||
<Textarea
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value);
|
||||
}}
|
||||
placeholder="Insert a value for the variable..."
|
||||
className="w-full resize-none custom-scroll"
|
||||
/>
|
||||
)}
|
||||
|
||||
<Label>Apply To Fields (optional)</Label>
|
||||
<InputComponent
|
||||
setSelectedOptions={(value) => setFields(value)}
|
||||
selectedOptions={fields}
|
||||
options={availableFields}
|
||||
password={false}
|
||||
placeholder="Choose a field for the variable..."
|
||||
id={"apply-to-fields"}
|
||||
></InputComponent>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Selected fields will auto-apply the variable as a default value.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseModal.Content>
|
||||
<BaseModal.Footer
|
||||
|
|
|
|||
|
|
@ -15,7 +15,129 @@ import {
|
|||
import { cn } from "@/utils/utils";
|
||||
import { PopoverAnchor } from "@radix-ui/react-popover";
|
||||
import { X } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { ReactNode, useMemo, useState } from "react";
|
||||
|
||||
const OptionBadge = ({
|
||||
option,
|
||||
onRemove,
|
||||
variant = "emerald",
|
||||
className = "",
|
||||
}: {
|
||||
option: string;
|
||||
variant?:
|
||||
| "default"
|
||||
| "emerald"
|
||||
| "gray"
|
||||
| "secondary"
|
||||
| "destructive"
|
||||
| "outline"
|
||||
| "secondaryStatic"
|
||||
| "pinkStatic"
|
||||
| "successStatic"
|
||||
| "errorStatic";
|
||||
className?: string;
|
||||
onRemove: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
}) => (
|
||||
<Badge
|
||||
variant={
|
||||
variant as
|
||||
| "default"
|
||||
| "emerald"
|
||||
| "gray"
|
||||
| "secondary"
|
||||
| "destructive"
|
||||
| "outline"
|
||||
| "secondaryStatic"
|
||||
| "pinkStatic"
|
||||
| "successStatic"
|
||||
| "errorStatic"
|
||||
}
|
||||
className={cn("flex items-center gap-1 truncate", className)}
|
||||
>
|
||||
<div className="truncate">{option}</div>
|
||||
<X
|
||||
className="h-3 w-3 cursor-pointer bg-transparent hover:text-destructive"
|
||||
onClick={(e) =>
|
||||
onRemove(e as unknown as React.MouseEvent<HTMLButtonElement>)
|
||||
}
|
||||
data-testid="remove-icon-badge"
|
||||
/>
|
||||
</Badge>
|
||||
);
|
||||
|
||||
const CommandItemContent = ({
|
||||
option,
|
||||
isSelected,
|
||||
optionButton,
|
||||
}: {
|
||||
option: string;
|
||||
isSelected: boolean;
|
||||
optionButton: (option: string) => ReactNode;
|
||||
}) => (
|
||||
<div className="group flex w-full items-center justify-between">
|
||||
<div className="flex items-center justify-between">
|
||||
<SelectionIndicator isSelected={isSelected} />
|
||||
<span className="max-w-52 truncate pr-2">{option}</span>
|
||||
</div>
|
||||
{optionButton && optionButton(option)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const SelectionIndicator = ({ isSelected }: { isSelected: boolean }) => (
|
||||
<div
|
||||
className={cn(
|
||||
"relative mr-2 h-4 w-4",
|
||||
isSelected ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
>
|
||||
<div className="absolute opacity-100 transition-all group-hover:opacity-0">
|
||||
<ForwardedIconComponent
|
||||
name="Check"
|
||||
className="mr-2 h-4 w-4 text-primary"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute opacity-0 transition-all group-hover:opacity-100">
|
||||
<ForwardedIconComponent
|
||||
name="X"
|
||||
className="mr-2 h-4 w-4 text-status-red"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const getInputClassName = (
|
||||
editNode: boolean,
|
||||
disabled: boolean,
|
||||
password: boolean,
|
||||
selectedOptions: string[],
|
||||
) => {
|
||||
return cn(
|
||||
"popover-input nodrag w-full truncate px-1 pr-4",
|
||||
editNode && "px-2",
|
||||
editNode && disabled && "h-fit w-fit",
|
||||
disabled &&
|
||||
"disabled:text-muted disabled:opacity-100 placeholder:disabled:text-muted-foreground",
|
||||
password && "text-clip pr-14",
|
||||
selectedOptions?.length >= 0 && "cursor-default",
|
||||
);
|
||||
};
|
||||
|
||||
const getAnchorClassName = (
|
||||
editNode: boolean,
|
||||
disabled: boolean,
|
||||
isFocused: boolean,
|
||||
) => {
|
||||
return cn(
|
||||
"primary-input noflow nopan nodelete nodrag border-1 flex h-full min-h-[2.375rem] cursor-default flex-wrap items-center px-2",
|
||||
editNode && "min-h-7 p-0 px-1",
|
||||
editNode && disabled && "min-h-5 border-muted",
|
||||
disabled && "bg-muted text-muted",
|
||||
isFocused &&
|
||||
"border-foreground ring-1 ring-foreground hover:border-foreground",
|
||||
);
|
||||
};
|
||||
|
||||
const CustomInputPopover = ({
|
||||
id,
|
||||
|
|
@ -43,16 +165,20 @@ const CustomInputPopover = ({
|
|||
nodeStyle,
|
||||
optionButton,
|
||||
autoFocus,
|
||||
className,
|
||||
popoverWidth,
|
||||
}) => {
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const memoizedOptions = useMemo(() => new Set<string>(options), [options]);
|
||||
|
||||
const PopoverContentInput = editNode
|
||||
? PopoverContent
|
||||
: PopoverContentWithoutPortal;
|
||||
|
||||
const handleRemoveOption = (optionToRemove, e) => {
|
||||
e.stopPropagation(); // Prevent the popover from opening when removing badges
|
||||
const handleRemoveOption = (
|
||||
optionToRemove: string,
|
||||
e: React.MouseEvent<HTMLButtonElement>,
|
||||
) => {
|
||||
e.stopPropagation();
|
||||
if (setSelectedOptions) {
|
||||
setSelectedOptions(
|
||||
selectedOptions.filter((option) => option !== optionToRemove),
|
||||
|
|
@ -62,58 +188,52 @@ const CustomInputPopover = ({
|
|||
}
|
||||
};
|
||||
|
||||
const handleOptionSelect = (currentValue: string) => {
|
||||
if (setSelectedOption) {
|
||||
setSelectedOption(currentValue === selectedOption ? "" : currentValue);
|
||||
}
|
||||
if (setSelectedOptions) {
|
||||
setSelectedOptions(
|
||||
selectedOptions?.includes(currentValue)
|
||||
? selectedOptions.filter((item) => item !== currentValue)
|
||||
: [...(selectedOptions || []), currentValue],
|
||||
);
|
||||
}
|
||||
!setSelectedOptions && setShowOptions(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover modal open={showOptions} onOpenChange={setShowOptions}>
|
||||
<PopoverAnchor>
|
||||
<div
|
||||
data-testid={`anchor-${id}`}
|
||||
className={cn(
|
||||
"primary-input noflow nopan nodelete nodrag border-1 flex h-full min-h-[2.375rem] cursor-default flex-wrap items-center px-2",
|
||||
editNode && "min-h-7 p-0 px-1",
|
||||
editNode && disabled && "min-h-5 border-muted",
|
||||
disabled && "bg-muted text-muted",
|
||||
isFocused &&
|
||||
"border-foreground ring-1 ring-foreground hover:border-foreground",
|
||||
)}
|
||||
onClick={() => {
|
||||
if (!nodeStyle && !disabled) {
|
||||
setShowOptions(true);
|
||||
}
|
||||
}}
|
||||
className={getAnchorClassName(editNode, disabled, isFocused)}
|
||||
onClick={() => !nodeStyle && !disabled && setShowOptions(true)}
|
||||
>
|
||||
{selectedOptions?.length > 0 ? (
|
||||
selectedOptions.map((option) => (
|
||||
<Badge
|
||||
key={option}
|
||||
variant="secondary"
|
||||
className="m-[1px] flex items-center gap-1 truncate px-1"
|
||||
>
|
||||
<div className="truncate">{option}</div>
|
||||
<X
|
||||
className="h-3 w-3 cursor-pointer bg-transparent hover:text-destructive"
|
||||
onClick={(e) => handleRemoveOption(option, e)}
|
||||
<div className="mr-5 flex flex-wrap gap-2">
|
||||
{selectedOptions.map((option) => (
|
||||
<OptionBadge
|
||||
key={option}
|
||||
option={option}
|
||||
onRemove={(e) => handleRemoveOption(option, e)}
|
||||
className="rounded-[3px] p-1 font-mono"
|
||||
/>
|
||||
</Badge>
|
||||
))
|
||||
))}
|
||||
</div>
|
||||
) : selectedOption?.length > 0 ? (
|
||||
<Badge
|
||||
<OptionBadge
|
||||
option={selectedOption}
|
||||
onRemove={(e) => handleRemoveOption(selectedOption, e)}
|
||||
variant={nodeStyle ? "emerald" : "secondary"}
|
||||
className={cn(
|
||||
"flex items-center gap-1 truncate",
|
||||
editNode && "text-xs",
|
||||
nodeStyle ? "font-jetbrains rounded-[3px] px-1" : "bg-muted",
|
||||
nodeStyle ? "rounded-[3px] px-1 font-mono" : "bg-muted",
|
||||
)}
|
||||
>
|
||||
<div className="max-w-36 truncate">{selectedOption}</div>
|
||||
<X
|
||||
className="h-3 w-3 cursor-pointer bg-transparent hover:text-destructive"
|
||||
onClick={(e) => handleRemoveOption(selectedOption, e)}
|
||||
data-testid={"remove-icon-badge"}
|
||||
/>
|
||||
</Badge>
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{!selectedOption && (
|
||||
{!selectedOption?.length && !selectedOptions?.length && (
|
||||
<input
|
||||
autoComplete="off"
|
||||
onFocus={() => setIsFocused(true)}
|
||||
|
|
@ -128,13 +248,11 @@ const CustomInputPopover = ({
|
|||
value={value || ""}
|
||||
disabled={disabled}
|
||||
required={required}
|
||||
className={cn(
|
||||
"popover-input nodrag w-full truncate px-1 pr-4",
|
||||
editNode && "px-2",
|
||||
editNode && disabled && "h-fit w-fit",
|
||||
disabled &&
|
||||
"disabled:text-muted disabled:opacity-100 placeholder:disabled:text-muted-foreground",
|
||||
password && "text-clip pr-14",
|
||||
className={getInputClassName(
|
||||
editNode,
|
||||
disabled,
|
||||
password,
|
||||
selectedOptions,
|
||||
)}
|
||||
placeholder={
|
||||
selectedOptions?.length > 0 || selectedOption ? "" : placeholder
|
||||
|
|
@ -149,9 +267,13 @@ const CustomInputPopover = ({
|
|||
)}
|
||||
</div>
|
||||
</PopoverAnchor>
|
||||
|
||||
<PopoverContentInput
|
||||
className="noflow nowheel nopan nodelete nodrag p-0"
|
||||
style={{ minWidth: refInput?.current?.clientWidth ?? "200px" }}
|
||||
style={{
|
||||
minWidth: refInput?.current?.clientWidth ?? "200px",
|
||||
width: popoverWidth ?? null,
|
||||
}}
|
||||
side="bottom"
|
||||
align="start"
|
||||
>
|
||||
|
|
@ -168,59 +290,21 @@ const CustomInputPopover = ({
|
|||
<CommandInput placeholder={optionsPlaceholder} />
|
||||
<CommandList>
|
||||
<CommandGroup>
|
||||
{options.map((option, id) => (
|
||||
{Array.from(memoizedOptions).map((option, id) => (
|
||||
<CommandItem
|
||||
key={option + id}
|
||||
value={option}
|
||||
onSelect={(currentValue) => {
|
||||
if (setSelectedOption) {
|
||||
setSelectedOption(
|
||||
currentValue === selectedOption ? "" : currentValue,
|
||||
);
|
||||
}
|
||||
if (setSelectedOptions) {
|
||||
setSelectedOptions(
|
||||
selectedOptions?.includes(currentValue)
|
||||
? selectedOptions.filter(
|
||||
(item) => item !== currentValue,
|
||||
)
|
||||
: [...(selectedOptions || []), currentValue],
|
||||
);
|
||||
}
|
||||
!setSelectedOptions && setShowOptions(false);
|
||||
}}
|
||||
onSelect={handleOptionSelect}
|
||||
className="group"
|
||||
>
|
||||
<div className="group flex w-full items-center justify-between">
|
||||
<div className="flex items-center justify-between">
|
||||
<div
|
||||
className={cn(
|
||||
"relative mr-2 h-4 w-4",
|
||||
selectedOption === option ||
|
||||
selectedOptions?.includes(option)
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
)}
|
||||
>
|
||||
<div className="absolute opacity-100 transition-all group-hover:opacity-0">
|
||||
<ForwardedIconComponent
|
||||
name="Check"
|
||||
className="mr-2 h-4 w-4 text-primary"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute opacity-0 transition-all group-hover:opacity-100">
|
||||
<ForwardedIconComponent
|
||||
name="X"
|
||||
className="mr-2 h-4 w-4 text-status-red"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span className="max-w-52 truncate pr-2">{option}</span>
|
||||
</div>
|
||||
{optionButton && optionButton(option)}
|
||||
</div>
|
||||
<CommandItemContent
|
||||
option={option}
|
||||
isSelected={
|
||||
selectedOption === option ||
|
||||
selectedOptions?.includes(option)
|
||||
}
|
||||
optionButton={optionButton}
|
||||
/>
|
||||
</CommandItem>
|
||||
))}
|
||||
{optionsButton}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ export default function InputComponent({
|
|||
onChangeFolderName,
|
||||
nodeStyle,
|
||||
isToolMode,
|
||||
popoverWidth,
|
||||
}: InputComponentType): JSX.Element {
|
||||
const [pwdVisible, setPwdVisible] = useState(false);
|
||||
const refInput = useRef<HTMLInputElement>(null);
|
||||
|
|
@ -148,8 +149,8 @@ export default function InputComponent({
|
|||
blurOnEnter={blurOnEnter}
|
||||
options={options}
|
||||
optionsPlaceholder={optionsPlaceholder}
|
||||
className={className}
|
||||
nodeStyle={nodeStyle}
|
||||
popoverWidth={popoverWidth}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ export default function InputGlobalComponent({
|
|||
return (
|
||||
<InputComponent
|
||||
nodeStyle
|
||||
popoverWidth="315px"
|
||||
placeholder={getPlaceholder(disabled, placeholder)}
|
||||
id={id}
|
||||
editNode={editNode}
|
||||
|
|
|
|||
54
src/frontend/src/components/ui/tabs-button.tsx
Normal file
54
src/frontend/src/components/ui/tabs-button.tsx
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
"use client";
|
||||
|
||||
import { cn } from "@/utils/utils";
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
||||
import * as React from "react";
|
||||
|
||||
const Tabs = TabsPrimitive.Root;
|
||||
|
||||
const TabsList = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsList.displayName = TabsPrimitive.List.displayName;
|
||||
|
||||
const TabsTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
||||
|
||||
const TabsContent = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
||||
|
||||
export { Tabs, TabsContent, TabsList, TabsTrigger };
|
||||
|
|
@ -46,6 +46,7 @@ export type InputComponentType = {
|
|||
onChangeFolderName?: (e: any) => void;
|
||||
nodeStyle?: boolean;
|
||||
isToolMode?: boolean;
|
||||
popoverWidth?: string;
|
||||
};
|
||||
export type DropDownComponent = {
|
||||
disabled?: boolean;
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@ test(
|
|||
await page.getByTestId("icon-Globe").nth(0).click();
|
||||
await page.getByText("Add New Variable", { exact: true }).click();
|
||||
await page
|
||||
.getByPlaceholder("Insert a name for the variable...")
|
||||
.getByPlaceholder("Enter a name for the variable...")
|
||||
.fill(genericName);
|
||||
await page.getByText("Generic", { exact: true }).first().isVisible();
|
||||
await page
|
||||
.getByPlaceholder("Insert a value for the variable...")
|
||||
.getByPlaceholder("Enter a value for the variable...")
|
||||
.fill("This is a test of generic variable value");
|
||||
await page.getByText("Save Variable", { exact: true }).click();
|
||||
expect(page.getByText(genericName, { exact: true })).not.toBeNull();
|
||||
|
|
@ -45,12 +45,11 @@ test(
|
|||
|
||||
await page.getByText("Add New Variable", { exact: true }).click();
|
||||
await page
|
||||
.getByPlaceholder("Insert a name for the variable...")
|
||||
.getByPlaceholder("Enter a name for the variable...")
|
||||
.fill(credentialName);
|
||||
await page.getByTestId("select-type-global-variables").first().click();
|
||||
await page.getByText("Credential", { exact: true }).last().click();
|
||||
await page.getByTestId("credential-tab").click();
|
||||
await page
|
||||
.getByPlaceholder("Insert a value for the variable...")
|
||||
.getByPlaceholder("Enter a value for the variable...")
|
||||
.fill("This is a test of credential variable value");
|
||||
await page.getByText("Save Variable", { exact: true }).click();
|
||||
expect(page.getByText(credentialName, { exact: true })).not.toBeNull();
|
||||
|
|
|
|||
|
|
@ -44,40 +44,39 @@ test(
|
|||
.isVisible();
|
||||
await page.getByText("Add New").click();
|
||||
await page
|
||||
.getByPlaceholder("Insert a name for the variable...")
|
||||
.getByPlaceholder("Enter a name for the variable...")
|
||||
.fill(randomName);
|
||||
await page.getByTestId("select-type-global-variables").first().click();
|
||||
await page.getByText("Generic", { exact: true }).last().isVisible();
|
||||
await page.getByText("Generic", { exact: true }).last().click();
|
||||
|
||||
await page
|
||||
.getByPlaceholder("Insert a value for the variable...")
|
||||
.getByPlaceholder("Enter a value for the variable...")
|
||||
.fill("testtesttesttesttesttesttesttest");
|
||||
await page.getByTestId("popover-anchor-apply-to-fields").click();
|
||||
|
||||
await page.getByPlaceholder("Search options...").waitFor({
|
||||
await page.getByPlaceholder("Fields").waitFor({
|
||||
state: "visible",
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
await page.getByPlaceholder("Search options...").fill("System");
|
||||
await page.getByPlaceholder("Fields").fill("System");
|
||||
|
||||
await page.waitForSelector("text=System", { timeout: 30000 });
|
||||
|
||||
await page.getByText("System").last().click();
|
||||
|
||||
await page.getByPlaceholder("Search options...").fill("openAI");
|
||||
await page.getByPlaceholder("Fields").fill("openAI");
|
||||
|
||||
await page.waitForSelector("text=openai", { timeout: 30000 });
|
||||
|
||||
await page.getByText("openai").last().click();
|
||||
|
||||
await page.getByPlaceholder("Search options...").waitFor({
|
||||
await page.getByPlaceholder("Fields").waitFor({
|
||||
state: "visible",
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
await page.getByPlaceholder("Search options...").fill("ollama");
|
||||
await page.getByPlaceholder("Fields").fill("ollama");
|
||||
|
||||
await page.keyboard.press("Escape");
|
||||
await page.getByText("Save Variable", { exact: true }).click();
|
||||
|
|
@ -87,13 +86,13 @@ test(
|
|||
await page.getByText(randomName).last().click();
|
||||
await page.getByText(randomName).last().click();
|
||||
|
||||
await page.getByPlaceholder("Insert a name for the variable...").waitFor({
|
||||
await page.getByPlaceholder("Enter a name for the variable...").waitFor({
|
||||
state: "visible",
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
await page
|
||||
.getByPlaceholder("Insert a name for the variable...")
|
||||
.getByPlaceholder("Enter a name for the variable...")
|
||||
.fill(randomName2);
|
||||
|
||||
await page.getByText("Update Variable", { exact: true }).last().click();
|
||||
|
|
@ -102,13 +101,13 @@ test(
|
|||
|
||||
await page.getByText(randomName2).last().click();
|
||||
|
||||
await page.getByPlaceholder("Insert a name for the variable...").waitFor({
|
||||
await page.getByPlaceholder("Enter a name for the variable...").waitFor({
|
||||
state: "visible",
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
await page
|
||||
.getByPlaceholder("Insert a name for the variable...")
|
||||
.getByPlaceholder("Enter a name for the variable...")
|
||||
.fill(randomName3);
|
||||
|
||||
await page.getByText("Update Variable", { exact: true }).last().click();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue