FN-5894: share project data-boundary and status helpers
Centralize project loading and status-state handling across dashboard surfaces. - add a reusable DataBoundary component for loading, empty, error, and loaded states - extract shared project status config helpers for labels, colors, icons, and initializing detection - update project overview, cards, badges, and selector UI to use the shared helpers - add regression coverage for data-boundary behavior, project overview empty/loading transitions, and status config fallbacks Files changed: packages/dashboard/app/components/DataBoundary.tsx | 64 ++++++++++++++++++++++ packages/dashboard/app/components/ProjectCard.tsx | 48 ++-------------- .../app/components/ProjectHealthBadge.tsx | 45 +-------------- .../dashboard/app/components/ProjectOverview.tsx | 4 +- .../dashboard/app/components/ProjectSelector.tsx | 27 +-------- .../app/components/__tests__/DataBoundary.test.tsx | 62 +++++++++++++++++++++ .../components/__tests__/ProjectOverview.test.tsx | 25 +++++++++ .../__tests__/ProjectThemeTokens.test.tsx | 37 ++++--------- .../utils/__tests__/projectStatusConfig.test.ts | 53 ++++++++++++++++++ .../dashboard/app/utils/projectStatusConfig.ts | 50 +++++++++++++++++ packages/dashboard/vitest.config.ts | 2 + 11 files changed, 282 insertions(+), 135 deletions(-) Fusion-Task-Id: FN-5894 Fusion-Task-Lineage: 3626a4af-b62b-456d-82d7-cf47bbf55c21
This commit is contained in:
64
packages/dashboard/app/components/DataBoundary.tsx
Normal file
64
packages/dashboard/app/components/DataBoundary.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { AlertCircle } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
import { getErrorMessage } from "@fusion/core";
|
||||
import { AgentEmptyState } from "./AgentEmptyState";
|
||||
import { ProjectGridSkeleton } from "./ProjectGridSkeleton";
|
||||
|
||||
export interface DataBoundaryProps {
|
||||
isEmpty: boolean;
|
||||
isLoading?: boolean;
|
||||
hasFetched?: boolean;
|
||||
error?: unknown;
|
||||
loadingFallback?: ReactNode;
|
||||
emptyFallback?: ReactNode;
|
||||
errorFallback?: ReactNode;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
function DefaultErrorFallback({ error }: { error: unknown }) {
|
||||
return (
|
||||
<div className="agent-empty" data-testid="data-boundary-error">
|
||||
<AlertCircle className="agent-empty-state__icon" size={48} opacity={0.3} />
|
||||
<p className="agent-empty-state__title">Unable to load data</p>
|
||||
<p className="agent-empty-state__description text-secondary">
|
||||
{getErrorMessage(error) || "Something went wrong while loading this view."}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function DataBoundary({
|
||||
isEmpty,
|
||||
isLoading = false,
|
||||
hasFetched = false,
|
||||
error,
|
||||
loadingFallback,
|
||||
emptyFallback,
|
||||
errorFallback,
|
||||
children,
|
||||
}: DataBoundaryProps) {
|
||||
if (error) {
|
||||
return <>{errorFallback ?? <DefaultErrorFallback error={error} />}</>;
|
||||
}
|
||||
|
||||
const shouldShowLoading = isLoading || (!hasFetched && !error);
|
||||
if (shouldShowLoading) {
|
||||
return <>{loadingFallback ?? <ProjectGridSkeleton />}</>;
|
||||
}
|
||||
|
||||
if (hasFetched && isEmpty) {
|
||||
return (
|
||||
<>
|
||||
{emptyFallback ?? (
|
||||
<AgentEmptyState
|
||||
title="No data available"
|
||||
description="There is nothing to show yet."
|
||||
ctaLabel=""
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { memo, useCallback, useState } from "react";
|
||||
import { Play, Pause, AlertCircle, Loader2, Trash2, Folder, ArrowRight } from "lucide-react";
|
||||
import { Play, Pause, Trash2, Folder, ArrowRight } from "lucide-react";
|
||||
import "./ProjectCard.css";
|
||||
import type { RegisteredProject, ProjectHealth, ProjectStatus } from "@fusion/core";
|
||||
import type { RegisteredProject, ProjectHealth } from "@fusion/core";
|
||||
import type { ProjectNodeAvailability } from "../api";
|
||||
import { getProjectStatusConfig, isInitializingStatus } from "../utils/projectStatusConfig";
|
||||
|
||||
export interface ProjectCardProps {
|
||||
project: RegisteredProject;
|
||||
@@ -15,45 +16,6 @@ export interface ProjectCardProps {
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
type StatusConfig = { label: string; color: string; icon: typeof Play };
|
||||
|
||||
const STATUS_CONFIG: Record<ProjectStatus, StatusConfig> = {
|
||||
active: { label: "Active", color: "var(--color-success)", icon: Play },
|
||||
paused: { label: "Paused", color: "var(--color-warning)", icon: Pause },
|
||||
errored: { label: "Error", color: "var(--color-error)", icon: AlertCircle },
|
||||
initializing: { label: "Initializing", color: "var(--color-warning)", icon: Loader2 },
|
||||
};
|
||||
|
||||
const FALLBACK_STATUS_CONFIG: StatusConfig = {
|
||||
label: "Unknown",
|
||||
color: "var(--color-error)",
|
||||
icon: AlertCircle,
|
||||
};
|
||||
|
||||
function formatStatusLabel(status: string | null | undefined): string {
|
||||
if (!status) {
|
||||
return FALLBACK_STATUS_CONFIG.label;
|
||||
}
|
||||
|
||||
return status
|
||||
.split(/[-_\s]+/)
|
||||
.filter(Boolean)
|
||||
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
function getStatusConfig(status: string | null | undefined): StatusConfig {
|
||||
const config = STATUS_CONFIG[status as ProjectStatus];
|
||||
if (config) {
|
||||
return config;
|
||||
}
|
||||
|
||||
return {
|
||||
...FALLBACK_STATUS_CONFIG,
|
||||
label: formatStatusLabel(status),
|
||||
};
|
||||
}
|
||||
|
||||
function formatRelativeTime(timestamp: string | undefined): string {
|
||||
if (!timestamp) return "Never";
|
||||
|
||||
@@ -127,7 +89,7 @@ function ProjectCardInner({
|
||||
isLoading = false,
|
||||
}: ProjectCardProps) {
|
||||
const [removeArmed, setRemoveArmed] = useState(false);
|
||||
const statusConfig = getStatusConfig(project.status);
|
||||
const statusConfig = getProjectStatusConfig(project.status);
|
||||
const StatusIcon = statusConfig.icon;
|
||||
|
||||
const handleSelect = useCallback(() => {
|
||||
@@ -157,7 +119,7 @@ function ProjectCardInner({
|
||||
|
||||
const isPaused = project.status === "paused";
|
||||
const isErrored = project.status === "errored";
|
||||
const isInitializing = project.status === "initializing";
|
||||
const isInitializing = isInitializingStatus(project.status);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { Play, Pause, AlertCircle, Loader2 } from "lucide-react";
|
||||
import type { ProjectStatus } from "@fusion/core";
|
||||
import type { ProjectHealth } from "../api";
|
||||
import { getProjectStatusConfig, isInitializingStatus } from "../utils/projectStatusConfig";
|
||||
|
||||
export interface ProjectHealthBadgeProps {
|
||||
status: ProjectStatus;
|
||||
@@ -10,45 +10,6 @@ export interface ProjectHealthBadgeProps {
|
||||
showTooltip?: boolean;
|
||||
}
|
||||
|
||||
type StatusConfig = { label: string; color: string; icon: typeof Play };
|
||||
|
||||
const STATUS_CONFIG: Record<ProjectStatus, StatusConfig> = {
|
||||
active: { label: "Active", color: "var(--success)", icon: Play },
|
||||
paused: { label: "Paused", color: "var(--warning)", icon: Pause },
|
||||
errored: { label: "Error", color: "var(--color-error)", icon: AlertCircle },
|
||||
initializing: { label: "Initializing", color: "var(--info)", icon: Loader2 },
|
||||
};
|
||||
|
||||
const FALLBACK_STATUS_CONFIG: StatusConfig = {
|
||||
label: "Unknown",
|
||||
color: "var(--color-error)",
|
||||
icon: AlertCircle,
|
||||
};
|
||||
|
||||
function formatStatusLabel(status: string | null | undefined): string {
|
||||
if (!status) {
|
||||
return FALLBACK_STATUS_CONFIG.label;
|
||||
}
|
||||
|
||||
return status
|
||||
.split(/[-_\s]+/)
|
||||
.filter(Boolean)
|
||||
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
function getStatusConfig(status: string | null | undefined): StatusConfig {
|
||||
const config = STATUS_CONFIG[status as ProjectStatus];
|
||||
if (config) {
|
||||
return config;
|
||||
}
|
||||
|
||||
return {
|
||||
...FALLBACK_STATUS_CONFIG,
|
||||
label: formatStatusLabel(status),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* ProjectHealthBadge - Color-coded badge showing project health status
|
||||
*
|
||||
@@ -62,7 +23,7 @@ export function ProjectHealthBadge({
|
||||
showTooltip = true,
|
||||
}: ProjectHealthBadgeProps) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const config = getStatusConfig(status);
|
||||
const config = getProjectStatusConfig(status);
|
||||
const StatusIcon = config.icon;
|
||||
|
||||
const handleMouseEnter = useCallback(() => {
|
||||
@@ -81,7 +42,7 @@ export function ProjectHealthBadge({
|
||||
lg: "project-health-badge--lg",
|
||||
};
|
||||
|
||||
const isInitializing = status === "initializing";
|
||||
const isInitializing = isInitializingStatus(status);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -127,7 +127,9 @@ export function ProjectOverview({
|
||||
paused: 2,
|
||||
active: 3,
|
||||
};
|
||||
comparison = statusOrder[a.project.status] - statusOrder[b.project.status];
|
||||
const aOrder = statusOrder[a.project.status] ?? Number.MAX_SAFE_INTEGER;
|
||||
const bOrder = statusOrder[b.project.status] ?? Number.MAX_SAFE_INTEGER;
|
||||
comparison = aOrder - bOrder;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,12 @@ import {
|
||||
Grid3X3,
|
||||
Search,
|
||||
Clock,
|
||||
Play,
|
||||
Pause,
|
||||
AlertCircle,
|
||||
Loader2,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import type { ProjectInfo } from "../api";
|
||||
import type { ProjectStatus } from "@fusion/core";
|
||||
import { getTrailingPath } from "../utils/pathDisplay";
|
||||
import { getProjectStatusConfig, isInitializingStatus } from "../utils/projectStatusConfig";
|
||||
|
||||
export interface ProjectSelectorProps {
|
||||
projects: ProjectInfo[];
|
||||
@@ -25,24 +22,6 @@ export interface ProjectSelectorProps {
|
||||
recentProjectIds?: string[];
|
||||
}
|
||||
|
||||
type StatusConfig = { color: string; icon: typeof Play };
|
||||
|
||||
const STATUS_CONFIG: Record<ProjectStatus, StatusConfig> = {
|
||||
active: { color: "var(--success)", icon: Play },
|
||||
paused: { color: "var(--warning)", icon: Pause },
|
||||
errored: { color: "var(--color-error)", icon: AlertCircle },
|
||||
initializing: { color: "var(--info)", icon: Loader2 },
|
||||
};
|
||||
|
||||
const FALLBACK_STATUS_CONFIG: StatusConfig = {
|
||||
color: "var(--color-error)",
|
||||
icon: AlertCircle,
|
||||
};
|
||||
|
||||
function getStatusConfig(status: string | null | undefined): StatusConfig {
|
||||
return STATUS_CONFIG[status as ProjectStatus] ?? FALLBACK_STATUS_CONFIG;
|
||||
}
|
||||
|
||||
/**
|
||||
* ProjectSelector - Project switcher dropdown with keyboard navigation
|
||||
*
|
||||
@@ -234,13 +213,13 @@ export function ProjectSelector({
|
||||
|
||||
// Render status icon
|
||||
const renderStatusIcon = (status: ProjectStatus) => {
|
||||
const config = getStatusConfig(status);
|
||||
const config = getProjectStatusConfig(status);
|
||||
const Icon = config.icon;
|
||||
return (
|
||||
<Icon
|
||||
size={14}
|
||||
style={{ color: config.color }}
|
||||
className={status === "initializing" ? "animate-spin" : ""}
|
||||
className={isInitializingStatus(status) ? "animate-spin" : ""}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { DataBoundary } from "../DataBoundary";
|
||||
|
||||
vi.mock("lucide-react", () => ({
|
||||
AlertCircle: () => <span data-testid="alert-icon">⚠</span>,
|
||||
Bot: () => <span data-testid="bot-icon">🤖</span>,
|
||||
Folder: () => <span data-testid="folder-icon">📁</span>,
|
||||
Activity: () => <span data-testid="activity-icon">📊</span>,
|
||||
CheckCircle: () => <span data-testid="check-icon">✓</span>,
|
||||
}));
|
||||
|
||||
describe("DataBoundary", () => {
|
||||
it("renders the loading fallback before the first fetch completes", () => {
|
||||
render(
|
||||
<DataBoundary
|
||||
isEmpty={false}
|
||||
hasFetched={false}
|
||||
isLoading={false}
|
||||
loadingFallback={<div data-testid="loading-fallback">Loading</div>}
|
||||
>
|
||||
<div>Loaded content</div>
|
||||
</DataBoundary>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("loading-fallback")).toBeDefined();
|
||||
expect(screen.queryByText("Loaded content")).toBeNull();
|
||||
});
|
||||
|
||||
it("renders an empty state after fetch completion instead of an infinite skeleton", () => {
|
||||
render(
|
||||
<DataBoundary isEmpty hasFetched isLoading={false}>
|
||||
<div>Loaded content</div>
|
||||
</DataBoundary>
|
||||
);
|
||||
|
||||
expect(screen.getByText("No data available")).toBeDefined();
|
||||
expect(screen.queryByText("Loaded content")).toBeNull();
|
||||
});
|
||||
|
||||
it("renders an error state when error is set", () => {
|
||||
render(
|
||||
<DataBoundary isEmpty hasFetched={false} error={new Error("Boom")}>
|
||||
<div>Loaded content</div>
|
||||
</DataBoundary>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("data-boundary-error")).toBeDefined();
|
||||
expect(screen.getByText("Boom")).toBeDefined();
|
||||
});
|
||||
|
||||
it("renders children when data is present", () => {
|
||||
render(
|
||||
<DataBoundary isEmpty={false} hasFetched>
|
||||
<div>Loaded content</div>
|
||||
</DataBoundary>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Loaded content")).toBeDefined();
|
||||
expect(screen.queryByText("No data available")).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -328,6 +328,31 @@ describe("ProjectOverview", () => {
|
||||
expect(screen.getByTestId("project-card-proj_1")).toBeDefined();
|
||||
});
|
||||
|
||||
it("does not crash when sorting by status with unknown project statuses", () => {
|
||||
expect(() => {
|
||||
render(
|
||||
<ProjectOverview
|
||||
projects={[
|
||||
makeProject({ id: "proj_active", name: "Active Project", status: "active" }),
|
||||
makeProject({ id: "proj_unknown", name: "Unknown Project", status: "removing" as ProjectStatus }),
|
||||
]}
|
||||
onSelectProject={noop}
|
||||
onAddProject={noop}
|
||||
onPauseProject={noop}
|
||||
onResumeProject={noop}
|
||||
onRemoveProject={noop}
|
||||
/>
|
||||
);
|
||||
}).not.toThrow();
|
||||
|
||||
fireEvent.change(screen.getByLabelText("Sort projects"), {
|
||||
target: { value: "status-asc" },
|
||||
});
|
||||
|
||||
expect(screen.getByTestId("project-card-proj_active")).toBeDefined();
|
||||
expect(screen.getByTestId("project-card-proj_unknown")).toBeDefined();
|
||||
});
|
||||
|
||||
it("shows loading skeleton when loading prop is true", () => {
|
||||
render(
|
||||
<ProjectOverview
|
||||
|
||||
@@ -115,33 +115,20 @@ describe("Project component CSS theme tokens", () => {
|
||||
expect(html).not.toContain("var(--error)");
|
||||
});
|
||||
|
||||
it("no STATUS_CONFIG in any project component uses bare var(--error)", async () => {
|
||||
// This is a source-code-level regression check.
|
||||
// Read the source files and verify no bare var(--error) remains.
|
||||
it("shared project status config uses canonical error tokens only", async () => {
|
||||
const fs = await import("fs");
|
||||
const path = await import("path");
|
||||
|
||||
const componentDir = path.resolve(__dirname, "..");
|
||||
const files = [
|
||||
"ProjectHealthBadge.tsx",
|
||||
"ProjectSelector.tsx",
|
||||
"ProjectCard.tsx",
|
||||
];
|
||||
const source = fs.readFileSync(
|
||||
path.resolve(__dirname, "../../utils/projectStatusConfig.ts"),
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
for (const file of files) {
|
||||
const source = fs.readFileSync(path.join(componentDir, file), "utf-8");
|
||||
|
||||
// Should NOT contain the bare var(--error) token
|
||||
expect(
|
||||
source,
|
||||
`${file} should not contain var(--error)`
|
||||
).not.toMatch(/var\(--error\)(?!\w)/);
|
||||
|
||||
// Should contain the correct token for errored status
|
||||
expect(
|
||||
source,
|
||||
`${file} should contain var(--color-error) for errored status`
|
||||
).toContain('var(--color-error)');
|
||||
}
|
||||
expect(source, "projectStatusConfig.ts should not contain var(--error)").not.toMatch(
|
||||
/var\(--error\)(?!\w)/
|
||||
);
|
||||
expect(source, "projectStatusConfig.ts should contain var(--color-error)").toContain(
|
||||
"var(--color-error)"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { AlertCircle, Loader2, Pause, Play } from "lucide-react";
|
||||
import {
|
||||
STATUS_CONFIG,
|
||||
formatStatusLabel,
|
||||
getProjectStatusConfig,
|
||||
isInitializingStatus,
|
||||
} from "../projectStatusConfig";
|
||||
|
||||
describe("projectStatusConfig", () => {
|
||||
it("maps each known status to the expected config", () => {
|
||||
expect(STATUS_CONFIG.active.label).toBe("Active");
|
||||
expect(STATUS_CONFIG.active.color).toBe("var(--color-success)");
|
||||
expect(STATUS_CONFIG.active.icon).toBe(Play);
|
||||
|
||||
expect(STATUS_CONFIG.paused.label).toBe("Paused");
|
||||
expect(STATUS_CONFIG.paused.color).toBe("var(--color-warning)");
|
||||
expect(STATUS_CONFIG.paused.icon).toBe(Pause);
|
||||
|
||||
expect(STATUS_CONFIG.errored.label).toBe("Error");
|
||||
expect(STATUS_CONFIG.errored.color).toBe("var(--color-error)");
|
||||
expect(STATUS_CONFIG.errored.icon).toBe(AlertCircle);
|
||||
|
||||
expect(STATUS_CONFIG.initializing.label).toBe("Initializing");
|
||||
expect(STATUS_CONFIG.initializing.color).toBe("var(--color-info)");
|
||||
expect(STATUS_CONFIG.initializing.icon).toBe(Loader2);
|
||||
});
|
||||
|
||||
it("formats unknown statuses into readable labels", () => {
|
||||
expect(formatStatusLabel("removing_now")).toBe("Removing Now");
|
||||
expect(getProjectStatusConfig("removing")).toMatchObject({
|
||||
label: "Removing",
|
||||
color: "var(--color-error)",
|
||||
});
|
||||
expect(getProjectStatusConfig("removing").icon).toBe(AlertCircle);
|
||||
});
|
||||
|
||||
it("returns Unknown fallback labels for undefined, null, or empty statuses", () => {
|
||||
for (const status of [undefined, null, ""]) {
|
||||
const config = getProjectStatusConfig(status);
|
||||
expect(config.label).toBe("Unknown");
|
||||
expect(config.color).toBe("var(--color-error)");
|
||||
expect(config.icon).toBe(AlertCircle);
|
||||
expect(config.icon).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it("only treats literal initializing as the spinner state", () => {
|
||||
expect(isInitializingStatus("initializing")).toBe(true);
|
||||
expect(isInitializingStatus("INITIALIZING")).toBe(false);
|
||||
expect(isInitializingStatus(undefined)).toBe(false);
|
||||
});
|
||||
});
|
||||
50
packages/dashboard/app/utils/projectStatusConfig.ts
Normal file
50
packages/dashboard/app/utils/projectStatusConfig.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { AlertCircle, Loader2, Pause, Play } from "lucide-react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { ProjectStatus } from "@fusion/core";
|
||||
|
||||
export interface ProjectStatusConfig {
|
||||
label: string;
|
||||
color: string;
|
||||
icon: LucideIcon;
|
||||
}
|
||||
|
||||
export const STATUS_CONFIG: Record<ProjectStatus, ProjectStatusConfig> = {
|
||||
active: { label: "Active", color: "var(--color-success)", icon: Play },
|
||||
paused: { label: "Paused", color: "var(--color-warning)", icon: Pause },
|
||||
errored: { label: "Error", color: "var(--color-error)", icon: AlertCircle },
|
||||
initializing: { label: "Initializing", color: "var(--color-info)", icon: Loader2 },
|
||||
};
|
||||
|
||||
const FALLBACK_STATUS_CONFIG: ProjectStatusConfig = {
|
||||
label: "Unknown",
|
||||
color: "var(--color-error)",
|
||||
icon: AlertCircle,
|
||||
};
|
||||
|
||||
export function formatStatusLabel(status: string | null | undefined): string {
|
||||
if (!status) {
|
||||
return FALLBACK_STATUS_CONFIG.label;
|
||||
}
|
||||
|
||||
return status
|
||||
.split(/[-_\s]+/)
|
||||
.filter(Boolean)
|
||||
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
export function getProjectStatusConfig(status: string | null | undefined): ProjectStatusConfig {
|
||||
const config = STATUS_CONFIG[status as ProjectStatus];
|
||||
if (config) {
|
||||
return config;
|
||||
}
|
||||
|
||||
return {
|
||||
...FALLBACK_STATUS_CONFIG,
|
||||
label: formatStatusLabel(status),
|
||||
};
|
||||
}
|
||||
|
||||
export function isInitializingStatus(status: string | null | undefined): boolean {
|
||||
return status === "initializing";
|
||||
}
|
||||
@@ -108,6 +108,7 @@ const qualityAppComponentTests = [
|
||||
"Column",
|
||||
"ConfirmDialog",
|
||||
"ConversationHistory",
|
||||
"DataBoundary",
|
||||
"DashboardLoader",
|
||||
"DevServerView.mobile",
|
||||
"DirectoryPicker",
|
||||
@@ -135,6 +136,7 @@ const qualityAppComponentTests = [
|
||||
"PrCreateModal",
|
||||
"PrCreateModal.layout",
|
||||
"ProjectCard",
|
||||
"ProjectHealthBadge",
|
||||
"ProjectSelector",
|
||||
"ProviderIcon",
|
||||
"PrPanel",
|
||||
|
||||
Reference in New Issue
Block a user