Skip to content

Commit

Permalink
[UI v2] feat: Adds deployment parameter table
Browse files Browse the repository at this point in the history
  • Loading branch information
devinvillarosa committed Feb 10, 2025
1 parent 26f354b commit f892661
Show file tree
Hide file tree
Showing 7 changed files with 344 additions and 70 deletions.
15 changes: 15 additions & 0 deletions ui-v2/src/components/deployments/deployment-description.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Deployment } from "@/api/deployments";
import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";

type DeploymentDescriptionProps = {
deployment: Deployment;
};

export const DeploymentDescription = ({
deployment,
}: DeploymentDescriptionProps) => (
<Markdown className="prose" remarkPlugins={[remarkGfm]}>
{deployment.description}
</Markdown>
);
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const DeploymentDetailsPage = ({ id }: DeploymentDetailsPageProps) => {
</div>
<div className="grid gap-4" style={{ gridTemplateColumns: "3fr 1fr" }}>
<div className="flex flex-col gap-5">
<DeploymentDetailsTabs />
<DeploymentDetailsTabs deployment={data} />
</div>
<div className="flex flex-col gap-3">
<div className="border border-red-400">
Expand Down
147 changes: 78 additions & 69 deletions ui-v2/src/components/deployments/deployment-details-tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { Deployment } from "@/api/deployments";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import type { DeploymentDetailsTabOptions } from "@/routes/deployments/deployment.$id";
import { Link, getRouteApi } from "@tanstack/react-router";
import { type JSX } from "react";

import { DeploymentDescription } from "./deployment-description";
import { DeploymentParametersTable } from "./deployment-parameters-table";

const routeApi = getRouteApi("/deployments/deployment/$id");

type TabOption = {
Expand All @@ -11,77 +15,82 @@ type TabOption = {
ViewComponent: () => JSX.Element;
};

const TAB_OPTIONS = [
{
value: "Runs",
LinkComponent: () => (
<Link to="." search={{ tab: "Runs" }}>
<TabsTrigger value="Runs">Runs</TabsTrigger>
</Link>
),
ViewComponent: () => (
<TabsContent value="Runs">
<div className="border border-red-400">{"<RunsView />"}</div>
</TabsContent>
),
},
{
value: "Upcoming",
LinkComponent: () => (
<Link to="." search={{ tab: "Upcoming" }}>
<TabsTrigger value="Upcoming">Upcoming</TabsTrigger>
</Link>
),
ViewComponent: () => (
<TabsContent value="Upcoming">
<div className="border border-red-400">{"<UpcomingView />"}</div>
</TabsContent>
),
},
{
value: "Parameters",
LinkComponent: () => (
<Link to="." search={{ tab: "Parameters" }}>
<TabsTrigger value="Parameters">Parameters</TabsTrigger>
</Link>
),
ViewComponent: () => (
<TabsContent value="Parameters">
<div className="border border-red-400">{"<ParametersView />"}</div>
</TabsContent>
),
},
{
value: "Configuration",
LinkComponent: () => (
<Link to="." search={{ tab: "Configuration" }}>
<TabsTrigger value="Configuration">Configuration</TabsTrigger>
</Link>
),
ViewComponent: () => (
<TabsContent value="Configuration">
<div className="border border-red-400">{"<ConfigurationView />"}</div>
</TabsContent>
),
},
{
value: "Description",
LinkComponent: () => (
<Link to="." search={{ tab: "Description" }}>
<TabsTrigger value="Description">Description</TabsTrigger>
</Link>
),
ViewComponent: () => (
<TabsContent value="Description">
<div className="border border-red-400">{"<DescriptionView />"}</div>
</TabsContent>
),
},
] as const satisfies Array<TabOption>;

export const DeploymentDetailsTabs = (): JSX.Element => {
type DeploymentDetailsTabsProps = {
deployment: Deployment;
};
export const DeploymentDetailsTabs = ({
deployment,
}: DeploymentDetailsTabsProps): JSX.Element => {
const { tab } = routeApi.useSearch();

const TAB_OPTIONS = [
{
value: "Runs",
LinkComponent: () => (
<Link to="." search={{ tab: "Runs" }}>
<TabsTrigger value="Runs">Runs</TabsTrigger>
</Link>
),
ViewComponent: () => (
<TabsContent value="Runs">
<div className="border border-red-400">{"<RunsView />"}</div>
</TabsContent>
),
},
{
value: "Upcoming",
LinkComponent: () => (
<Link to="." search={{ tab: "Upcoming" }}>
<TabsTrigger value="Upcoming">Upcoming</TabsTrigger>
</Link>
),
ViewComponent: () => (
<TabsContent value="Upcoming">
<div className="border border-red-400">{"<UpcomingView />"}</div>
</TabsContent>
),
},
{
value: "Parameters",
LinkComponent: () => (
<Link to="." search={{ tab: "Parameters" }}>
<TabsTrigger value="Parameters">Parameters</TabsTrigger>
</Link>
),
ViewComponent: () => (
<TabsContent value="Parameters">
<DeploymentParametersTable deployment={deployment} />
</TabsContent>
),
},
{
value: "Configuration",
LinkComponent: () => (
<Link to="." search={{ tab: "Configuration" }}>
<TabsTrigger value="Configuration">Configuration</TabsTrigger>
</Link>
),
ViewComponent: () => (
<TabsContent value="Configuration">
<div className="border border-red-400">{"<ConfigurationView />"}</div>
</TabsContent>
),
},
{
value: "Description",
LinkComponent: () => (
<Link to="." search={{ tab: "Description" }}>
<TabsTrigger value="Description">Description</TabsTrigger>
</Link>
),
ViewComponent: () => (
<TabsContent value="Description">
<DeploymentDescription deployment={deployment} />
</TabsContent>
),
},
] as const satisfies Array<TabOption>;

return (
<Tabs defaultValue={TAB_OPTIONS[0].value} value={tab}>
<TabsList>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { Meta, StoryObj } from "@storybook/react";

import { createFakeDeployment } from "@/mocks";
import { DeploymentParametersTable } from "./deployment-parameters-table";

const meta = {
title: "Components/Deployments/DeploymentParametersTable",
component: DeploymentParametersTable,
args: {
deployment: createFakeDeployment({
parameter_openapi_schema: {
title: "Parameters",
type: "object",
properties: {
name: {
default: "world",
position: 0,
title: "name",
type: "string",
},
goodbye: {
default: false,
position: 1,
title: "goodbye",
type: "boolean",
},
},
},
parameters: {
// @ts-expect-error TODO: Fix OpenAPI Schema
goodbye: false,
// @ts-expect-error TODO: Fix OpenAPI Schema
name: "world",
},
}),
},
} satisfies Meta<typeof DeploymentParametersTable>;

export default meta;

export const story: StoryObj = { name: "DeploymentParametersTable" };
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it } from "vitest";

import { createFakeDeployment } from "@/mocks";
import { DeploymentParametersTable } from "./deployment-parameters-table";

const MOCK_DEPLOYMENT = createFakeDeployment({
parameter_openapi_schema: {
title: "Parameters",
type: "object",
properties: {
name: {
default: "world",
position: 0,
title: "name",
type: "string",
},
goodbye: {
default: false,
position: 1,
title: "goodbye",
type: "boolean",
},
},
},
parameters: {
// @ts-expect-error TODO: Fix OpenAPI Schema
goodbye: false,
// @ts-expect-error TODO: Fix OpenAPI Schema
name: "world",
},
});

describe("DeploymentParametersTable", () => {
it("renders table with rows", () => {
// ------------ Setup
render(<DeploymentParametersTable deployment={MOCK_DEPLOYMENT} />);

// ------------ Assert
expect(screen.getByRole("cell", { name: /name/i })).toBeVisible();
expect(screen.getByRole("cell", { name: /goodbye/i })).toBeVisible();
});

it("filters table rows", async () => {
// ------------ Setup
const user = userEvent.setup();

render(<DeploymentParametersTable deployment={MOCK_DEPLOYMENT} />);

// ------------ Act
await user.type(screen.getByRole("textbox"), "name");

// ------------ Assert
await waitFor(() =>
expect(
screen.queryByRole("cell", { name: /goodbye/i }),
).not.toBeInTheDocument(),
);
expect(screen.getByRole("cell", { name: /name/i })).toBeVisible();
});

it("filters no results", async () => {
// ------------ Setup
const user = userEvent.setup();

render(<DeploymentParametersTable deployment={MOCK_DEPLOYMENT} />);

// ------------ Act
await user.type(screen.getByRole("textbox"), "no results found");

// ------------ Assert
await waitFor(() =>
expect(
screen.queryByRole("cell", { name: /goodbye/i }),
).not.toBeInTheDocument(),
);
expect(
screen.queryByRole("cell", { name: /name/i }),
).not.toBeInTheDocument();

expect(screen.getByText("No results.")).toBeVisible();
});
});
Loading

0 comments on commit f892661

Please sign in to comment.