Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0b63216
web: Use basename instead of full name for disks
ancorgs Apr 2, 2025
700f8e5
web: Modify behavior of button to delete VG
ancorgs Mar 31, 2025
0b9ee7e
web: WIP New DriveDeviceMenu component
ancorgs Mar 31, 2025
a7ab48a
web: Truncate long device names (in a suboptimal way)
ancorgs Apr 4, 2025
93bad18
web: add component MenuButton
joseivanlopez Apr 11, 2025
142888c
web: adapt ConfigureDeviceMenu to use MenuButton
joseivanlopez Apr 14, 2025
dcc5e38
web: adapt DriveDeviceMenu to use MenuButton
joseivanlopez Apr 14, 2025
4063b9f
web: adapt tests
joseivanlopez Apr 14, 2025
cf62f73
fix(web): get volume group targets correctly
joseivanlopez Apr 14, 2025
ab361d4
web: save the active menu item
joseivanlopez Apr 15, 2025
9d147ac
web: add MenuButton tests
joseivanlopez Apr 15, 2025
8a69a41
Merge branch 'master' into lvm-menus
joseivanlopez Apr 15, 2025
47f7c07
web: small code improvement
joseivanlopez Apr 15, 2025
0f52cfe
web: use menu item label for back label
joseivanlopez Apr 15, 2025
fae3dbd
web: indicate whether the disk cannot be changed
joseivanlopez Apr 15, 2025
0e06ef9
web: add aria-label to nested menus
joseivanlopez Apr 15, 2025
0ef60e9
web: Revisit truncation of device names
ancorgs Apr 15, 2025
4311e24
web: Restore the generic string to go back on menus
ancorgs Apr 15, 2025
6c1fb64
web: clean up inline toggle styles
dgdavid Apr 15, 2025
53e59e4
web: allow passing props to MenuButton toggle
dgdavid Apr 15, 2025
faf8731
web: drop no longer needed toggle tweak
dgdavid Apr 15, 2025
51f02ae
web: shorten CSS classname
dgdavid Apr 15, 2025
2ef3721
web: use disabled color for disabled danger action
dgdavid Apr 15, 2025
6ba69cd
Improve MenuButtonItem code
joseivanlopez Apr 16, 2025
02e8a24
web: make clear why useId cannot be used
joseivanlopez Apr 16, 2025
8fc5d55
web: changelog
joseivanlopez Apr 16, 2025
86fd1d1
Merge branch 'master' into lvm-menus
joseivanlopez Apr 16, 2025
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
7 changes: 7 additions & 0 deletions web/package/agama-web-ui.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
-------------------------------------------------------------------
Wed Apr 16 06:09:58 UTC 2025 - José Iván López González <jlopez@suse.com>

- Shorten storage device names.
- Move LVM logical volumes to partitions, if possible.
- Add MenuButton component (gh#agama-project/agama#2241).

-------------------------------------------------------------------
Tue Apr 15 08:51:27 UTC 2025 - Ancor Gonzalez Sosa <ancor@suse.com>

Expand Down
31 changes: 21 additions & 10 deletions web/src/assets/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -171,24 +171,31 @@
}
}

.agm-inline-menu-toggle {
.agm-inline-toggle {
border-radius: 0;
inline-size: fit-content;
flex-direction: row-reverse;
align-items: center;
padding: 0 var(--pf-t--global--spacer--control--vertical--compact);
background: var(--agm-t--color--fog);
padding: var(--pf-t--global--spacer--control--vertical--compact);

&:hover {
&:hover,
&.pf-m-expanded {
background: #c0efde; // var(--agm-t--action--background--color--hover);
border-radius: var(--pf-t--global--border--radius--small);

&::before {
border-radius: var(--pf-t--global--border--radius--small);
}
}

&.pf-m-expanded {
background: var(--agm-t--color--jungle);
&::before {
border: 0;
border-radius: 0;
border-bottom: 1px solid var(--pf-t--global--border--color--default);
}

.pf-v6-c-menu-toggle__icon svg {
block-size: var(--pf-t--global--icon--size--lg);
inline-size: var(--pf-t--global--icon--size--lg);
&:focus-visible::before {
color: red;
border-bottom: 0;
}
}

Expand Down Expand Up @@ -355,6 +362,10 @@ label.pf-m-disabled + .pf-v6-c-check__description {
color: var(--pf-v6-c-check__label--disabled--Color);
}

.pf-v6-c-menu__list-item.pf-m-danger.pf-m-disabled {
--pf-v6-c-menu__item--Color: var(--pf-v6-c-menu__item--m-disabled--Color);
}

// Do not change the default cursor for labels forms because it is confusing
//
// See:
Expand Down
167 changes: 167 additions & 0 deletions web/src/components/core/MenuButton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* Copyright (c) [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, within } from "@testing-library/react";
import { plainRender } from "~/test-utils";
import MenuButton, { MenuButtonItem } from "~/components/core/MenuButton";

it("renders a button", () => {
plainRender(<MenuButton>{"test"}</MenuButton>);
const button = screen.getByRole("button", { name: "test" });
expect(button).toBeInTheDocument();
});

it("opens a menu on click", async () => {
const { user } = plainRender(
<MenuButton menuProps={{ "aria-label": "test menu" }}>{"test"}</MenuButton>,
);

const button = screen.getByRole("button", { name: "test" });
const menu = screen.queryByRole("menu", { name: "test menu" });
expect(menu).not.toBeInTheDocument();
await user.click(button);
screen.getByRole("menu", { name: "test menu" });
});

it("renders all the given menu items", async () => {
const { user } = plainRender(
<MenuButton
items={[
<MenuButtonItem key="item1">{"item 1"}</MenuButtonItem>,
<MenuButtonItem key="item2">{"item 2"}</MenuButtonItem>,
<MenuButtonItem key="item3">{"item 3"}</MenuButtonItem>,
]}
>
test
</MenuButton>,
);

const button = screen.getByRole("button", { name: "test" });
await user.click(button);
const menu = screen.getByRole("menu");
within(menu).getByRole("menuitem", { name: "item 1" });
within(menu).getByRole("menuitem", { name: "item 2" });
within(menu).getByRole("menuitem", { name: "item 3" });
});

it("allows passing props to the toggle", () => {
plainRender(
<MenuButton
toggleProps={{ className: "inline-toggle" }}
items={[
<MenuButtonItem key="item1">{"item 1"}</MenuButtonItem>,
<MenuButtonItem key="item2">{"item 2"}</MenuButtonItem>,
]}
>
test
</MenuButton>,
);

const button = screen.getByRole("button", { name: "test" });
expect(button).toHaveClass("inline-toggle");
});

it("allows to drill in", async () => {
const { user } = plainRender(
<MenuButton
menuProps={{ "aria-label": "test menu" }}
items={[
<MenuButtonItem
key="item1"
items={[
<MenuButtonItem key="item11">{"item 1-1"}</MenuButtonItem>,
<MenuButtonItem key="item12">{"item 1-2"}</MenuButtonItem>,
]}
>
item 1
</MenuButtonItem>,
]}
>
test
</MenuButton>,
);
const button = screen.getByRole("button", { name: "test" });
await user.click(button);
const menu = screen.getByRole("menu", { name: "test menu" });
const item1 = within(menu).getByRole("menuitem", { name: "item 1" });
// Jsdom does not report correct styles, see https://github.com/jsdom/jsdom/issues/2986.
// const item11 = within(menu).getByRole("menuitem", { name: "item 1-1" });
// expect(item11).not.toBeVisible();
expect(item1).toHaveAttribute("aria-current", "false");
await user.click(item1);
expect(item1).toHaveAttribute("aria-current", "true");
});

it("allows to drill out", async () => {
const { user } = plainRender(
<MenuButton
menuProps={{ "aria-label": "test menu" }}
items={[
<MenuButtonItem
key="item1"
items={[
<MenuButtonItem key="item11">{"item 1-1"}</MenuButtonItem>,
<MenuButtonItem key="item12">{"item 1-2"}</MenuButtonItem>,
]}
upProps={{ label: "return" }}
>
item 1
</MenuButtonItem>,
]}
>
test
</MenuButton>,
);
const button = screen.getByRole("button", { name: "test" });
await user.click(button);
const menu = screen.getByRole("menu", { name: "test menu" });
const item1 = within(menu).getByRole("menuitem", { name: "item 1" });
await user.click(item1);
expect(item1).toHaveAttribute("aria-current", "true");
const back = within(menu).getByRole("menuitem", { name: "return" });
await user.click(back);
expect(item1).not.toHaveAttribute("aria-current");
});

it("calls the item action on click", async () => {
const action = jest.fn();
const { user } = plainRender(
<MenuButton
items={[
<MenuButtonItem key="item1">{"item 1"}</MenuButtonItem>,
<MenuButtonItem key="item2" onClick={action}>
item 2
</MenuButtonItem>,
]}
>
test
</MenuButton>,
);

const button = screen.getByRole("button", { name: "test" });
await user.click(button);
const menu = screen.getByRole("menu");
const item2 = within(menu).getByRole("menuitem", { name: "item 2" });
await user.click(item2);
expect(action).toHaveBeenCalled();
});
Loading
Loading