Skip to content

Commit da38035

Browse files
committed
Enhance dropdown behavior in ConnectWallet and MobileNavigation components
- Added state management for dropdown visibility in ConnectWallet to prevent conflicts with open Sheets/Dialogs. - Implemented periodic checks to close the dropdown when a Sheet/Dialog is opened, improving accessibility and user experience. - Updated MobileNavigation to close any open dropdowns when the navigation menu is opened, ensuring consistent behavior across components. - Enhanced focus management in MobileNavigation to prevent aria-hidden conflicts and improve keyboard navigation.
1 parent 6a907ce commit da38035

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

src/components/common/cardano-objects/connect-wallet.tsx

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,43 @@ function ConnectWalletContent({
102102

103103
// Track wallet detection state
104104
const [detectingWallets, setDetectingWallets] = useState(true);
105+
const [dropdownOpen, setDropdownOpen] = useState(false);
105106
const walletsRef = useRef(wallets);
106107
const hasInitializedPersist = useRef(false);
107108
const hasAttemptedAutoConnect = useRef(false);
108109

110+
// Check if any Sheet/Dialog is open to prevent dropdown from opening
111+
const checkIfSheetOpen = useCallback(() => {
112+
if (typeof window === "undefined") return false;
113+
const sheets = document.querySelectorAll('[data-radix-dialog-content], [data-radix-sheet-content]');
114+
return Array.from(sheets).some(
115+
(sheet) => sheet.getAttribute('data-state') === 'open'
116+
);
117+
}, []);
118+
119+
// Close dropdown when a Sheet/Dialog opens to prevent aria-hidden conflicts
120+
useEffect(() => {
121+
if (!dropdownOpen) return;
122+
123+
const checkForOpenSheets = () => {
124+
if (checkIfSheetOpen()) {
125+
setDropdownOpen(false);
126+
}
127+
};
128+
129+
// Check periodically when dropdown is open
130+
const interval = setInterval(checkForOpenSheets, 100);
131+
return () => clearInterval(interval);
132+
}, [dropdownOpen, checkIfSheetOpen]);
133+
134+
// Prevent dropdown from opening if Sheet is already open
135+
const handleDropdownOpenChange = useCallback((open: boolean) => {
136+
if (open && checkIfSheetOpen()) {
137+
return; // Don't open if Sheet is open
138+
}
139+
setDropdownOpen(open);
140+
}, [checkIfSheetOpen]);
141+
109142

110143

111144
// Keep wallets ref in sync
@@ -317,7 +350,7 @@ function ConnectWalletContent({
317350
};
318351

319352
return (
320-
<DropdownMenu>
353+
<DropdownMenu open={dropdownOpen} onOpenChange={handleDropdownOpenChange}>
321354
<DropdownMenuTrigger asChild>
322355
<Button
323356
variant="secondary"

src/components/ui/mobile-navigation.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ export function MobileNavigation({ showWalletMenu, isLoggedIn, walletId, fallbac
3232
const router = useRouter();
3333
const [open, setOpen] = useState(false);
3434

35+
// Close any open dropdowns when sheet opens to prevent aria-hidden conflicts
36+
React.useEffect(() => {
37+
if (open) {
38+
// Close any open Radix dropdown menus
39+
const dropdownTriggers = document.querySelectorAll('[data-radix-dropdown-menu-trigger]');
40+
dropdownTriggers.forEach((trigger) => {
41+
const button = trigger as HTMLElement;
42+
if (button.getAttribute('data-state') === 'open') {
43+
button.click();
44+
}
45+
});
46+
}
47+
}, [open]);
48+
3549
return (
3650
<>
3751
{/* Custom overlay - rendered outside of Sheet via Portal */}
@@ -108,6 +122,22 @@ export function MobileNavigation({ showWalletMenu, isLoggedIn, walletId, fallbac
108122
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left"
109123
)}
110124
style={{ top: '56px', height: 'calc(100vh - 56px)' }}
125+
onOpenAutoFocus={(e) => {
126+
// Blur any focused elements in the header to prevent aria-hidden conflicts
127+
const header = document.querySelector('[data-header="main"]');
128+
if (header) {
129+
const focusedElement = header.querySelector(':focus');
130+
if (focusedElement && focusedElement instanceof HTMLElement) {
131+
focusedElement.blur();
132+
}
133+
}
134+
// Focus the first focusable element in the sheet content
135+
e.preventDefault();
136+
const firstFocusable = e.currentTarget.querySelector('a, button, [tabindex]:not([tabindex="-1"])');
137+
if (firstFocusable && firstFocusable instanceof HTMLElement) {
138+
firstFocusable.focus();
139+
}
140+
}}
111141
onInteractOutside={(e) => {
112142
// Check if click was in header
113143
const header = document.querySelector('[data-header="main"]');

0 commit comments

Comments
 (0)