Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update subnet table layout #5226

Merged
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
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
Loading