Skip to content

Commit

Permalink
[UI v2] feat: Adds flow run data table filter components
Browse files Browse the repository at this point in the history
  • Loading branch information
devinvillarosa committed Feb 14, 2025
1 parent 93a270f commit e3aa54a
Show file tree
Hide file tree
Showing 11 changed files with 528 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ describe("CronScheduleForm", () => {
await user.click(screen.getByLabelText(/active/i));
await user.clear(screen.getByLabelText(/value/i));
await user.type(screen.getByLabelText(/value/i), "* * * * 1/2");
screen.logTestingPlaygroundURL();

await user.click(screen.getByRole("switch", { name: /day or/i }));
await user.click(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ describe("CronScheduleForm", () => {
await user.click(screen.getByRole("combobox", { name: /interval/i }));
await user.click(screen.getByRole("option", { name: /hours/i }));

screen.logTestingPlaygroundURL();

await user.click(
screen.getByRole("combobox", { name: /select timezone/i }),
);
Expand Down
15 changes: 15 additions & 0 deletions ui-v2/src/components/flow-runs/data-table/run-name-search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Icon } from "@/components/ui/icons";
import { Input, type InputProps } from "@/components/ui/input";

export const RunNameSearch = (props: InputProps) => {
return (
<div className="relative">
<Input placeholder="Search by run name" className="pl-10" {...props} />
<Icon
id="Search"
className="absolute left-3 top-2.5 text-muted-foreground"
size={18}
/>
</div>
);
};
74 changes: 74 additions & 0 deletions ui-v2/src/components/flow-runs/data-table/sort-filter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { beforeAll, describe, expect, it, vi } from "vitest";

import { mockPointerEvents } from "@tests/utils/browser";
import { SortFilter } from "./sort-filter";

describe("FlowRunsDataTable -- SortFilter", () => {
beforeAll(mockPointerEvents);

it("returns correct sort filter for Newest to oldest", async () => {
// Setup
const user = userEvent.setup();
const mockOnSelectFn = vi.fn();
render(<SortFilter value={undefined} onSelect={mockOnSelectFn} />);

// Test
await user.click(
screen.getByRole("combobox", { name: /flow run sort order/i }),
);
await user.click(screen.getByRole("option", { name: /newest to oldest/i }));

// Assert
expect(mockOnSelectFn).toBeCalledWith("START_TIME_ASC");
});

it("returns correct sort filter for Oldest to newest", async () => {
// Setup
const user = userEvent.setup();
const mockOnSelectFn = vi.fn();
render(<SortFilter value={undefined} onSelect={mockOnSelectFn} />);

// Test
await user.click(
screen.getByRole("combobox", { name: /flow run sort order/i }),
);
await user.click(screen.getByRole("option", { name: /oldest to newest/i }));

// Assert
expect(mockOnSelectFn).toBeCalledWith("START_TIME_DESC");
});

it("returns correct sort filter for A to Z", async () => {
// Setup
const user = userEvent.setup();
const mockOnSelectFn = vi.fn();
render(<SortFilter value={undefined} onSelect={mockOnSelectFn} />);

// Test
await user.click(
screen.getByRole("combobox", { name: /flow run sort order/i }),
);
await user.click(screen.getByRole("option", { name: /a to z/i }));

// Assert
expect(mockOnSelectFn).toBeCalledWith("NAME_ASC");
});

it("returns correct sort filter for Z to A", async () => {
// Setup
const user = userEvent.setup();
const mockOnSelectFn = vi.fn();
render(<SortFilter value={undefined} onSelect={mockOnSelectFn} />);

// Test
await user.click(
screen.getByRole("combobox", { name: /flow run sort order/i }),
);
await user.click(screen.getByRole("option", { name: /z to a/i }));

// Assert
expect(mockOnSelectFn).toBeCalledWith("NAME_DESC");
});
});
34 changes: 34 additions & 0 deletions ui-v2/src/components/flow-runs/data-table/sort-filter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";

type SortFilters =
| "START_TIME_ASC"
| "START_TIME_DESC"
| "NAME_ASC"
| "NAME_DESC";

type SortFilterProps = {
onSelect: (filter: SortFilters) => void;
value: undefined | SortFilters;
};

export const SortFilter = ({ value, onSelect }: SortFilterProps) => {
return (
<Select value={value} onValueChange={onSelect}>
<SelectTrigger aria-label="Flow run sort order">
<SelectValue placeholder="Sort by" />
</SelectTrigger>
<SelectContent>
<SelectItem value="START_TIME_ASC">Newest to oldest</SelectItem>
<SelectItem value="START_TIME_DESC">Oldest to newest</SelectItem>
<SelectItem value="NAME_ASC">A to Z</SelectItem>
<SelectItem value="NAME_DESC">Z to A</SelectItem>
</SelectContent>
</Select>
);
};
17 changes: 17 additions & 0 deletions ui-v2/src/components/flow-runs/data-table/state-filter.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Meta, StoryObj } from "@storybook/react";

import { useState } from "react";
import { type FlowRunState, StateFilter } from "./state-filter";

const meta: Meta<typeof StateFilter> = {
title: "UI/FlowRuns/DataTable/StateFilter",
component: StateFilterStory,
};
export default meta;

function StateFilterStory() {
const [filters, setFilters] = useState<Set<FlowRunState>>();
return <StateFilter selectedFilters={filters} onSelectFilter={setFilters} />;
}

export const story: StoryObj = { name: "StateFilter" };
87 changes: 87 additions & 0 deletions ui-v2/src/components/flow-runs/data-table/state-filter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { beforeAll, describe, expect, it } from "vitest";

import { mockPointerEvents } from "@tests/utils/browser";
import { useState } from "react";
import { type FlowRunState, StateFilter } from "./state-filter";

describe("FlowRunsDataTable -- StateFilter", () => {
beforeAll(mockPointerEvents);

const TestStateFilter = () => {
const [filters, setFilters] = useState<Set<FlowRunState>>();
return (
<StateFilter selectedFilters={filters} onSelectFilter={setFilters} />
);
};

it("selects All except scheduled option", async () => {
// Setup
const user = userEvent.setup();
render(<TestStateFilter />);
// Test
await user.click(screen.getByRole("button", { name: /all run states/i }));
await user.click(
screen.getByRole("menuitem", { name: /all except scheduled/i }),
);
await user.keyboard("{Escape}");

// Assert
expect(
screen.getByRole("button", { name: /all except scheduled/i }),
).toBeVisible();
});

it("selects All run states option", async () => {
// Setup
const user = userEvent.setup();
render(<TestStateFilter />);
// Test
await user.click(screen.getByRole("button", { name: /all run states/i }));
await user.click(screen.getByRole("menuitem", { name: /all run states/i }));
await user.keyboard("{Escape}");

// Assert
expect(
screen.getByRole("button", { name: /all run states/i }),
).toBeVisible();
});

it("selects a single run state option", async () => {
// Setup
const user = userEvent.setup();
render(<TestStateFilter />);
// Test
await user.click(screen.getByRole("button", { name: /all run states/i }));
await user.click(screen.getByRole("menuitem", { name: /failed/i }));

await user.keyboard("{Escape}");

// Assert
expect(screen.getByRole("button", { name: /failed/i })).toBeVisible();
});

it("selects multiple run state options", async () => {
// Setup
const user = userEvent.setup();
render(<TestStateFilter />);
// Test
await user.click(screen.getByRole("button", { name: /all run states/i }));
await user.click(screen.getByRole("menuitem", { name: /timedout/i }));
await user.click(screen.getByRole("menuitem", { name: /crashed/i }));

await user.click(screen.getByRole("menuitem", { name: /failed/i }));
await user.click(screen.getByRole("menuitem", { name: /running/i }));
await user.click(screen.getByRole("menuitem", { name: /retrying/i }));

await user.keyboard("{Escape}");

// Assert
expect(
screen.getByRole("button", {
name: /timedout crashed failed running \+ 1/i,
}),
).toBeVisible();
});
});
Loading

0 comments on commit e3aa54a

Please sign in to comment.