diff --git a/web/src/components/core/Drawer.tsx b/web/src/components/core/Drawer.tsx deleted file mode 100644 index b1ab80a060..0000000000 --- a/web/src/components/core/Drawer.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) [2024-2025] SUSE LLC - * - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, contact SUSE LLC. - * - * To contact SUSE LLC about this file by physical or electronic mail, you may - * find current contact information at www.suse.com. - */ - -import React, { ReactNode, forwardRef, useImperativeHandle, useState } from "react"; -import { - Drawer as PFDrawer, - DrawerPanelBody, - DrawerPanelContent, - DrawerContent, - DrawerContentBody, - DrawerHead, - DrawerActions, - DrawerCloseButton, - DrawerProps as PFDrawerProps, -} from "@patternfly/react-core"; - -type DrawerProps = { - panelHeader: ReactNode; - panelContent: ReactNode; -} & PFDrawerProps; - -/** - * PF/Drawer wrapper - * - * NOTE: Although the React documentation encourages to use props over - * impreative behaviours, this looks a more than reasanable use case for - * keeping the state and its manipulation inside the component instead of - * its parent, just exposing a kind of "public API" to influence it. - * - * @todo write documentation - */ -const Drawer = forwardRef( - ({ panelHeader, panelContent, isExpanded = false, children }: DrawerProps, ref) => { - const [isOpen, setIsOpen] = useState(isExpanded); - const open = () => setIsOpen(true); - const close = () => setIsOpen(false); - const publicAPI = () => ({ open, close }); - - useImperativeHandle(ref, publicAPI, []); - - const onEscape = (event) => event.key === "Escape" && close(); - - return ( - - - - {panelHeader} - - - - - {panelContent} - - } - colorVariant="primary" - > - {children} - - - ); - }, -); - -export default Drawer; diff --git a/web/src/components/core/index.ts b/web/src/components/core/index.ts index 604f6e256e..a0c9a5fa6d 100644 --- a/web/src/components/core/index.ts +++ b/web/src/components/core/index.ts @@ -43,7 +43,6 @@ export { default as Link } from "./Link"; export { default as EmptyState } from "./EmptyState"; export { default as InstallerOptions } from "./InstallerOptions"; export { default as IssuesDrawer } from "./IssuesDrawer"; -export { default as Drawer } from "./Drawer"; export { default as SelectWrapper } from "./SelectWrapper"; export { default as NestedContent } from "./NestedContent"; export { default as SubtleContent } from "./SubtleContent"; diff --git a/web/src/components/network/ConnectionsTable.test.tsx b/web/src/components/network/ConnectionsTable.test.tsx deleted file mode 100644 index 721cc4f810..0000000000 --- a/web/src/components/network/ConnectionsTable.test.tsx +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) [2023-2025] SUSE LLC - * - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, contact SUSE LLC. - * - * To contact SUSE LLC about this file by physical or electronic mail, you may - * find current contact information at www.suse.com. - */ - -import React from "react"; -import { screen, waitFor, within } from "@testing-library/react"; -import { mockNavigateFn, plainRender } from "~/test-utils"; -import { Connection, ConnectionMethod, ConnectionType, Device, DeviceState } from "~/types/network"; -import ConnectionsTable from "~/components/network/ConnectionsTable"; - -const mockOnForgetFn = jest.fn(); - -const enp1s0: Device = { - name: "enp1s0", - connection: "enp1s0", - type: ConnectionType.ETHERNET, - state: DeviceState.CONNECTED, - addresses: [{ address: "192.168.69.200", prefix: 24 }], - nameservers: ["192.168.69.1"], - method4: ConnectionMethod.MANUAL, - method6: ConnectionMethod.AUTO, - gateway4: "192.168.69.1", - gateway6: "", - macAddress: "AA:11:22:33:44::FF", -}; - -const wlan0: Device = { - name: "wlan0", - connection: "WiFi", - type: ConnectionType.WIFI, - state: DeviceState.CONNECTED, - addresses: [{ address: "192.168.69.201", prefix: 24 }], - nameservers: ["192.168.69.1"], - method4: ConnectionMethod.MANUAL, - method6: ConnectionMethod.AUTO, - gateway4: "192.168.69.1", - gateway6: "", - macAddress: "AA:11:22:33:55::FF", -}; - -const wiredConnection: Connection = new Connection("enp1s0", { - iface: "enp1s0", - addresses: [{ address: "192.168.69.200", prefix: 24 }], -}); - -const wirelessConnection: Connection = new Connection("WiFi", { - iface: "wlan0", - addresses: [{ address: "192.168.69.201", prefix: 24 }], -}); - -const connections = [wiredConnection, wirelessConnection]; -const devices = [enp1s0, wlan0]; - -describe("ConnectionsTable", () => { - describe("when there are no connections", () => { - it("renders nothing", async () => { - const { container } = plainRender(); - await waitFor(() => expect(container).toBeEmptyDOMElement()); - }); - }); - - describe("when there are connections", () => { - it("renders them in a table", () => { - plainRender(); - const table = screen.getByRole("grid"); - within(table).getByText("Name"); - within(table).getByText("IP addresses"); - within(table).getByText("enp1s0"); - within(table).getByText("192.168.69.200/24"); - within(table).getByText("WiFi"); - within(table).getByText("192.168.69.201/24"); - }); - - it("renders an actions toggler per connection", async () => { - plainRender(); - screen.getByRole("button", { name: "Actions for connection enp1s0" }); - screen.getByRole("button", { name: "Actions for connection WiFi" }); - }); - - it("allows to edit a connection", async () => { - const { user } = plainRender( - , - ); - const connectionActions = screen.getByRole("button", { - name: "Actions for connection enp1s0", - }); - await user.click(connectionActions); - const menu = screen.getByRole("menu"); - const editAction = within(menu).getByRole("menuitem", { name: "Edit connection enp1s0" }); - await user.click(editAction); - expect(mockNavigateFn).toHaveBeenCalled(); - }); - - describe("and onForget callback is given", () => { - it("allows to forget a connectionn", async () => { - const { user } = plainRender( - , - ); - const connectionActions = screen.getByRole("button", { - name: "Actions for connection enp1s0", - }); - await user.click(connectionActions); - const menu = screen.getByRole("menu"); - const forgetAction = within(menu).getByRole("menuitem", { - name: "Forget connection enp1s0", - }); - await user.click(forgetAction); - expect(mockOnForgetFn).toHaveBeenCalledWith(wiredConnection); - }); - }); - }); -}); diff --git a/web/src/components/network/ConnectionsTable.tsx b/web/src/components/network/ConnectionsTable.tsx deleted file mode 100644 index f3c8575cdb..0000000000 --- a/web/src/components/network/ConnectionsTable.tsx +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) [2023-2025] SUSE LLC - * - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, contact SUSE LLC. - * - * To contact SUSE LLC about this file by physical or electronic mail, you may - * find current contact information at www.suse.com. - */ - -import React from "react"; -import { useNavigate, generatePath } from "react-router-dom"; -import { Table, Thead, Tr, Th, Tbody, Td } from "@patternfly/react-table"; -import { RowActions } from "~/components/core"; -import { Icon } from "~/components/layout"; -import { Connection, Device } from "~/types/network"; -import { NETWORK as PATHS } from "~/routes/paths"; -import { formatIp } from "~/utils/network"; -import { sprintf } from "sprintf-js"; -import { _ } from "~/i18n"; -import { Stack, StackItem } from "@patternfly/react-core"; - -type ConnectionsTableProps = { - connections: Connection[]; - devices: Device[]; - onForget?: (connection: Connection) => void; -}; - -const IPAddresses = ({ devices, connection }: { devices: Device[]; connection: Connection }) => { - const device = devices.find( - ({ connection: deviceConnectionId }) => deviceConnectionId === connection.id, - ); - const addresses = device ? device.addresses : connection.addresses; - - return ( - - {addresses.map((address, index) => ( - {formatIp(address)} - ))} - - ); -}; - -/** - * - * Displays given connections in a table - * - */ -const ConnectionsTable = ({ - connections, - devices, - onForget, -}: ConnectionsTableProps): React.ReactNode => { - const navigate = useNavigate(); - if (connections.length === 0) return null; - - return ( - - - - {/* TRANSLATORS: table header */} - - {/* TRANSLATORS: table header */} - - {/* TRANSLATORS: table header aria label */} - - - - {connections.map((connection) => { - const actions = [ - { - title: _("Edit"), - role: "link", - // TRANSLATORS: %s is replaced by a network connection name - "aria-label": sprintf(_("Edit connection %s"), connection.id), - onClick: () => navigate(generatePath(PATHS.editConnection, { id: connection.id })), - }, - typeof onForget === "function" && { - title: _("Forget"), - // TRANSLATORS: %s is replaced by a network connection name - "aria-label": sprintf(_("Forget connection %s"), connection.id), - icon: , - onClick: () => onForget(connection), - isDanger: true, - }, - ].filter(Boolean); - - return ( - - - - - - ); - })} - -
{_("Name")}{_("IP addresses")} -
{connection.id} - - - -
- ); -}; - -export default ConnectionsTable;