You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When opening a Radix UI or , the body scroll (mouse wheel scrolling) is completely blocked. Even when modal={false} is set for the , or forceMount is used in the , the issue persists. This prevents users from scrolling the background content while these components are open.
Affected component/components
"use client";importReact,{useEffect}from"react";import{zodResolver}from"@hookform/resolvers/zod";import{useForm}from"react-hook-form";import{z}from"zod";import{Button}from"@/components/ui/button";import{CalendarIcon,Check,CheckIcon,ChevronsUpDown,Plus,RefreshCcw,X,}from"lucide-react";import{Dialog,DialogContent,DialogDescription,DialogFooter,DialogHeader,DialogTitle,DialogTrigger,}from"@/components/ui/dialog";import{Input}from"@/components/ui/input";import{Form,FormControl,FormDescription,FormField,FormItem,FormLabel,FormMessage,}from"@/components/ui/form";import{Popover,PopoverContent,PopoverTrigger,}from"@/components/ui/popover";import{generateCouponCode}from"@/lib/generateCoupon";import{Tooltip,TooltipContent,TooltipTrigger,}from"@/components/ui/tooltip";import{Command,CommandGroup,CommandItem,CommandList,}from"@/components/ui/command";import{useFormatOptions}from"@/hooks/useFormatOptions";import{dummyProducts}from"@/data/dummyProducts";import{Badge}from"@/components/ui/badge";import{useSelection}from"@/hooks/use-selection";import{Calendar}from"@/components/ui/calendar";import{cn}from"@/lib/utils";import{format}from"date-fns";import{Avatar,AvatarFallback,AvatarImage}from"@/components/ui/avatar";constformSchema=z.object({name: z.string().min(1,"Name is required"),products: z.array(z.object({value: z.string().min(1,"Product value is required"),label: z.string().min(1,"Product label is required"),})).min(1,"At least one product is required"),code: z.string().min(1,"Code is required"),discount: z.object({type: z.string({required_error: "Discount type is required"}).refine((val)=>["cash","percent"].includes(val),{message: "Discount type must be 'cash' or 'percent'",}),value: z.string().min(1,"Discount value is required").regex(/^\d+$/,"Discount value must be a valid number"),}),startDate: z.date({required_error: "A start date is required."}),endDate: z.date({required_error: "An end date is required."}),redemptionLimit: z.string().optional(),});constdiscountTypes=[{value: "cash",label: "$",},{value: "percent",label: "%",},];constDiscountCreateForm=()=>{constform=useForm<z.infer<typeofformSchema>>({resolver: zodResolver(formSchema),mode: "onChange",defaultValues: {name: "",products: [{value: "",label: "",},],code: "",discount: {type: "cash",value: "",},startDate: newDate(),endDate: undefined,redemptionLimit: "",},});functiononSubmit(data: z.infer<typeofformSchema>){console.log(data);}const[open,setOpen]=React.useState(false);constoptions=useFormatOptions({data: dummyProducts,value: "barcode",label: "item",options: {value: "all",label: "All"},});const{ value, onSelect, onRemove, shouldSelect }=useSelection(options);useEffect(()=>{if(value){form.setValue("products",value);}},[form,value]);constproductImage=(barcode: string)=>dummyProducts.find((product)=>product.barcode===barcode);useEffect(()=>{consthandleScrollUnlock=()=>{document.body.style.overflow="auto";// Allow scrolling};handleScrollUnlock();return()=>{document.body.style.overflow="";// Restore when closing};},[]);return(<Dialog><DialogTriggerasChild><Buttonsize="sm"><Plus/>
New discount
</Button></DialogTrigger><DialogContentclassName="w-[90vw] max-w-[500px] p-0 gap-0"><Form{...form}><formonSubmit={form.handleSubmit(onSubmit)}><DialogHeaderclassName="p-4 border-b"><DialogTitle>Add discount</DialogTitle><DialogDescription/></DialogHeader><divclassName="p-4 whisper-scroll max-h-[calc(100vh-150px)]"><divclassName="space-y-4"><FormFieldcontrol={form.control}name="name"render={({ field })=>(<FormItem><FormLabel>Name</FormLabel><FormControl><Input{...field}/></FormControl><FormDescription>
This will appear on your customer’s invoice.
</FormDescription><FormMessage/></FormItem>)}/><FormFieldcontrol={form.control}name="products"render={({ field })=>(<FormItem><FormLabel>Products</FormLabel><Popover><PopoverTriggerasChild><Buttonvariant="outline"role="combobox"aria-expanded={open}className="w-full justify-start h-full flex-wrap">{field.value.length>0 ? (<>{value.slice(0,5).map((option)=>(<Badgevariant="outline"key={option.value}className="flex items-center gap-1"><AvatarclassName="size-4"><AvatarImagesrc={productImage(option.value)?.img.src}/><AvatarFallback>{productImage(option.value)?.item[0]}</AvatarFallback></Avatar>{option.label}<spanclassName="cursor-pointer hover:text-red-500"onClick={(e)=>{e.stopPropagation();onRemove(option.value);}}><X/></span></Badge>))}{value.length>5&&(<Badgevariant="outline"className="font-semibold">
+{value.length-5} more
</Badge>)}</>) : (<divclassName="text-muted-foreground">
Select options (multi-select)...
</div>)}<ChevronsUpDownclassName="text-muted-foreground ml-auto"/></Button></PopoverTrigger><PopoverContentclassName="w-(--radix-popover-trigger-width) p-0"align="start"><Command><CommandList><CommandGroup>{options.map((option)=>(<CommandItemkey={option.value}value={option.value}onSelect={()=>onSelect(option.value)}><divclassName="border-input data-[selected=true]:border-primary data-[selected=true]:bg-primary data-[selected=true]:text-primary-foreground pointer-events-none size-4 shrink-0 rounded-[4px] border transition-all select-none *:[svg]:opacity-0 data-[selected=true]:*:[svg]:opacity-100"data-selected={shouldSelect(option.value)}><CheckIconclassName="size-3.5 text-current"/></div><AvatarclassName="size-5"><AvatarImagesrc={productImage(option.value)?.img.src}/><AvatarFallback>{productImage(option.value)?.item[0]}</AvatarFallback></Avatar>{option.label}</CommandItem>))}</CommandGroup></CommandList></Command></PopoverContent></Popover><FormDescription>
Select the products this discount applies to.
</FormDescription><FormMessage/></FormItem>)}/><FormFieldcontrol={form.control}name="code"render={({ field })=>(<FormItem><FormLabel>Code</FormLabel><divclassName="relative"><Tooltip><TooltipTriggerclassName="cursor-pointer absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"onClick={(e)=>{e.preventDefault();form.setValue("code",generateCouponCode());}}type="button"><RefreshCcwsize={16}/></TooltipTrigger><TooltipContent>Generate code</TooltipContent></Tooltip><FormControl><Input{...field}className="pr-8"/></FormControl></div><FormDescription>
The code your customers will enter during checkout.
</FormDescription><FormMessage/></FormItem>)}/><FormFieldcontrol={form.control}name="discount.value"render={({ field })=>(<FormItem><FormLabel>Amount</FormLabel><divclassName="relative"><Popoveropen={open}onOpenChange={setOpen}><PopoverTriggerasChild><Buttonvariant="ghost"role="combobox"aria-expanded={open}size="sm"className="absolute right-0.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground cursor-pointer"title="Click to switch the discount type (Cash or Percent)">{form.getValues("discount.type")&&discountTypes.find((item)=>item.value===form.getValues("discount.type"))?.label}</Button></PopoverTrigger><PopoverContentalign="end"className="w-16 p-0"><Command><CommandList><CommandGroup>{discountTypes.map((item)=>(<CommandItemkey={item.value}value={item.value}onSelect={()=>{form.setValue("discount.type",item.value);setOpen(false);}}>{item.label}<Checksize={16}className={cn("mr-0",form.getValues("discount.type")===item.value
? "opacity-100"
: "opacity-0")}/></CommandItem>))}</CommandGroup></CommandList></Command></PopoverContent></Popover><FormControl><Input{...field}className="pr-8"/></FormControl></div><FormMessage/></FormItem>)}/><divclassName="grid gap-4 md:grid-cols-2"><FormFieldcontrol={form.control}name="startDate"render={({ field })=>(<FormItemclassName="flex flex-col"><FormLabel>Date of birth</FormLabel><Popover><PopoverTriggerasChild><FormControl><Buttonvariant={"outline"}className={cn("w-full pl-3 text-left font-normal",!field.value&&"text-muted-foreground")}>{field.value ? (format(field.value,"PPP")) : (<span>Pick a date</span>)}<CalendarIconsize={16}className="ml-auto opacity-50"/></Button></FormControl></PopoverTrigger><PopoverContentclassName="w-auto p-0"align="start"><Calendarmode="single"selected={field.value}onSelect={field.onChange}disabled={(date)=>date<newDate(newDate().setHours(0,0,0,0))}/></PopoverContent></Popover><FormMessage/></FormItem>)}/><FormFieldcontrol={form.control}name="endDate"render={({ field })=>(<FormItemclassName="flex flex-col"><FormLabel>End date</FormLabel><Popover><PopoverTriggerasChild><FormControl><Buttonvariant="outline"className={cn("w-full pl-3 text-left font-normal",!field.value&&"text-muted-foreground")}>{field.value ? (format(field.value,"PPP")) : (<span>Pick a date</span>)}<CalendarIconsize={16}className="ml-auto opacity-50"/></Button></FormControl></PopoverTrigger><PopoverContentclassName="w-auto p-0"align="start"><Calendarmode="single"selected={field.value}onSelect={field.onChange}/></PopoverContent></Popover><FormMessage/></FormItem>)}/></div><FormFieldcontrol={form.control}name="redemptionLimit"render={({ field })=>(<FormItem><FormLabel>Limit the number of redemptions?</FormLabel><FormControl><Inputtype="number"{...field}/></FormControl><FormDescription>
Limit applies across all customers, not per customer.
</FormDescription><FormMessage/></FormItem>)}/></div></div><DialogFooterclassName="p-4 pt-0"><Buttontype="submit">Save changes</Button></DialogFooter></form></Form></DialogContent></Dialog>);};exportdefaultDiscountCreateForm;
How to reproduce
Description
When opening a Radix UI <Dialog> or <Popover>, the body scroll gets locked, preventing users from scrolling the page with the mouse wheel. This behavior is problematic for pages with long content.
Steps to Reproduce
Open a Radix UI <Dialog> or <Popover>.
Try to scroll the background using the mouse wheel.
Notice that the body scroll is locked, preventing page navigation.
Expected Behavior
The body should remain scrollable when the <Dialog> or <Popover> is open.
The user should be able to scroll the page content with the mouse wheel while interacting with these components.
Possible Fixes
Manually override document.body.style.overflow when the component is opened.
Use the body-scroll-lock package to prevent unintended scroll locking while maintaining focus trapping.
Check if Radix UI has built-in options to disable body scroll locking.
Describe the bug
Project Link: https://mun-xi.vercel.app/seller/dashboard/discounts
When opening a Radix UI or , the body scroll (mouse wheel scrolling) is completely blocked. Even when modal={false} is set for the , or forceMount is used in the , the issue persists. This prevents users from scrolling the background content while these components are open.
Affected component/components
How to reproduce
Description
When opening a Radix UI
<Dialog>
or<Popover>
, the body scroll gets locked, preventing users from scrolling the page with the mouse wheel. This behavior is problematic for pages with long content.Steps to Reproduce
<Dialog>
or<Popover>
.Expected Behavior
<Dialog>
or<Popover>
is open.Possible Fixes
document.body.style.overflow
when the component is opened.body-scroll-lock
package to prevent unintended scroll locking while maintaining focus trapping.Environment
@radix-ui/react-dialog
,@radix-ui/react-popover
)Additional Context
This issue affects usability, especially for users who need to interact with content outside the modal while keeping it open.
Codesandbox/StackBlitz link
No response
Logs
System Info
Before submitting
The text was updated successfully, but these errors were encountered: