diff --git a/web/package/cockpit-agama.changes b/web/package/cockpit-agama.changes index 51ad097014..791086ffdd 100644 --- a/web/package/cockpit-agama.changes +++ b/web/package/cockpit-agama.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Wed Aug 2 18:59:16 UTC 2023 - Balsa Asanovic + +- Introduced functionality to close Dropdown automatically + when the user clicks outside of it. + (gh#openSUSE/agama#552). + ------------------------------------------------------------------- Wed Aug 2 10:03:23 UTC 2023 - Imobach Gonzalez Sosa diff --git a/web/src/components/core/PageOptions.jsx b/web/src/components/core/PageOptions.jsx index a25a5a264e..cf3c9d3de0 100644 --- a/web/src/components/core/PageOptions.jsx +++ b/web/src/components/core/PageOptions.jsx @@ -19,7 +19,7 @@ * find current contact information at www.suse.com. */ -import React, { useState } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { Button, Dropdown, DropdownItem, DropdownGroup } from '@patternfly/react-core'; import { Icon, PageOptions as PageOptionsSlot } from "~/components/layout"; @@ -114,20 +114,40 @@ const Item = ({ children, ...props }) => { */ const PageOptions = ({ children }) => { const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + const onToggle = () => setIsOpen(!isOpen); const onSelect = () => setIsOpen(false); + useEffect(() => { + const handleClickOutside = (event) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { + setIsOpen(false); + } + }; + + if (isOpen) { + document.addEventListener('click', handleClickOutside); + } + + return () => { + document.removeEventListener('click', handleClickOutside); + }; + }, [isOpen]); + return ( - } - onSelect={onSelect} - dropdownItems={Array(children)} - position="right" - className="page-options" - isGrouped - /> +
+ } + onSelect={onSelect} + dropdownItems={Array(children)} + position="right" + className="page-options" + isGrouped + /> +
); }; diff --git a/web/src/components/core/PageOptions.test.jsx b/web/src/components/core/PageOptions.test.jsx index 00c26aaafa..31576c26dd 100644 --- a/web/src/components/core/PageOptions.test.jsx +++ b/web/src/components/core/PageOptions.test.jsx @@ -20,7 +20,7 @@ */ import React from "react"; -import { screen } from "@testing-library/react"; +import { screen, fireEvent } from "@testing-library/react"; import { plainRender, mockLayout } from "~/test-utils"; import { PageOptions } from "~/components/core"; @@ -74,3 +74,25 @@ it("hide the component content when the user clicks on one of its actions", asyn expect(screen.queryByRole("menuitem", { name: "A dummy action" })).toBeNull(); }); + +it('should close the dropdown on click outside', async () => { + const { user } = plainRender( + + <>Item 1 + <>Item 2 + + ); + + // Open the dropdown + const toggler = screen.getByRole("button"); + await user.click(toggler); + + // Ensure the dropdown is open + screen.getByRole("menuitem", { name: "Item 2" }); + + // Click outside the dropdown + fireEvent.click(document); + + // Ensure the dropdown is closed + expect(screen.queryByRole("menuitem", { name: "Item 2" })).toBeNull(); +});