I have a React application where I’m using the @nextui-org/react
library to implement a sidebar using the Drawer component. Within my main content area, I have an input field. The issue is that when the sidebar is open, clicking on the input field doesn’t focus it. Instead of the input gaining focus, one of the items within the open sidebar’s navigation list becomes focused.
This prevents users from interacting with the input while the sidebar is visible. Removing the DrawerContent
component from the Sidebar implementation resolves the issue, suggesting the problem lies within that component or its interaction with the Drawer. The presence of <span data-focus-scope-start="true" hidden="" aria-hidden="true"></span>
within the Drawer when open makes me suspect it’s related to focus management.
I initially suspected the portalContainer
prop was causing an issue with event handling, so I removed it, but the problem persisted. I also checked the z-index
of the relevant elements using the browser’s developer tools to rule out any stacking order issues.
Through testing, I isolated the problem to the DrawerContent
component. When the sidebar is open and I click the input, I expect the input field to receive focus, allowing the user to type. However, the actual result is that the input does not get focused, and instead, an item in the sidebar’s navigation receives the focus. I also noticed the <span data-focus-scope-start="true" ...>
element and suspect it is involved in incorrectly managing focus.
LayoutPanel.js:
import React from "react";
import { Outlet, useLocation, useNavigate } from "react-router";
import Sidebar from "../components/Sidebar";
import {
Button,
Card,
CardBody,
CardHeader,
Divider,
useDisclosure,
} from "@nextui-org/react";
import { Icon } from "@iconify/react";
import { images } from "../utils/images";
const LayoutPanel = () => {
const { isOpen, onOpen, onOpenChange } = useDisclosure();
const navigate = useNavigate();
const location = useLocation();
return (
<div>
{/* <HeaderNavigation /> */}
<div className="flex w-full">
<Sidebar isOpen={isOpen} onOpenChange={onOpenChange} />
<Card className="flex-1 m-3 min-h-[calc(100vh-24px)]">
<CardHeader className="h-[76px]">
<div className="flex items-center gap-2">
{!isOpen && (
<Button isIconOnly variant="light" onPress={onOpenChange}>
<Icon icon="solar:siderbar-outline" width={24} />
</Button>
)}
<img
src={images.logo}
alt="logo"
className="object-contain h-6 dark:invert"
/>
</div>
</CardHeader>
<Divider />
<CardBody className="container mx-auto max-w-7xl text-start">
<Outlet />
</CardBody>
</Card>
</div>
{/* <Footer /> */}
</div>
);
};
export default LayoutPanel;
Sidebar.js:
import React from "react";
import {
Drawer,
DrawerContent,
DrawerHeader,
DrawerBody,
Listbox,
ListboxItem,
Accordion,
Button,
AccordionItem,
cn,
Divider,
} from "@nextui-org/react";
import sidebarNavigation from "../utils/sidebarNavigation";
import { Icon } from "@iconify/react";
const Sidebar = ({ isOpen, onOpenChange }) => {
return (
<div
id="sidebar"
className={cn(
"transition-all absolute md:relative duration-300 ease-in-out overflow-hidden",
isOpen ? "w-[336px]" : "w-0"
)}
>
<Drawer
isDismissable={false}
hideCloseButton
isKeyboardDismissDisabled={false}
portalContainer={document.getElementById("sidebar") || undefined}
backdrop={window.innerWidth < 768 && isOpen ? "blur" : "transparent"}
classNames={{
wrapper: "!items-stretch w-auto relative",
base: "data-[placement=right]:sm:my-3 data-[placement=left]:sm:my-3 rounded-e-large rounded-s-none",
body: "pe-0",
header: "p-[calc(1rem+2px)] ",
}}
shouldBlockScroll
isOpen={isOpen}
onOpenChange={onOpenChange}
>
<DrawerContent>
<DrawerHeader className="flex justify-between gap-3 ">
<h3>منو</h3>
<Button isIconOnly variant="light" onPress={onOpenChange}>
<Icon icon="solar:siderbar-outline" width={24} />
</Button>
</DrawerHeader>
<Divider />
<DrawerBody>
<Listbox
variant="flat"
className="overflow-y-auto pe-3 ps-0"
>
{sidebarNavigation.map((menuItem) =>
menuItem.subItems ? (
<ListboxItem key={menuItem.title}>
<Accordion showDivider={false} className="space-y-3">
<AccordionItem
key={menuItem.title}
aria-label={menuItem.title}
startContent={
<div
className={`flex items-center rounded-small justify-center ${menuItem.color} w-9 h-9`}
>
<img
src={menuItem.icon}
className="object-contain invert"
width={24}
alt={menuItem.title}
/>
</div>
}
subtitle={menuItem.subtitle}
classNames={{
trigger: "p-0",
content: "py-0 ps-3",
}}
title={menuItem.title}
>
<Listbox
variant="flat"
aria-label={`زیرمنو ${menuItem.title}`}
classNames={{
list: cn("border-s border-default-200 ps-4"),
}}
>
{menuItem.subItems.map((subItem) => (
<ListboxItem
key={subItem.title}
textValue={subItem.title}
href={subItem.path}
startContent={
<div
className={`flex items-center rounded-small justify-center ${menuItem.color} w-7 h-7`}
>
<img
src={subItem.icon}
width={20}
className="object-contain"
alt={subItem.title}
/>
</div>
}
>
<span>{subItem.title}</span>
</ListboxItem>
))}
</Listbox>
</AccordionItem>
</Accordion>
</ListboxItem>
) : (
<ListboxItem
title={menuItem.title}
description={menuItem.subtitle}
classNames={{
base: "gap-3",
}}
startContent={
<div className="ms-2">
<div
className={`flex items-center rounded-small justify-center ${menuItem.color} w-9 h-9`}
>
<img
src={menuItem.icon}
width={20}
className="invert"
alt={menuItem.title}
/>
</div>
</div>
}
>
{menuItem.title}
</ListboxItem>
)
)}
</Listbox>
</DrawerBody>
</DrawerContent>
</Drawer>
</div>
);
};
export default Sidebar;
FormFastGroup.js:
import { Controller, useForm } from "react-hook-form";
import { Button, Form, Input } from "@nextui-org/react";
const FormFastGroup = () => {
const { handleSubmit, control } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<Form
className="w-full max-w-xs flex flex-col gap-4"
onSubmit={handleSubmit(onSubmit)}
>
<Controller
control={control}
name="name"
render={({
field: { name, value, onChange, onBlur, ref },
fieldState: { invalid, error },
}) => (
<Input
ref={ref}
isRequired
errorMessage={error?.message}
// Let React Hook Form handle validation instead of the browser.
validationBehavior="aria"
isInvalid={invalid}
label="Name"
name={name}
value={value}
onBlur={onBlur}
onChange={onChange}
/>
)}
rules={{ required: "Name is required." }}
/>
<Button type="submit">Submit</Button>
</Form>
);
};
export default FormFastGroup;
FastGroup.js:
import React from "react";
import FormFastGroup from "../components/FormFastGroup";
const FastGroup = () => {
return (
<div className="flex flex-col gap-4">
<h3 className="font-bold text-xl">ارسال سریع و گروهی</h3>
<div>
<FormFastGroup />
</div>
</div>
);
};
export default FastGroup;