fix: Enhance Growth UI Layout and Consistency (#7768)
This commit is contained in:
parent
f213f487a6
commit
60de34074b
32 changed files with 260 additions and 323 deletions
|
|
@ -1,51 +0,0 @@
|
|||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# Concise Task Management Protocol - Sequential Mode
|
||||
|
||||
## File Creation
|
||||
- Filename: `YYYYMMDD_HHMMSS_task_name.md`
|
||||
- IMPORTANT: Always include a descriptive task name after the timestamp (e.g., `20250404_135621_api_integration.md`)
|
||||
- Never create files with timestamp only
|
||||
- Store in `tasks/` directory
|
||||
|
||||
## Task Structure
|
||||
|
||||
```
|
||||
# Task: [Task Name]
|
||||
**Status**: [Not Started | In Progress | Completed]
|
||||
|
||||
## Analysis
|
||||
- [ ] Requirements
|
||||
- [ ] Subtask 1
|
||||
- [ ] Challenges
|
||||
- [ ] Dependencies
|
||||
|
||||
## Plan
|
||||
- [ ] Step 1: [Description]
|
||||
- [ ] Subtask 1.1
|
||||
- [ ] Step 2: [Description]
|
||||
|
||||
## Execution
|
||||
- [ ] Implementation 1
|
||||
- [ ] Details
|
||||
- [ ] Implementation 2
|
||||
- [ ] Files modified: [files]
|
||||
|
||||
## Summary
|
||||
- [ ] Files modified: `filename.ext` (lines X-Y)
|
||||
- [ ] Dependencies added/changed
|
||||
- [ ] Edge cases considered
|
||||
- [ ] Known limitations
|
||||
- [ ] Future impact points
|
||||
```
|
||||
|
||||
## Execution Rules
|
||||
- Execute one subtask at a time in sequence
|
||||
- Update the task file after EACH subtask is completed
|
||||
- Mark completed subtasks with [x] as they are finished
|
||||
- Update main task status throughout execution
|
||||
- Document work incrementally, not all at once
|
||||
- Never proceed to the next section until all subtasks in the current section are completed and marked
|
||||
|
|
@ -53,7 +53,7 @@ const AlertDropdown = forwardRef<HTMLDivElement, AlertDropdownType>(
|
|||
Notifications
|
||||
<div className="flex gap-3 pr-3">
|
||||
<button
|
||||
className="text-foreground hover:text-status-red"
|
||||
className="text-muted-foreground hover:text-status-red"
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
setTimeout(clearNotificationList, 100);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export default function PageLayout({
|
|||
onClick={() => {
|
||||
navigate(backTo);
|
||||
}}
|
||||
data-testid="back_page_button"
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="ChevronLeft"
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export default function RenderIcons({
|
|||
<span
|
||||
className={cn(
|
||||
"flex items-center gap-0.5",
|
||||
tableRender ? "justify-start" : "justify-center text-[12px]",
|
||||
tableRender ? "justify-start" : "justify-center text-xs",
|
||||
)}
|
||||
>
|
||||
{shortcutList.map((key, index) => (
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "../../ui/tooltip";
|
|||
|
||||
// Extract static styles
|
||||
const BASE_TOOLTIP_CLASSES =
|
||||
"z-[99] max-w-96 bg-tooltip text-[12px] text-tooltip-foreground";
|
||||
"z-[99] max-w-96 bg-tooltip text-xs text-tooltip-foreground";
|
||||
|
||||
// Memoize the tooltip content component
|
||||
const MemoizedTooltipContent = memo(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { usePostLikeComponent } from "@/controllers/API/queries/store";
|
|||
import { useState } from "react";
|
||||
import { getComponent } from "../../../controllers/API";
|
||||
import useAlertStore from "../../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
|
||||
import { useStoreStore } from "../../../stores/storeStore";
|
||||
import { FlowType } from "../../../types/flow";
|
||||
import { storeComponent } from "../../../types/store";
|
||||
|
|
@ -42,11 +41,6 @@ export default function StoreCardComponent({
|
|||
const [downloadsCount, setDownloadsCount] = useState(
|
||||
data?.downloads_count ?? 0,
|
||||
);
|
||||
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
|
||||
// const [openPlayground, setOpenPlayground] = useState(false);
|
||||
const [loadingPlayground, setLoadingPlayground] = useState(false);
|
||||
const playground =
|
||||
data.last_tested_version?.includes("1.0.0") && !data.is_component;
|
||||
|
||||
const name = data.is_component ? "Component" : "Flow";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { ForwardedIconComponent } from "@/components/common/genericIconComponent";
|
||||
import {
|
||||
DATASTAX_DOCS_URL,
|
||||
DISCORD_URL,
|
||||
DOCS_URL,
|
||||
GITHUB_URL,
|
||||
TWITTER_URL,
|
||||
} from "@/constants/constants";
|
||||
import { useLogout } from "@/controllers/API/queries/auth";
|
||||
import { ENABLE_DATASTAX_LANGFLOW } from "@/customization/feature-flags";
|
||||
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
||||
import useAuthStore from "@/stores/authStore";
|
||||
import { useDarkStore } from "@/stores/darkStore";
|
||||
|
|
@ -45,7 +47,7 @@ export const AccountMenu = () => {
|
|||
<HeaderMenu>
|
||||
<HeaderMenuToggle>
|
||||
<div
|
||||
className="h-7 w-7 rounded-lg focus-visible:outline-0"
|
||||
className="h-6 w-6 rounded-lg focus-visible:outline-0"
|
||||
data-testid="user-profile-settings"
|
||||
>
|
||||
<ProfileIcon />
|
||||
|
|
@ -107,7 +109,10 @@ export const AccountMenu = () => {
|
|||
</HeaderMenuItemButton>
|
||||
</div>
|
||||
)}
|
||||
<HeaderMenuItemLink newPage href={DOCS_URL}>
|
||||
<HeaderMenuItemLink
|
||||
newPage
|
||||
href={ENABLE_DATASTAX_LANGFLOW ? DATASTAX_DOCS_URL : DOCS_URL}
|
||||
>
|
||||
<span data-testid="menu_docs_button" id="menu_docs_button">
|
||||
Docs
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
[currentFlowName],
|
||||
);
|
||||
|
||||
const handleNameSubmit = useCallback(() => {
|
||||
const handleNameSubmit = useCallback(async () => {
|
||||
if (
|
||||
flowName.trim() !== "" &&
|
||||
flowName !== currentFlowName &&
|
||||
|
|
@ -203,9 +203,10 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
name: flowName,
|
||||
id: currentFlowId!,
|
||||
};
|
||||
setCurrentFlow(newFlow);
|
||||
|
||||
saveFlow(newFlow)
|
||||
.then(() => {
|
||||
setCurrentFlow(newFlow);
|
||||
setSuccessData({ title: "Flow name updated successfully" });
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
@ -268,7 +269,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
{currentFolder?.name && (
|
||||
<div className="hidden truncate md:flex">
|
||||
<div
|
||||
className="cursor-pointer truncate pr-1 text-muted-foreground hover:text-primary"
|
||||
className="cursor-pointer truncate pr-1 text-xs text-muted-foreground hover:text-primary"
|
||||
onClick={() => {
|
||||
navigate(
|
||||
currentFolder?.id
|
||||
|
|
@ -313,7 +314,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
>
|
||||
<Input
|
||||
className={cn(
|
||||
"h-6 w-full shrink-0 cursor-text font-semibold",
|
||||
"h-6 w-full shrink-0 cursor-text text-xs font-semibold",
|
||||
"bg-transparent pl-1 pr-0 transition-colors duration-200",
|
||||
"border-0 outline-none focus:border-0 focus:outline-none focus:ring-0 focus:ring-offset-0",
|
||||
!editingName && "text-primary hover:opacity-80",
|
||||
|
|
@ -334,7 +335,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
/>
|
||||
<span
|
||||
ref={measureRef}
|
||||
className="invisible absolute left-0 top-0 -z-10 w-fit whitespace-pre font-semibold"
|
||||
className="invisible absolute left-0 top-0 -z-10 w-fit whitespace-pre text-xs font-semibold"
|
||||
aria-hidden="true"
|
||||
data-testid="flow_name"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -16,13 +16,15 @@ export const HeaderMenu = ({ children }) => (
|
|||
|
||||
export const HeaderMenuToggle = ({ children }) => (
|
||||
<DropdownMenuTrigger
|
||||
className="group inline-flex w-full items-center justify-center gap-1 rounded-md pr-0"
|
||||
className="inline-flex w-full items-center justify-center rounded-md pl-4 pr-1"
|
||||
data-testid="user_menu_button"
|
||||
id="user_menu_button"
|
||||
>
|
||||
<div className="flex items-center gap-1 rounded-lg px-2 py-1.5 group-hover:bg-muted">
|
||||
{children}
|
||||
<ChevronsUpDown className="h-4 w-4 text-muted-foreground group-hover:text-foreground" />
|
||||
<div className="group flex items-center self-center rounded-md">
|
||||
<div className="flex h-6 w-10 items-center justify-center rounded-full bg-background transition-colors hover:bg-muted group-hover:bg-muted">
|
||||
<div className="relative right-1 z-10">{children}</div>
|
||||
<ChevronsUpDown className="relative h-[14px] w-[14px] text-muted-foreground group-hover:text-primary" />
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export function ProfileIcon() {
|
|||
return (
|
||||
<img
|
||||
src={profileImageUrl}
|
||||
className="h-7 w-7 shrink-0 focus-visible:outline-0"
|
||||
className="h-6 w-6 shrink-0 focus-visible:outline-0"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ export const LangflowCounts = () => {
|
|||
const discordCount: number = useDarkStore((state) => state.discordCount);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="flex items-center gap-3">
|
||||
<ShadTooltip
|
||||
content="Go to GitHub repo"
|
||||
side="bottom"
|
||||
styleClasses="z-10"
|
||||
>
|
||||
<div className="flex items-center gap-2 text-muted-foreground hover:text-foreground">
|
||||
<div className="hit-area-hover flex items-center gap-2 rounded-md p-1 text-muted-foreground">
|
||||
<FaGithub className="h-4 w-4" />
|
||||
<span className="text-xs font-semibold">{formatNumber(stars)}</span>
|
||||
</div>
|
||||
|
|
@ -25,7 +25,7 @@ export const LangflowCounts = () => {
|
|||
side="bottom"
|
||||
styleClasses="z-10"
|
||||
>
|
||||
<div className="flex items-center gap-2 text-muted-foreground hover:text-foreground">
|
||||
<div className="hit-area-hover flex items-center gap-2 rounded-md p-1 text-muted-foreground">
|
||||
<FaDiscord className="h-4 w-4" />
|
||||
<span className="text-xs font-semibold">
|
||||
{formatNumber(discordCount)}
|
||||
|
|
|
|||
|
|
@ -54,9 +54,16 @@ export default function AppHeader(): JSX.Element {
|
|||
|
||||
const isEmpty = flows?.length !== examples?.length || folders?.length > 1;
|
||||
|
||||
const getNotificationBadge = () => {
|
||||
const baseClasses = "absolute h-1 w-1 rounded-full bg-destructive";
|
||||
return notificationCenter
|
||||
? `${baseClasses} right-[5.1rem] top-[10px]`
|
||||
: "hidden";
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex h-[44px] w-full items-center justify-between border-b p-6 dark:bg-background ${
|
||||
className={`flex h-[48px] w-full items-center justify-between border-b px-6 dark:bg-background ${
|
||||
!isEmpty ? "hidden" : ""
|
||||
}`}
|
||||
data-testid="app-header"
|
||||
|
|
@ -75,7 +82,7 @@ export default function AppHeader(): JSX.Element {
|
|||
{ENABLE_DATASTAX_LANGFLOW ? (
|
||||
<DataStaxLogo className="fill-black dark:fill-[white]" />
|
||||
) : (
|
||||
<LangflowLogo className="h-5 w-6" />
|
||||
<LangflowLogo className="h-6 w-6" />
|
||||
)}
|
||||
</Button>
|
||||
{ENABLE_DATASTAX_LANGFLOW && (
|
||||
|
|
@ -93,22 +100,20 @@ export default function AppHeader(): JSX.Element {
|
|||
|
||||
{/* Right Section */}
|
||||
<div
|
||||
className={`relative left-3 z-30 flex items-center gap-2`}
|
||||
className={`relative left-3 z-30 flex items-center gap-1`}
|
||||
data-testid="header_right_section_wrapper"
|
||||
>
|
||||
{!ENABLE_DATASTAX_LANGFLOW && (
|
||||
<>
|
||||
<Button
|
||||
unstyled
|
||||
className="hidden items-center whitespace-nowrap pr-2 2xl:inline"
|
||||
onClick={() =>
|
||||
window.open("https://github.com/langflow-ai/langflow", "_blank")
|
||||
}
|
||||
>
|
||||
<LangflowCounts />
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<>
|
||||
<Button
|
||||
unstyled
|
||||
className="hidden items-center whitespace-nowrap pr-2 lg:inline"
|
||||
onClick={() =>
|
||||
window.open("https://github.com/langflow-ai/langflow", "_blank")
|
||||
}
|
||||
>
|
||||
<LangflowCounts />
|
||||
</Button>
|
||||
</>
|
||||
<AlertDropdown
|
||||
notificationRef={notificationContentRef}
|
||||
onClose={() => setActiveState(null)}
|
||||
|
|
@ -129,19 +134,21 @@ export default function AppHeader(): JSX.Element {
|
|||
}
|
||||
data-testid="notification_button"
|
||||
>
|
||||
<span
|
||||
className={
|
||||
notificationCenter
|
||||
? `absolute right-[5.3rem] top-[10px] h-1 w-1 rounded-full bg-destructive`
|
||||
: "hidden"
|
||||
}
|
||||
/>
|
||||
<ForwardedIconComponent
|
||||
name="Bell"
|
||||
className="side-bar-button-size ml-1 h-4 w-4 text-muted-foreground"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<span className="hidden whitespace-nowrap">Notifications</span>
|
||||
<div className="hit-area-hover group items-center rounded-md px-2 py-1 text-muted-foreground">
|
||||
<span className={getNotificationBadge()} />
|
||||
<ForwardedIconComponent
|
||||
name="Bell"
|
||||
className={`side-bar-button-size h-4 w-4 ${
|
||||
activeState === "notifications"
|
||||
? "text-primary"
|
||||
: "text-muted-foreground group-hover:text-primary"
|
||||
}`}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<span className="hidden whitespace-nowrap">
|
||||
Notifications
|
||||
</span>
|
||||
</div>
|
||||
</Button>
|
||||
</AlertDropdown>
|
||||
</ShadTooltip>
|
||||
|
|
@ -150,50 +157,7 @@ export default function AppHeader(): JSX.Element {
|
|||
orientation="vertical"
|
||||
className="my-auto ml-3 h-7 dark:border-zinc-700"
|
||||
/>
|
||||
{ENABLE_DATASTAX_LANGFLOW && (
|
||||
<>
|
||||
<ShadTooltip content="Docs" side="bottom" styleClasses="z-10">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="flex text-sm font-medium"
|
||||
onClick={() =>
|
||||
window.open(
|
||||
"https://docs.datastax.com/en/langflow/index.html",
|
||||
"_blank",
|
||||
)
|
||||
}
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="book-open-text"
|
||||
className="side-bar-button-size h-[18px] w-[18px]"
|
||||
/>
|
||||
<span className="hidden whitespace-nowrap 2xl:inline">
|
||||
Docs
|
||||
</span>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
<ShadTooltip content="Settings" side="bottom" styleClasses="z-10">
|
||||
<Button
|
||||
data-testid="user-profile-settings"
|
||||
variant="ghost"
|
||||
className="flex text-sm font-medium"
|
||||
onClick={() => navigate("/settings")}
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="Settings"
|
||||
className="side-bar-button-size h-[18px] w-[18px]"
|
||||
/>
|
||||
<span className="hidden whitespace-nowrap 2xl:inline">
|
||||
Settings
|
||||
</span>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
<Separator
|
||||
orientation="vertical"
|
||||
className="my-auto h-7 dark:border-zinc-700"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="flex">
|
||||
<AccountMenu />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -82,9 +82,17 @@ export const GetStartedProgress: FC<{
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="h-[180px] w-full">
|
||||
<div className="mt-3 h-[180px] w-full">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="font-[14px]">Get started</span>
|
||||
<span className="text-sm font-semibold">
|
||||
{percentageGetStarted >= 100 ? (
|
||||
<>
|
||||
<span>All Set</span> <span className="pl-1"> 🎉 </span>
|
||||
</>
|
||||
) : (
|
||||
"Get started"
|
||||
)}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => handleUserTrack("dialog_dismissed")}
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
|
|
@ -94,7 +102,7 @@ export const GetStartedProgress: FC<{
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mb-1 flex items-center justify-between gap-3">
|
||||
<div className="mb-1 mt-2 flex items-center justify-between gap-3">
|
||||
<div className="h-1 w-full rounded-full bg-muted">
|
||||
<div
|
||||
className="h-1 w-[33%] rounded-full bg-accent-pink-foreground"
|
||||
|
|
@ -143,7 +151,7 @@ export const GetStartedProgress: FC<{
|
|||
)}
|
||||
<span
|
||||
className={cn(
|
||||
"text-sm",
|
||||
"text-xs",
|
||||
isGithubStarredChild && "text-muted-foreground line-through",
|
||||
)}
|
||||
>
|
||||
|
|
@ -185,7 +193,7 @@ export const GetStartedProgress: FC<{
|
|||
)}
|
||||
<span
|
||||
className={cn(
|
||||
"text-sm",
|
||||
"text-xs",
|
||||
isDiscordJoinedChild && "text-muted-foreground line-through",
|
||||
)}
|
||||
>
|
||||
|
|
@ -215,7 +223,7 @@ export const GetStartedProgress: FC<{
|
|||
)}
|
||||
/>
|
||||
</span>
|
||||
<span className={cn("text-sm", hasFlows && "line-through")}>
|
||||
<span className={cn("text-xs", hasFlows && "line-through")}>
|
||||
Create a flow
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export const HeaderButtons = ({
|
|||
handleDismissDialog={handleDismissDialog}
|
||||
/>
|
||||
|
||||
<div className="-mx-4 mt-4 w-[280px]">
|
||||
<div className="-mx-4 mt-1 w-[280px]">
|
||||
<hr className="border-t-1 w-full" />
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@ import {
|
|||
import { useGetDownloadFolders } from "@/controllers/API/queries/folders/use-get-download-folders";
|
||||
import {
|
||||
ENABLE_CUSTOM_PARAM,
|
||||
ENABLE_DATASTAX_LANGFLOW,
|
||||
ENABLE_FILE_MANAGEMENT,
|
||||
} from "@/customization/feature-flags";
|
||||
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
|
||||
import { track } from "@/customization/utils/analytics";
|
||||
import { createFileUpload } from "@/helpers/create-file-upload";
|
||||
import { getObjectsFromFilelist } from "@/helpers/get-objects-from-filelist";
|
||||
|
|
@ -56,6 +58,8 @@ const SideBarFoldersButtonsComponent = ({
|
|||
const loading = !folders;
|
||||
const refInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
const navigate = useCustomNavigate();
|
||||
|
||||
const currentFolder = pathname.split("/");
|
||||
const urlWithoutPath =
|
||||
pathname.split("/").length < (ENABLE_CUSTOM_PARAM ? 5 : 4);
|
||||
|
|
@ -348,7 +352,7 @@ const SideBarFoldersButtonsComponent = ({
|
|||
collapsible={isMobile ? "offcanvas" : "none"}
|
||||
data-testid="folder-sidebar"
|
||||
>
|
||||
<SidebarHeader className="p-4">
|
||||
<SidebarHeader className="px-4 py-1">
|
||||
<HeaderButtons
|
||||
handleUploadFlowsToFolder={handleUploadFlowsToFolder}
|
||||
isUpdatingFolder={isUpdatingFolder}
|
||||
|
|
@ -447,7 +451,24 @@ const SideBarFoldersButtonsComponent = ({
|
|||
</SidebarContent>
|
||||
{ENABLE_FILE_MANAGEMENT && (
|
||||
<SidebarFooter className="border-t">
|
||||
<div className="flex w-full items-center gap-2 p-2">
|
||||
<div className="grid w-full items-center gap-2 p-2">
|
||||
{!ENABLE_DATASTAX_LANGFLOW && (
|
||||
<div
|
||||
className="flex w-full items-center"
|
||||
data-testid="button-store"
|
||||
>
|
||||
<SidebarMenuButton
|
||||
size="md"
|
||||
className="text-[13px]"
|
||||
onClick={() => {
|
||||
window.open("/store", "_blank");
|
||||
}}
|
||||
>
|
||||
<ForwardedIconComponent name="Store" />
|
||||
Store
|
||||
</SidebarMenuButton>
|
||||
</div>
|
||||
)}
|
||||
<SidebarMenuButton
|
||||
isActive={checkPathFiles}
|
||||
onClick={() => handleFilesClick?.()}
|
||||
|
|
|
|||
|
|
@ -1073,5 +1073,7 @@ export const DISCORD_URL = "https://discord.com/invite/EqksyE2EX9";
|
|||
export const GITHUB_URL = "https://github.com/langflow-ai/langflow";
|
||||
export const TWITTER_URL = "https://x.com/langflow_ai";
|
||||
export const DOCS_URL = "https://docs.langflow.org";
|
||||
export const DATASTAX_DOCS_URL =
|
||||
"https://docs.datastax.com/en/langflow/index.html";
|
||||
|
||||
export const UUID_PARSING_ERROR = "uuid_parsing";
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ const ButtonSendWrapper = ({
|
|||
<Case condition={showStopButton}>
|
||||
<div className="flex items-center gap-2 rounded-md text-[14px] font-medium">
|
||||
Stop
|
||||
<Loading className="h-[16px] w-[16px]" />
|
||||
<Loading className="h-4 w-4" />
|
||||
</div>
|
||||
</Case>
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ const NoInputView: React.FC<NoInputViewProps> = ({
|
|||
>
|
||||
<div className="flex items-center gap-2 rounded-md text-[14px] font-medium">
|
||||
Stop
|
||||
<Loading className="h-[16px] w-[16px]" />
|
||||
<Loading className="h-4 w-4" />
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export default function ShortcutDisplay({
|
|||
{display_name && <span> {display_name} </span>}
|
||||
<span
|
||||
className={cn(
|
||||
"flex h-[16px] w-[16px] items-center justify-center rounded-sm bg-muted text-muted-foreground",
|
||||
"flex h-4 w-4 items-center justify-center rounded-sm bg-muted text-muted-foreground",
|
||||
display_name && "ml-3",
|
||||
)}
|
||||
>
|
||||
|
|
@ -26,7 +26,7 @@ export default function ShortcutDisplay({
|
|||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex content-center items-center justify-center self-center text-[12px]">
|
||||
<div className="flex content-center items-center justify-center self-center text-xs">
|
||||
<span> {display_name} </span>
|
||||
<span
|
||||
className={`ml-3 flex items-center rounded-sm bg-primary-hover px-1.5 py-[0.1em] text-muted`}
|
||||
|
|
|
|||
|
|
@ -15,15 +15,9 @@ export default function ToolbarSelectItem({
|
|||
<div className={`flex ${style}`} data-testid={dataTestId}>
|
||||
<ForwardedIconComponent
|
||||
name={icon}
|
||||
className={`mr-2 ${
|
||||
icon === "Share3"
|
||||
? "absolute left-2 top-[0.25em] h-6 w-6"
|
||||
: "mt-[0.15em] h-4 w-4"
|
||||
} ${ping && "animate-pulse text-green-500"}`}
|
||||
className={`mr-2 mt-[0.15em] h-4 w-4 ${ping && "animate-pulse text-green-500"}`}
|
||||
/>
|
||||
<span className={`${icon === "Share3" ? "ml-[1.8em]" : " "}`}>
|
||||
{value}
|
||||
</span>
|
||||
<span>{value}</span>
|
||||
<span
|
||||
className={`absolute right-2 top-[0.43em] flex items-center rounded-sm bg-muted px-1.5 py-[0.1em] text-muted-foreground`}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ const GridComponent = ({ flowData }: { flowData: FlowType }) => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="line-clamp-2 h-full pt-5 text-sm text-primary">
|
||||
<div className="line-clamp-2 h-full pt-3 text-sm text-primary">
|
||||
{flowData.description}
|
||||
</div>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ const HeaderComponent = ({
|
|||
value={debouncedSearch}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
<div className="relative mr-2 flex rounded-lg border border-muted bg-muted">
|
||||
<div className="relative top-[3px] mr-2 flex h-fit rounded-lg border border-muted bg-muted">
|
||||
{/* Sliding Indicator */}
|
||||
<div
|
||||
className={`absolute top-[3px] h-[33px] w-8 transform rounded-lg bg-background shadow-md transition-transform duration-300 ${
|
||||
|
|
@ -129,7 +129,7 @@ const HeaderComponent = ({
|
|||
<ForwardedIconComponent
|
||||
name={viewType === "list" ? "Menu" : "LayoutGrid"}
|
||||
aria-hidden="true"
|
||||
className="h-4 w-4 group-hover:text-foreground"
|
||||
className="relative bottom-[1px] h-4 w-4 group-hover:text-foreground"
|
||||
/>
|
||||
</Button>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import { useFolderStore } from "@/stores/foldersStore";
|
|||
import { formatNumber } from "@/utils/utils";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import { FaDiscord, FaGithub } from "react-icons/fa";
|
||||
import { HiArrowRight } from "react-icons/hi";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import useFileDrop from "../hooks/use-on-file-drop";
|
||||
|
||||
|
|
@ -68,14 +67,14 @@ export const EmptyPageCommunity = ({
|
|||
onFileDrop={handleFileDrop}
|
||||
>
|
||||
<div className="m-0 h-full w-full bg-background p-0">
|
||||
<div className="z-50 flex h-full w-full flex-col items-center justify-center gap-6">
|
||||
<div className="z-50 flex flex-col items-center gap-3">
|
||||
<div className="z-50 flex h-full w-full flex-col items-center justify-center gap-5">
|
||||
<div className="z-50 flex flex-col items-center gap-2">
|
||||
<div className="z-50 dark:hidden">
|
||||
<img
|
||||
src={logoLightPng}
|
||||
alt="Langflow Logo Light"
|
||||
data-testid="empty_page_logo_light"
|
||||
className=""
|
||||
className="relative top-3"
|
||||
/>
|
||||
</div>
|
||||
<div className="z-50 hidden dark:block">
|
||||
|
|
@ -83,19 +82,19 @@ export const EmptyPageCommunity = ({
|
|||
src={logoDarkPng}
|
||||
alt="Langflow Logo Dark"
|
||||
data-testid="empty_page_logo_dark"
|
||||
className=""
|
||||
className="relative top-3"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
data-testid="mainpage_title"
|
||||
className="z-50 text-center text-2xl font-semibold text-foreground"
|
||||
className="z-50 text-center font-chivo text-2xl font-semibold text-foreground"
|
||||
>
|
||||
{EMPTY_PAGE_TITLE}
|
||||
</span>
|
||||
|
||||
<span
|
||||
data-testid="empty_page_description"
|
||||
className="z-50 text-center text-[14px] text-secondary-foreground"
|
||||
className="z-50 text-center text-xs text-secondary-foreground"
|
||||
>
|
||||
{folders?.length > 1
|
||||
? EMPTY_PAGE_FOLDER_DESCRIPTION
|
||||
|
|
@ -103,7 +102,7 @@ export const EmptyPageCommunity = ({
|
|||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full max-w-[510px] flex-col gap-12 sm:gap-8">
|
||||
<div className="flex w-full max-w-[510px] flex-col gap-12 sm:gap-[29px]">
|
||||
<Button
|
||||
unstyled
|
||||
className="group mx-3 h-[84px] sm:mx-0"
|
||||
|
|
@ -113,7 +112,7 @@ export const EmptyPageCommunity = ({
|
|||
}}
|
||||
data-testid="empty_page_github_button"
|
||||
>
|
||||
<div className="relative flex flex-col rounded-lg border-[1px] bg-background p-4 transition-all duration-300 hover:-translate-y-0.5 hover:border-accent-pink-foreground hover:shadow-[0_4px_10px_rgba(198,97,184,0.25)]">
|
||||
<div className="relative flex flex-col rounded-lg border-[1px] bg-background p-4 transition-all duration-300 hover:border-accent-pink-foreground">
|
||||
<div className="grid w-full items-center justify-between gap-2">
|
||||
<div className="flex gap-3">
|
||||
<FaGithub className="h-6 w-6" />
|
||||
|
|
@ -143,7 +142,7 @@ export const EmptyPageCommunity = ({
|
|||
}}
|
||||
data-testid="empty_page_discord_button"
|
||||
>
|
||||
<div className="relative flex flex-col rounded-lg border-[1px] bg-background p-4 transition-all duration-300 hover:-translate-y-0.5 hover:border-discord-color hover:shadow-[0_4px_10px_rgba(88,101,242,0.25)]">
|
||||
<div className="relative flex flex-col rounded-lg border-[1px] bg-background p-4 transition-all duration-300 hover:border-discord-color">
|
||||
<div className="grid w-full items-center justify-between gap-2">
|
||||
<div className="flex gap-3">
|
||||
<FaDiscord className="h-6 w-6 text-discord-color" />
|
||||
|
|
@ -166,7 +165,7 @@ export const EmptyPageCommunity = ({
|
|||
|
||||
<Button
|
||||
variant="default"
|
||||
className="z-10 m-auto h-10 w-full max-w-[155px] rounded-lg font-bold transition-all duration-300"
|
||||
className="z-10 m-auto mt-3 h-10 w-full max-w-[155px] rounded-lg font-bold transition-all duration-300"
|
||||
onClick={() => setOpenModal(true)}
|
||||
id="new-project-btn"
|
||||
data-testid="new_project_btn_empty_page"
|
||||
|
|
@ -178,7 +177,7 @@ export const EmptyPageCommunity = ({
|
|||
</div>
|
||||
<p
|
||||
data-testid="empty_page_drag_and_drop_text"
|
||||
className="absolute bottom-5 left-0 right-0 mt-4 cursor-default text-center text-sm text-muted-foreground"
|
||||
className="absolute bottom-5 left-0 right-0 mt-4 cursor-default text-center text-xs text-muted-foreground"
|
||||
>
|
||||
{EMPTY_PAGE_DRAG_AND_DROP_TEXT}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -148,7 +148,10 @@ export default function StorePage(): JSX.Element {
|
|||
setLoading(false);
|
||||
setErrorData({
|
||||
title: COMPONENTS_ERROR_ALERT,
|
||||
list: [err["response"]["data"]["detail"]],
|
||||
list: [
|
||||
err?.response?.data?.detail ??
|
||||
"There was an error fetching the components",
|
||||
],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1257,6 +1257,10 @@
|
|||
@apply h-7 w-7 rounded-md;
|
||||
}
|
||||
|
||||
.hit-area-hover {
|
||||
@apply transition-colors duration-200 hover:bg-muted hover:text-foreground;
|
||||
}
|
||||
|
||||
.node-toolbar-buttons {
|
||||
@apply flex w-max items-center gap-1 rounded-lg text-foreground;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@ withEventDeliveryModes(
|
|||
const output = await page.getByTestId("div-chat-message").allTextContents();
|
||||
const outputText = output.join("\n");
|
||||
|
||||
expect(outputText.toLowerCase()).toContain("weather");
|
||||
expect(outputText.toLowerCase()).toContain("budget");
|
||||
expect(outputText.toLowerCase()).toContain("travel");
|
||||
expect(outputText.toLowerCase()).toContain("day");
|
||||
|
||||
expect(outputText.toLowerCase()).toContain(randomCity.toLowerCase());
|
||||
expect(outputText.toLowerCase()).toContain(randomCity2.toLowerCase());
|
||||
|
|
|
|||
|
|
@ -37,35 +37,6 @@ withEventDeliveryModes(
|
|||
|
||||
await initialGPTsetup(page);
|
||||
|
||||
if (process?.env?.ASTRA_DB_API_ENDPOINT?.includes("astra-dev")) {
|
||||
await page.getByTestId("title-Astra DB").first().click();
|
||||
await page.getByTestId("code-button-modal").click();
|
||||
await page.waitForSelector("text=Edit Code", {
|
||||
timeout: 3000,
|
||||
});
|
||||
let cleanCode = await extractAndCleanCode(page);
|
||||
cleanCode = cleanCode!.replace(
|
||||
'"pre_delete_collection": self.pre_delete_collection or False,',
|
||||
'"pre_delete_collection": self.pre_delete_collection or False,\n "environment": "dev",',
|
||||
);
|
||||
await page.locator("textarea").last().press(`ControlOrMeta+a`);
|
||||
await page.keyboard.press("Backspace");
|
||||
await page.locator("textarea").last().fill(cleanCode);
|
||||
await page.locator('//*[@id="checkAndSaveBtn"]').click();
|
||||
await page.waitForSelector('[data-testid="title-Astra DB"]', {
|
||||
timeout: 3000,
|
||||
});
|
||||
await page.getByTestId("title-Astra DB").last().click();
|
||||
await page.getByTestId("code-button-modal").click();
|
||||
await page.waitForSelector("text=Edit Code", {
|
||||
timeout: 3000,
|
||||
});
|
||||
await page.locator("textarea").last().press(`ControlOrMeta+a`);
|
||||
await page.keyboard.press("Backspace");
|
||||
await page.locator("textarea").last().fill(cleanCode);
|
||||
await page.locator('//*[@id="checkAndSaveBtn"]').click();
|
||||
}
|
||||
|
||||
await page.waitForSelector('[data-testid="title-Astra DB"]', {
|
||||
timeout: 3000,
|
||||
});
|
||||
|
|
@ -263,7 +234,10 @@ withEventDeliveryModes(
|
|||
path.join(__dirname, "../../assets/test_file.txt"),
|
||||
);
|
||||
await page.getByText("test_file.txt").last().isVisible();
|
||||
await page.waitForTimeout(500);
|
||||
await page.waitForSelector("text=file uploaded successfully", {
|
||||
timeout: 10000,
|
||||
});
|
||||
await page.waitForTimeout(3000);
|
||||
await page.getByTestId("select-files-modal-button").click();
|
||||
await page.getByTestId("button_run_astra db").last().click();
|
||||
await page.waitForSelector("text=built successfully", {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ test(
|
|||
|
||||
await page.keyboard.press("Enter");
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
let flowName = await page.getByTestId("input-flow-name").inputValue();
|
||||
|
||||
expect(flowName).toBe(randomName);
|
||||
|
|
@ -44,6 +46,8 @@ test(
|
|||
|
||||
await page.keyboard.press("Enter");
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
flowName = await page.getByTestId("input-flow-name").inputValue();
|
||||
|
||||
expect(flowName).toBe(randomName2);
|
||||
|
|
@ -84,6 +88,8 @@ test(
|
|||
|
||||
await page.keyboard.press("Enter");
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
flowName = await page.getByTestId("input-flow-name").inputValue();
|
||||
|
||||
expect(flowName).toBe(randomName4);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { test } from "@playwright/test";
|
||||
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||
|
||||
test("should exists Store", { tag: ["@release"] }, async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await awaitBootstrapTest(page, { skipModal: true });
|
||||
|
||||
await page.getByTestId("button-store").isVisible();
|
||||
await page.getByTestId("button-store").isEnabled();
|
||||
});
|
||||
|
||||
test("should not have an API key", { tag: ["@release"] }, async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await awaitBootstrapTest(page, { skipModal: true });
|
||||
|
||||
await page.getByTestId("button-store").click();
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ test.skip(
|
|||
if (!process.env.CI) {
|
||||
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
|
||||
}
|
||||
await awaitBootstrapTest(page);
|
||||
await awaitBootstrapTest(page, { skipModal: true });
|
||||
|
||||
await page.getByText("Close", { exact: true }).click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
|
@ -96,7 +96,8 @@ test.skip(
|
|||
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
|
||||
}
|
||||
|
||||
await page.goto("/");
|
||||
await awaitBootstrapTest(page, { skipModal: true });
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await page.getByTestId("button-store").click();
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
import * as dotenv from "dotenv";
|
||||
import path from "path";
|
||||
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||
|
||||
test(
|
||||
"should order the visualization (requires store API key)",
|
||||
{ tag: ["@release"] },
|
||||
async ({ page }) => {
|
||||
async ({ page, context }) => {
|
||||
test.skip();
|
||||
|
||||
test.skip(
|
||||
!process?.env?.STORE_API_KEY,
|
||||
"STORE_API_KEY required to run this test",
|
||||
|
|
@ -15,45 +18,71 @@ test(
|
|||
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
|
||||
}
|
||||
|
||||
await page.goto("/");
|
||||
await awaitBootstrapTest(page, { skipModal: true });
|
||||
|
||||
await page.getByTestId("button-store").click();
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitForSelector('[data-testid="button-store"]', {
|
||||
state: "visible",
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
await page.getByTestId("api-key-button-store").click({
|
||||
const [newPageStore] = await Promise.all([
|
||||
context.waitForEvent("page"),
|
||||
page.getByTestId("button-store").click(),
|
||||
]);
|
||||
|
||||
await newPageStore.waitForTimeout(1000);
|
||||
|
||||
await newPageStore.getByTestId("api-key-button-store").click({
|
||||
timeout: 200000,
|
||||
});
|
||||
|
||||
await page
|
||||
await newPageStore.getByTestId("sidebar-nav-Langflow Store").click();
|
||||
|
||||
await newPageStore
|
||||
.getByPlaceholder("Insert your API Key")
|
||||
.fill(process.env.STORE_API_KEY ?? "");
|
||||
|
||||
await page.getByTestId("api-key-save-button-store").click();
|
||||
await newPageStore.getByTestId("api-key-save-button-store").click();
|
||||
|
||||
await expect(page.getByText("API key saved successfully")).toBeVisible({
|
||||
await expect(
|
||||
newPageStore.getByText("API key saved successfully"),
|
||||
).toBeVisible({
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
await page.getByTestId("button-store").click();
|
||||
await newPageStore.getByTestId("back_page_button").click();
|
||||
|
||||
await expect(page.getByText("Basic RAG")).toBeVisible({ timeout: 30000 });
|
||||
await newPageStore.waitForTimeout(1000);
|
||||
|
||||
await page.getByTestId("select-order-store").click();
|
||||
const newPageStore2 = await context.newPage();
|
||||
|
||||
await page.getByText("Alphabetical").click();
|
||||
await newPageStore2.goto("/store");
|
||||
|
||||
await page.getByText("Album Cover Builder").isVisible();
|
||||
await newPageStore2.waitForTimeout(1000);
|
||||
|
||||
await page.getByTestId("select-order-store").click();
|
||||
await page.getByText("Popular").click();
|
||||
await expect(newPageStore2.getByText("Basic RAG")).toBeVisible({
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
await page.getByText("Basic RAG").isVisible();
|
||||
await newPageStore2.getByTestId("select-order-store").click();
|
||||
|
||||
await newPageStore2.getByText("Alphabetical").click();
|
||||
|
||||
await newPageStore2.getByText("Album Cover Builder").isVisible();
|
||||
|
||||
await newPageStore2.getByTestId("select-order-store").click();
|
||||
await newPageStore2.getByText("Popular").click();
|
||||
|
||||
await newPageStore2.getByText("Basic RAG").isVisible();
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"should filter by type (requires store API key)",
|
||||
{ tag: ["@release"] },
|
||||
async ({ page }) => {
|
||||
async ({ page, context }) => {
|
||||
test.skip();
|
||||
test.skip(
|
||||
!process?.env?.STORE_API_KEY,
|
||||
"STORE_API_KEY required to run this test",
|
||||
|
|
@ -61,60 +90,95 @@ test(
|
|||
if (!process.env.CI) {
|
||||
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
|
||||
}
|
||||
await page.goto("/");
|
||||
await awaitBootstrapTest(page, { skipModal: true });
|
||||
|
||||
await page.waitForSelector('[data-testid="button-store"]', {
|
||||
state: "visible",
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
await page.getByTestId("button-store").click();
|
||||
await page.getByTestId("api-key-button-store").click({
|
||||
|
||||
const newPageStore = await context.waitForEvent("page", { timeout: 30000 });
|
||||
|
||||
await newPageStore.waitForTimeout(1000);
|
||||
|
||||
await newPageStore.getByTestId("api-key-button-store").click({
|
||||
timeout: 200000,
|
||||
});
|
||||
|
||||
await page
|
||||
await newPageStore.getByTestId("sidebar-nav-Langflow Store").click();
|
||||
|
||||
await newPageStore
|
||||
.getByPlaceholder("Insert your API Key")
|
||||
.fill(process.env.STORE_API_KEY ?? "");
|
||||
await page.getByTestId("api-key-save-button-store").click();
|
||||
await expect(page.getByText("API key saved successfully")).toBeVisible({
|
||||
await newPageStore.getByTestId("api-key-save-button-store").click();
|
||||
await expect(
|
||||
newPageStore.getByText("API key saved successfully"),
|
||||
).toBeVisible({
|
||||
timeout: 5000,
|
||||
});
|
||||
await page.getByTestId("button-store").click();
|
||||
await page.waitForSelector('[data-testid="likes-Website Content QA"]', {
|
||||
|
||||
await newPageStore.getByTestId("back_page_button").click();
|
||||
|
||||
await newPageStore.waitForTimeout(1000);
|
||||
|
||||
const newPageStore2 = await context.newPage();
|
||||
|
||||
await newPageStore2.goto("/store");
|
||||
|
||||
await newPageStore2.waitForTimeout(1000);
|
||||
|
||||
await newPageStore2.waitForSelector(
|
||||
'[data-testid="likes-Website Content QA"]',
|
||||
{
|
||||
timeout: 100000,
|
||||
},
|
||||
);
|
||||
await newPageStore2.getByText("Website Content QA").isVisible();
|
||||
await newPageStore2.waitForSelector('[data-testid="flows-button-store"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
await page.getByText("Website Content QA").isVisible();
|
||||
await page.waitForSelector('[data-testid="flows-button-store"]', {
|
||||
await newPageStore2.getByTestId("flows-button-store").click();
|
||||
await newPageStore2.waitForSelector('[data-testid="icon-Group"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
await page.getByTestId("flows-button-store").click();
|
||||
await page.waitForSelector('[data-testid="icon-Group"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
let iconGroup = await page.getByTestId("icon-Group")?.count();
|
||||
let iconGroup = await newPageStore2.getByTestId("icon-Group")?.count();
|
||||
expect(iconGroup).not.toBe(0);
|
||||
await page.getByText("icon-ToyBrick").last().isHidden();
|
||||
await page.waitForSelector('[data-testid="components-button-store"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
await page.getByTestId("components-button-store").click();
|
||||
await expect(page.getByTestId("icon-Group").last()).toBeHidden({
|
||||
await newPageStore2.getByText("icon-ToyBrick").last().isHidden();
|
||||
await newPageStore2.waitForSelector(
|
||||
'[data-testid="components-button-store"]',
|
||||
{
|
||||
timeout: 100000,
|
||||
},
|
||||
);
|
||||
await newPageStore2.getByTestId("components-button-store").click();
|
||||
await expect(newPageStore2.getByTestId("icon-Group").last()).toBeHidden({
|
||||
timeout: 30000,
|
||||
});
|
||||
await page.waitForSelector('[data-testid="icon-ToyBrick"]', {
|
||||
await newPageStore2.waitForSelector('[data-testid="icon-ToyBrick"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
let toyBrick = await page.getByTestId("icon-ToyBrick")?.count();
|
||||
let toyBrick = await newPageStore2.getByTestId("icon-ToyBrick")?.count();
|
||||
expect(toyBrick).not.toBe(0);
|
||||
await page.waitForSelector('[data-testid="all-button-store"]', {
|
||||
await newPageStore2.waitForSelector('[data-testid="all-button-store"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
await page.getByTestId("all-button-store").click();
|
||||
await page.waitForSelector('[data-testid="icon-Group"]', {
|
||||
await newPageStore2.getByTestId("all-button-store").click();
|
||||
await newPageStore2.waitForSelector('[data-testid="icon-Group"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
await page.waitForSelector('[data-testid="icon-ToyBrick"]', {
|
||||
await newPageStore2.waitForSelector('[data-testid="icon-ToyBrick"]', {
|
||||
timeout: 100000,
|
||||
});
|
||||
let iconGroupAllCount = await page.getByTestId("icon-Group")?.count();
|
||||
await page.waitForTimeout(500);
|
||||
let toyBrickAllCount = await page.getByTestId("icon-ToyBrick")?.count();
|
||||
await page.waitForTimeout(500);
|
||||
let iconGroupAllCount = await newPageStore2
|
||||
.getByTestId("icon-Group")
|
||||
?.count();
|
||||
await newPageStore2.waitForTimeout(500);
|
||||
let toyBrickAllCount = await newPageStore2
|
||||
.getByTestId("icon-ToyBrick")
|
||||
?.count();
|
||||
await newPageStore2.waitForTimeout(500);
|
||||
if (iconGroupAllCount === 0 || toyBrickAllCount === 0) {
|
||||
expect(false).toBe(true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
# Task: Gradient Border Styling Refinement
|
||||
|
||||
**Status**: Completed
|
||||
|
||||
## Analysis
|
||||
|
||||
- [x] Requirements
|
||||
- [x] Match the gradient border style of the GitHub card shown in the reference image
|
||||
- [x] Ensure a consistent purple hue that fades from top to bottom
|
||||
- [x] Maintain proper dark background for content
|
||||
- [x] Challenges
|
||||
- [x] Achieving precise gradient fade matching the reference image
|
||||
- [x] Balancing border visibility while maintaining subtle effect
|
||||
- [x] Dependencies
|
||||
- [x] Existing BackgroundGradient component
|
||||
|
||||
## Plan
|
||||
|
||||
- [x] Step 1: Review the current implementation
|
||||
- [x] Analyze existing gradient colors and opacity values
|
||||
- [x] Compare with reference image for differences
|
||||
- [x] Step 2: Adjust color and gradient parameters
|
||||
- [x] Modify gradient colors to match the purple hue
|
||||
- [x] Fine-tune opacity values for natural fade
|
||||
- [x] Ensure proper background color for inner content
|
||||
- [x] Step 3: Optimize hover effects
|
||||
- [x] Refine blur and opacity transitions on hover
|
||||
- [x] Test with different content types
|
||||
|
||||
## Execution
|
||||
|
||||
- [x] Modify gradient parameters
|
||||
- [x] Update gradient direction and opacity stops
|
||||
- [x] Simplify to single-color fade for consistency
|
||||
- [x] Adjust container styles
|
||||
- [x] Set proper padding for border thickness
|
||||
- [x] Ensure rounded corners match reference
|
||||
- [x] Test and refine
|
||||
- [x] Compare implementation with reference image
|
||||
- [x] Make final adjustments to achieve exact match
|
||||
|
||||
## Summary
|
||||
|
||||
- [x] Files modified: `src/frontend/src/components/ui/background-gradient.tsx`
|
||||
- [x] Dependencies added/changed: None
|
||||
- [x] Edge cases considered: Different screen sizes, container dimensions
|
||||
- [x] Known limitations: Exact color reproduction may vary slightly based on monitor calibration
|
||||
- [x] Future impact points: Component can be reused across the application for consistent styling
|
||||
|
||||
### Implementation Details
|
||||
|
||||
1. Changed the gradient to a simpler purple fade from top to bottom
|
||||
2. Used opacity values of 0.7 at top and 0.3 at bottom for subtle fade
|
||||
3. Reduced border thickness to 2px with `p-[2px]`
|
||||
4. Made the hover effect more subtle with `opacity-60` instead of `opacity-100`
|
||||
5. Changed blur on hover from `blur-lg` to `blur-md` for a less intense glow
|
||||
Loading…
Add table
Add a link
Reference in a new issue