Skip to content

Commit

Permalink
feat: update subnet table layout (#5226)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jay-Topher committed Dec 6, 2023
1 parent d1ccfb5 commit f365399
Show file tree
Hide file tree
Showing 14 changed files with 561 additions and 290 deletions.
35 changes: 16 additions & 19 deletions cypress/e2e/with-users/subnets/subnets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ context("Subnets", () => {

it("displays the main networking view correctly", () => {
const expectedHeaders = [
"Fabric",
"VLAN",
"DHCP",
"Subnet",
"Available IPs",
"Space",
];

cy.findByRole("table", { name: "Subnets by Fabric" }).within(() => {
cy.findByRole("grid", { name: "Subnets by Fabric" }).within(() => {
expectedHeaders.forEach((name) => {
cy.findByRole("columnheader", { name }).should("exist");
});
Expand All @@ -31,35 +30,33 @@ context("Subnets", () => {
it("updates the URL to default grouping if no group paramater has been set", () => {
cy.visit(generateMAASURL("/networks"));

cy.findByRole("tab", { name: /fabric/i }).should(
"have.attr",
"aria-selected",
"true"
cy.findByRole("combobox", { name: /group by/i }).should(
"have.value",
"fabric"
);

cy.url().should("include", generateMAASURL("/networks?by=fabric"));
});

it("allows grouping by fabric and space", () => {
cy.findByRole("table", { name: "Subnets by Fabric" }).within(() => {
cy.findAllByRole("columnheader").first().should("have.text", "Fabric");
cy.findByRole("grid", { name: "Subnets by Fabric" }).within(() => {
cy.findAllByRole("columnheader").first().should("have.text", "VLAN");
});

cy.findByRole("tab", { name: /fabric/i }).should(
"have.attr",
"aria-selected",
"true"
cy.findByRole("combobox", { name: /group by/i }).should(
"have.value",
"fabric"
);
cy.findByRole("tab", { name: /space/i }).should(
"have.attr",
"aria-selected",
"false"

cy.findByRole("combobox", { name: /group by/i }).should(
"not.have.value",
"space"
);

cy.findByRole("tab", { name: /space/i }).click();
cy.findByRole("combobox", { name: /group by/i }).select("space");

cy.findByRole("table", { name: "Subnets by Space" }).within(() => {
cy.findAllByRole("columnheader").first().should("have.text", "Space");
cy.findByRole("grid", { name: "Subnets by Space" }).within(() => {
cy.findAllByRole("columnheader").first().should("have.text", "VLAN");
});

cy.url().should("include", generateMAASURL("/networks?by=space"));
Expand Down
19 changes: 19 additions & 0 deletions src/app/base/components/GroupRow/GroupRow.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Meta } from "@storybook/react";

import GroupRow from "./GroupRow";

const meta: Meta<typeof GroupRow> = {
title: "Components/GroupRow",
component: GroupRow,
tags: ["autodocs"],
};

export default meta;

export const Example = {
args: {
itemName: "network",
groupName: "fabric",
count: 2,
},
};
27 changes: 27 additions & 0 deletions src/app/base/components/GroupRow/GroupRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import pluralize from "pluralize";

import DoubleRow from "app/base/components/DoubleRow";

export enum Label {
HideGroup = "Hide",
ShowGroup = "Show",
}

type GroupRowProps = {
itemName: string;
groupName: string;
count: number;
};

const GroupRow = ({ itemName, groupName, count }: GroupRowProps) => {
return (
<>
<DoubleRow
primary={<strong>{groupName}</strong>}
secondary={<span>{pluralize(itemName, count, true)}</span>}
/>
</>
);
};

export default GroupRow;
1 change: 1 addition & 0 deletions src/app/base/components/GroupRow/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./GroupRow";
10 changes: 5 additions & 5 deletions src/app/subnets/views/SubnetsList/SubnetsList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ it("displays loading text", async () => {
route: urls.index,
});

expect(screen.getAllByRole("table")).toHaveLength(1);
expect(screen.getAllByRole("grid")).toHaveLength(1);
await userEvent.type(screen.getByRole("searchbox"), "non-existent-fabric");
await waitFor(() =>
expect(screen.getByText(/Loading.../)).toBeInTheDocument()
Expand All @@ -50,15 +50,15 @@ it("displays correct text when there are no results for the search criteria", as
route: urls.index,
});

expect(screen.getAllByRole("table")).toHaveLength(1);
const tableBody = screen.getAllByRole("rowgroup")[1];
expect(screen.getAllByRole("grid")).toHaveLength(1);

await userEvent.type(screen.getByRole("searchbox"), "non-existent-fabric");

await waitFor(() =>
expect(within(tableBody).getByText(/No results/)).toBeInTheDocument()
expect(
within(screen.getByRole("grid")).getByText(/No results/)
).toBeInTheDocument()
);
expect(within(tableBody).getAllByRole("row")).toHaveLength(1);
});

it("sets the options from the URL on load", async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { useMemo } from "react";

import { Pagination, ModularTable } from "@canonical/react-components";
import { Pagination, MainTable } from "@canonical/react-components";

import { CellContents } from "app/subnets/views/SubnetsList/SubnetsTable/components";
import TableHeader from "app/base/components/TableHeader";
import { generateSubnetGroupRows } from "app/subnets/views/SubnetsList/SubnetsTable/components";
import {
fabricTableColumns,
subnetColumnLabels,
SubnetsColumns,
} from "app/subnets/views/SubnetsList/SubnetsTable/constants";
import { usePagination } from "app/subnets/views/SubnetsList/SubnetsTable/hooks";
import type { SubnetsTableRow } from "app/subnets/views/SubnetsList/SubnetsTable/types";
import { groupRowsByFabricAndVlan } from "app/subnets/views/SubnetsList/SubnetsTable/utils";
import {
groupSubnetData,
groupRowsByFabric,
} from "app/subnets/views/SubnetsList/SubnetsTable/utils";

const FabricTable = ({
data,
Expand All @@ -19,62 +24,73 @@ const FabricTable = ({
emptyMsg: string;
}): JSX.Element => {
const { pageData, ...paginationProps } = usePagination(data);
const headers = useMemo(
() => [
{
"aria-label": subnetColumnLabels[SubnetsColumns.FABRIC],
key: SubnetsColumns.FABRIC,
content: (
<TableHeader>{subnetColumnLabels[SubnetsColumns.FABRIC]}</TableHeader>
),
},
{
"aria-label": subnetColumnLabels[SubnetsColumns.VLAN],
key: SubnetsColumns.VLAN,
content: (
<TableHeader>{subnetColumnLabels[SubnetsColumns.VLAN]}</TableHeader>
),
},
{
"aria-label": subnetColumnLabels[SubnetsColumns.DHCP],
key: SubnetsColumns.DHCP,
content: (
<TableHeader>{subnetColumnLabels[SubnetsColumns.DHCP]}</TableHeader>
),
},
{
"aria-label": subnetColumnLabels[SubnetsColumns.SUBNET],
key: SubnetsColumns.SUBNET,
content: (
<TableHeader>{subnetColumnLabels[SubnetsColumns.SUBNET]}</TableHeader>
),
},
{
"aria-label": subnetColumnLabels[SubnetsColumns.IPS],
key: SubnetsColumns.IPS,
content: (
<TableHeader>{subnetColumnLabels[SubnetsColumns.IPS]}</TableHeader>
),
},
{
"aria-label": subnetColumnLabels[SubnetsColumns.SPACE],
key: SubnetsColumns.SPACE,
className: "u-align--right",
content: (
<TableHeader>{subnetColumnLabels[SubnetsColumns.SPACE]}</TableHeader>
),
},
],
[]
);

const groupedData = useMemo(() => groupSubnetData(data, "fabric"), [data]);
const rowData = useMemo(() => groupRowsByFabric(pageData), [pageData]);

const rows = generateSubnetGroupRows({
groups: rowData,
itemName: "network",
columnLength: fabricTableColumns.length,
groupMap: groupedData,
});

return (
<>
<ModularTable
<MainTable
aria-label="Subnets by Fabric"
className="subnets-table"
columns={useMemo(
() => [
{
Header: subnetColumnLabels[SubnetsColumns.FABRIC],
accessor: SubnetsColumns.FABRIC,
Cell: CellContents,
},
{
Header: subnetColumnLabels[SubnetsColumns.VLAN],
accessor: SubnetsColumns.VLAN,
Cell: CellContents,
},
{
Header: subnetColumnLabels[SubnetsColumns.DHCP],
accessor: SubnetsColumns.DHCP,
Cell: CellContents,
},
{
Header: subnetColumnLabels[SubnetsColumns.SUBNET],
accessor: SubnetsColumns.SUBNET,
Cell: CellContents,
},
{
Header: subnetColumnLabels[SubnetsColumns.IPS],
accessor: SubnetsColumns.IPS,
Cell: CellContents,
},
{
Header: subnetColumnLabels[SubnetsColumns.SPACE],
accessor: SubnetsColumns.SPACE,
className: "u-align--right",
Cell: CellContents,
},
],
[]
)}
data={groupRowsByFabricAndVlan(pageData)}
emptyMsg={emptyMsg}
getCellProps={({ value, column }) => ({
className: `subnets-table__cell--${column.id}${
value.isVisuallyHidden ? " u-no-border--top" : ""
}`,
role: column.id === "fabric" ? "rowheader" : undefined,
})}
getHeaderProps={(header) => ({
className: `subnets-table__cell--${header.id}`,
})}
getRowProps={(row) => ({
"aria-label": row.values.fabric.label,
})}
className="fabric-table"
emptyStateMsg={emptyMsg}
headers={headers}
rows={rows}
/>
<Pagination {...paginationProps} aria-label="pagination" />
</>
Expand Down
Loading

0 comments on commit f365399

Please sign in to comment.