|
1 | 1 | /* eslint-disable react/prop-types */ |
2 | | -import { useState } from "react"; |
| 2 | +import { useState, useRef, useEffect } from "react"; |
3 | 3 |
|
4 | | -const DropdownMenu = ({ options, buttonLabel, executeAction }) => { |
| 4 | +const DropdownMenu = ({ |
| 5 | + options, |
| 6 | + buttonLabel, |
| 7 | + executeAction, |
| 8 | + className = "w-full bg-[#356C99] text-white py-2 px-4 rounded shadow-lg flex justify-between items-center", |
| 9 | +}) => { |
5 | 10 | const [isOpen, setIsOpen] = useState(false); |
| 11 | + const dropdownRef = useRef(null); |
6 | 12 |
|
7 | 13 | const handleToggle = () => { |
8 | | - setIsOpen(!isOpen); |
| 14 | + setIsOpen((prev) => !prev); |
9 | 15 | }; |
10 | 16 |
|
11 | 17 | const handleAction = async (action) => { |
12 | 18 | await executeAction(action); |
13 | 19 | setIsOpen(false); |
14 | 20 | }; |
15 | 21 |
|
| 22 | + // Close dropdown when clicking outside |
| 23 | + useEffect(() => { |
| 24 | + const handleClickOutside = (event) => { |
| 25 | + if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { |
| 26 | + setIsOpen(false); |
| 27 | + } |
| 28 | + }; |
| 29 | + |
| 30 | + document.addEventListener("mousedown", handleClickOutside); |
| 31 | + return () => { |
| 32 | + document.removeEventListener("mousedown", handleClickOutside); |
| 33 | + }; |
| 34 | + }, []); |
| 35 | + |
16 | 36 | return ( |
17 | | - <div className="relative inline-block w-max"> |
| 37 | + <div ref={dropdownRef} className="relative inline-block w-max"> |
18 | 38 | <button |
19 | 39 | onClick={handleToggle} |
20 | | - className="w-full bg-[#356C99] text-white py-2 px-4 rounded shadow-lg flex justify-between items-center" |
| 40 | + className={className} |
| 41 | + aria-haspopup="true" |
| 42 | + aria-expanded={isOpen} |
21 | 43 | > |
22 | 44 | {buttonLabel} |
23 | | - <span className="ml-2">▼</span> {/* Dropdown arrow */} |
| 45 | + <span className="ml-2">▼</span> |
24 | 46 | </button> |
| 47 | + |
25 | 48 | {isOpen && ( |
26 | | - <div className="absolute w-full mt-1 bg-white border border-[#356C99] rounded-lg shadow-lg z-10"> |
| 49 | + <div |
| 50 | + className="absolute w-full mt-1 bg-white border border-[#356C99] rounded-lg shadow-lg z-10" |
| 51 | + role="menu" |
| 52 | + > |
27 | 53 | {options?.length ? ( |
28 | 54 | options.map((option) => ( |
29 | 55 | <div |
30 | 56 | key={option.label} |
31 | 57 | onClick={() => handleAction(option)} |
32 | | - className="w-full text-left py-2 px-4 cursor-pointer hover:bg-[#0D486C] text-[#356C99] hover:text-white" |
| 58 | + tabIndex={0} |
| 59 | + className="w-full text-left py-2 px-4 cursor-pointer hover:bg-[#0D486C] text-[#356C99] hover:text-white focus:outline-none focus:bg-[#0D486C] focus:text-white" |
| 60 | + role="menuitem" |
33 | 61 | > |
34 | 62 | {option.label} |
35 | 63 | </div> |
|
0 commit comments