Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/src/analyses/components/Nuvs/NuvsBlastPending.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function RidTiming({ interval, lastCheckedAt }) {
export default function NuvsBlastPending({ interval, lastCheckedAt, rid }) {
return (
<Box className="flex items-start [&>div:first-of-type]:mr-1">
<Loader size="16px" color="primary" />
<Loader size="16px" color="blue" />
<div>
<div>
<span className="font-medium">BLAST in progress</span>
Expand Down
27 changes: 18 additions & 9 deletions apps/web/src/base/Loader.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
import { cn } from "@app/utils";

const colorToClass: Record<string, string> = {
blue: "border-blue-600",
green: "border-green-600",
grey: "border-gray-400",
greyDark: "border-gray-500",
red: "border-red-600",
export type LoaderColor =
| "blue"
| "green"
| "gray"
| "orange"
| "purple"
| "red";

export const colorToClass: Record<LoaderColor, string> = {
blue: "border-t-blue-600 border-x-blue-600",
green: "border-t-green-600 border-x-green-600",
gray: "border-t-gray-500 border-x-gray-500",
orange: "border-t-orange-600 border-x-orange-600",
purple: "border-t-purple-600 border-x-purple-600",
red: "border-t-red-600 border-x-red-600",
};

interface LoaderProps {
className?: string;
color?: string;
color?: LoaderColor;
size?: string;
}

export default function Loader({
className,
color = "greyDark",
color = "gray",
size = "22px",
}: LoaderProps) {
return (
Expand All @@ -25,7 +34,7 @@ export default function Loader({
aria-label="loading"
className={cn(
"animate-rotate inline-block rounded-full border-2 border-b-transparent",
colorToClass[color] || "border-gray-500",
colorToClass[color],
className,
)}
style={{ width: size, height: size }}
Expand Down
32 changes: 32 additions & 0 deletions apps/web/src/base/__tests__/Loader.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { screen } from "@testing-library/react";
import { renderWithProviders } from "@tests/setup.js";
import { describe, expect, it } from "vitest";
import Loader, { colorToClass } from "../Loader";

describe("<Loader />", () => {
it("keeps a transparent bottom border so the spin is visible", () => {
renderWithProviders(<Loader />);

const loader = screen.getByRole("status", { name: "loading" });

expect(loader).toHaveClass("border-b-transparent");
expect(loader).toHaveClass("animate-rotate");
});

it("keeps the transparent bottom border across every color", () => {
for (const color of Object.keys(
colorToClass,
) as (keyof typeof colorToClass)[]) {
const { unmount } = renderWithProviders(<Loader color={color} />);

const loader = screen.getByRole("status", { name: "loading" });

expect(loader).toHaveClass("border-b-transparent");
for (const expectedClass of colorToClass[color].split(" ")) {
expect(loader).toHaveClass(expectedClass);
}

unmount();
}
});
});
2 changes: 1 addition & 1 deletion apps/web/src/samples/components/Tag/WorkflowTag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function WorkflowTag({
<BaseWorkflowTag>
<WorkflowLabelIcon>
{workflowState === "pending" ? (
<Loader size="10px" color="white" />
<Loader size="10px" color="gray" />
) : (
<Icon icon={CheckCircle} size={14} />
)}
Expand Down