Skip to content

Commit

Permalink
EVG-20806 Refactor files table to use LG table component (evergreen-c…
Browse files Browse the repository at this point in the history
  • Loading branch information
khelif96 authored Sep 19, 2023
1 parent 372a1e0 commit f6cbdb8
Show file tree
Hide file tree
Showing 15 changed files with 475 additions and 181 deletions.
10 changes: 5 additions & 5 deletions cypress/integration/task/files_tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ describe("files table", () => {

it("File names under name column should link to a new tab", () => {
cy.visit(FILES_ROUTE);
cy.dataCy("fileLink").should("have.attr", "target", "_blank");
cy.dataCy("file-link").should("have.attr", "target", "_blank");
});

it("Searching for a non existent value yields 0 results, tables will not render and will display 'No files found'", () => {
cy.visit(FILES_ROUTE);
cy.dataCy("file-input").type("Hello world");
cy.dataCy("file-search-input").type("Hello world");
cy.dataCy("files-table").should("not.exist");
cy.dataCy("fileLink").should("not.exist");
cy.dataCy("file-link").should("not.exist");
cy.contains("No files found");
});

it("Searching for a value yields results across multiple tables", () => {
cy.visit(FILES_ROUTE);
cy.dataCy("file-input").type("458");
cy.dataCy("fileLink").should("have.length", 4);
cy.dataCy("file-search-input").type("458");
cy.dataCy("file-link").should("have.length", 4);
});

it("Should display 'No files found' after loading a task without files", () => {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"@leafygreen-ui/segmented-control": "8.2.6",
"@leafygreen-ui/select": "10.2.0",
"@leafygreen-ui/side-nav": "13.0.2",
"@leafygreen-ui/skeleton-loader": "1.1.0",
"@leafygreen-ui/table": "10.0.1",
"@leafygreen-ui/table/new": "npm:@leafygreen-ui/[email protected]",
"@leafygreen-ui/tabs": "11.0.4",
Expand Down
70 changes: 70 additions & 0 deletions src/components/Table/BaseTable.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useRef } from "react";
import { LGColumnDef, useLeafyGreenTable } from "@leafygreen-ui/table/new";
import { CustomStoryObj, CustomMeta } from "test_utils/types";
import { BaseTable } from "./BaseTable";

export default {
component: BaseTable,
} satisfies CustomMeta<typeof BaseTable>;

export const Default: CustomStoryObj<typeof BaseTable> = {
render: (args) => <TemplateComponent {...args} data={fullArray} />,
args: {
shouldAlternateRowColor: true,
darkMode: false,
},
};

export const EmptyState: CustomStoryObj<typeof BaseTable> = {
render: (args) => <TemplateComponent {...args} data={[]} />,
args: {
shouldAlternateRowColor: true,
darkMode: false,
},
};
interface DataShape {
name: string;
column1: string;
column2: string;
}
const fullArray: DataShape[] = Array.from({ length: 100 }, (_, i) => ({
name: `name ${i}`,
column1: `column1 ${i}`,
column2: `column2 ${i}`,
}));
const columns: LGColumnDef<DataShape>[] = [
{
accessorKey: "name",
header: "Name",
size: 60,
enableSorting: true,
},
{
accessorKey: "column1",
header: "Column 1",
size: 60,
enableSorting: true,
},
{
accessorKey: "column2",
header: "Column 2",
size: 60,
enableSorting: true,
},
];

const TemplateComponent: React.FC<
React.ComponentProps<typeof BaseTable> & {
data: DataShape[];
}
> = (args) => {
const { data, ...rest } = args;
const tableContainerRef = useRef<HTMLDivElement>(null);
const table = useLeafyGreenTable<DataShape>({
data,
columns,
containerRef: tableContainerRef,
});

return <BaseTable {...rest} table={table} />;
};
61 changes: 36 additions & 25 deletions src/components/Table/BaseTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,48 @@ import {
type SpruceTableProps = {
"data-cy-row"?: string;
"data-cy-table"?: string;
emptyComponent?: React.ReactNode;
};

export const BaseTable = <T extends LGRowData>({
"data-cy-row": dataCyRow,
"data-cy-table": dataCyTable,
emptyComponent,
table,
...args
}: SpruceTableProps & TableProps<T>) => (
<Table data-cy={dataCyTable} table={table} {...args}>
<TableHead>
{table.getHeaderGroups().map((headerGroup) => (
<HeaderRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<HeaderCell key={header.id} header={header}>
{flexRender(header.column.columnDef.header, header.getContext())}
</HeaderCell>
))}
</HeaderRow>
))}
</TableHead>
<TableBody>
{table.getRowModel().rows.map((row) => (
<Row key={row.id} row={row} data-cy={dataCyRow}>
{row.getVisibleCells().map((cell) => (
<Cell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Cell>
))}
{row.original.renderExpandedContent && <ExpandedContent row={row} />}
</Row>
))}
</TableBody>
</Table>
<>
<Table data-cy={dataCyTable} table={table} {...args}>
<TableHead>
{table.getHeaderGroups().map((headerGroup) => (
<HeaderRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<HeaderCell key={header.id} header={header}>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
</HeaderCell>
))}
</HeaderRow>
))}
</TableHead>
<TableBody>
{table.getRowModel().rows.map((row) => (
<Row key={row.id} row={row} data-cy={dataCyRow}>
{row.getVisibleCells().map((cell) => (
<Cell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Cell>
))}
{row.original.renderExpandedContent && (
<ExpandedContent row={row} />
)}
</Row>
))}
</TableBody>
</Table>
{table.getRowModel().rows.length === 0 &&
(emptyComponent || "No data to display")}
</>
);
1 change: 0 additions & 1 deletion src/components/Table/TableControl/TableControl.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { CustomStoryObj, CustomMeta } from "test_utils/types";

import TableControl from ".";

export default {
Expand Down
3 changes: 1 addition & 2 deletions src/components/Table/TablePlaceholder.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import styled from "@emotion/styled";
import { glyphs } from "@leafygreen-ui/icon";
import { palette } from "@leafygreen-ui/palette";
import Icon from "components/Icon";
import Icon, { glyphs } from "components/Icon";
import { size } from "constants/tokens";

const { gray } = palette;
Expand Down
4 changes: 2 additions & 2 deletions src/pages/task/TaskTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { queryString } from "utils";
import { BuildBaron } from "./taskTabs/BuildBaron";
import { useBuildBaronVariables } from "./taskTabs/buildBaronAndAnnotations";
import { ExecutionTasksTable } from "./taskTabs/ExecutionTasksTable";
import { FilesTables } from "./taskTabs/FilesTables";
import FileTable from "./taskTabs/FileTable";
import { Logs } from "./taskTabs/Logs";
import { TestsTable } from "./taskTabs/TestsTable";

Expand Down Expand Up @@ -115,7 +115,7 @@ export const TaskTabs: React.FC<TaskTabProps> = ({ isDisplayTask, task }) => {
data-cy="task-files-tab"
key="task-files-tab"
>
<FilesTables />
<FileTable taskId={id} execution={execution} />
</Tab>
),
[TaskTab.Annotations]: (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { CustomStoryObj, CustomMeta } from "test_utils/types";
import GroupedFileTable from ".";

const files = [
{
name: "some_file",
link: "some_link",
},
{
name: "another_file",
link: "another_link",
},
];

export default {
title: "Pages/Task/table/GroupedFileTable",
component: GroupedFileTable,
} satisfies CustomMeta<typeof GroupedFileTable>;

export const DefaultTable: CustomStoryObj<typeof GroupedFileTable> = {
render: (args) => <GroupedFileTable {...args} />,
args: {
taskName: "Task 1",
files,
},
};
58 changes: 58 additions & 0 deletions src/pages/task/taskTabs/FileTable/GroupedFileTable/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useRef } from "react";
import styled from "@emotion/styled";
import { useLeafyGreenTable, LGColumnDef } from "@leafygreen-ui/table/new";
import { Subtitle } from "@leafygreen-ui/typography";
import { StyledLink } from "components/styles";
import { BaseTable } from "components/Table/BaseTable";
import { size } from "constants/tokens";
import { Unpacked } from "types/utils";
import { GroupedFiles } from "../types";

type GroupedFilesFile = Unpacked<GroupedFiles["files"]>;

const columns: LGColumnDef<GroupedFilesFile>[] = [
{
accessorKey: "name",
header: "Name",
size: 60,
enableSorting: true,
cell: (value) => (
<StyledLink
href={value.row.original.link}
data-cy="file-link"
target="_blank"
>
{value.getValue()}
</StyledLink>
),
},
];

interface GroupedFileTableProps {
files: GroupedFilesFile[];
taskName?: string;
}
const GroupedFileTable: React.FC<GroupedFileTableProps> = ({
files,
taskName,
}) => {
const tableContainerRef = useRef<HTMLDivElement>(null);

const table = useLeafyGreenTable<GroupedFilesFile>({
containerRef: tableContainerRef,
data: files,
columns,
});

return (
<Container>
{taskName && <Subtitle>{taskName}</Subtitle>}
<BaseTable table={table} shouldAlternateRowColor />
</Container>
);
};

const Container = styled.div`
margin-bottom: ${size.m};
`;
export default GroupedFileTable;
74 changes: 74 additions & 0 deletions src/pages/task/taskTabs/FileTable/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useState } from "react";
import { useQuery } from "@apollo/client";
import styled from "@emotion/styled";
import { SearchInput } from "@leafygreen-ui/search-input";
import { Skeleton, TableSkeleton } from "@leafygreen-ui/skeleton-loader";
import { Body } from "@leafygreen-ui/typography";
import { size } from "constants/tokens";
import { useToastContext } from "context/toast";
import { TaskFilesQuery, TaskFilesQueryVariables } from "gql/generated/types";
import { GET_TASK_FILES } from "gql/queries";
import GroupedFileTable from "./GroupedFileTable";
import { filterGroupedFiles } from "./utils";

interface FileTableProps {
taskId: string;
execution: number;
}
const FileTable: React.FC<FileTableProps> = ({ execution, taskId }) => {
const [search, setSearch] = useState("");
const dispatchToast = useToastContext();
const { data, loading } = useQuery<TaskFilesQuery, TaskFilesQueryVariables>(
GET_TASK_FILES,
{
variables: {
taskId,
execution,
},
onError: (err) => {
dispatchToast.error(`Unable to load task files: ${err}`);
},
}
);
const { taskFiles } = data?.task ?? {};

const { groupedFiles = [] } = taskFiles ?? {};
const filteredGroupedFiles = filterGroupedFiles(groupedFiles, search);

// We only want to show the file group name if there are multiple file groups.
const hasMultipleFileGroups = groupedFiles.length > 1;

return loading ? (
<FilesTableSkeleton />
) : (
<>
<StyledSearchInput
aria-label="Search file names"
placeholder="Search file names"
onChange={(e) => setSearch(e.target.value)}
value={search}
data-cy="file-search-input"
/>
{filteredGroupedFiles.length === 0 && <Body>No files found</Body>}
{filteredGroupedFiles.map((groupedFile) => (
<GroupedFileTable
key={groupedFile?.taskName}
files={groupedFile?.files}
taskName={hasMultipleFileGroups && groupedFile?.taskName}
/>
))}
</>
);
};

const FilesTableSkeleton = () => (
<>
<Skeleton />
<TableSkeleton numCols={1} numRows={5} />
</>
);
const StyledSearchInput = styled(SearchInput)`
margin-bottom: ${size.m};
width: 400px;
`;
export default FileTable;
6 changes: 6 additions & 0 deletions src/pages/task/taskTabs/FileTable/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { TaskFilesQuery } from "gql/generated/types";
import { Unpacked } from "types/utils";

export type GroupedFiles = Unpacked<
TaskFilesQuery["task"]["taskFiles"]["groupedFiles"]
>;
Loading

0 comments on commit f6cbdb8

Please sign in to comment.