From 05f4fa90b8280e6c9608d49247de35859d8eb38f Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Wed, 31 Jan 2024 22:40:10 -0500 Subject: [PATCH] fix: click outside select input in popover event bubbling --- app/components/InputSelect.tsx | 17 ++++++++++++++--- app/components/Popover.tsx | 15 ++++++++++++++- app/hooks/useOnClickOutside.ts | 4 ++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/app/components/InputSelect.tsx b/app/components/InputSelect.tsx index 271ca9907..c8ad61d1a 100644 --- a/app/components/InputSelect.tsx +++ b/app/components/InputSelect.tsx @@ -14,6 +14,7 @@ import Button, { Inner } from "~/components/Button"; import Text from "~/components/Text"; import useMenuHeight from "~/hooks/useMenuHeight"; import useMobile from "~/hooks/useMobile"; +import useOnClickOutside from "~/hooks/useOnClickOutside"; import { fadeAndScaleIn } from "~/styles/animations"; import { Position, @@ -76,9 +77,9 @@ const InputSelect = (props: Props) => { selectedValue: value, }); - const popOver = useSelectPopover({ + const popover = useSelectPopover({ ...select, - hideOnClickOutside: true, + hideOnClickOutside: false, preventBodyScroll: true, disabled, }); @@ -107,6 +108,16 @@ const InputSelect = (props: Props) => { (option) => option.value === select.selectedValue ); + // Custom click outside handling rather than using `hideOnClickOutside` from reakit so that we can + // prevent event bubbling. + useOnClickOutside(contentRef, (event) => { + if (select.visible) { + event.stopPropagation(); + event.preventDefault(); + select.hide(); + } + }); + React.useEffect(() => { previousValue.current = value; select.setSelectedValue(value); @@ -156,7 +167,7 @@ const InputSelect = (props: Props) => { )} - + {(props: InnerProps) => { const topAnchor = props.style?.top === "0"; const rightAnchor = props.placement === "bottom-end"; diff --git a/app/components/Popover.tsx b/app/components/Popover.tsx index 268d75fec..0f639bf10 100644 --- a/app/components/Popover.tsx +++ b/app/components/Popover.tsx @@ -6,6 +6,7 @@ import breakpoint from "styled-components-breakpoint"; import { depths, s } from "@shared/styles"; import useKeyDown from "~/hooks/useKeyDown"; import useMobile from "~/hooks/useMobile"; +import useOnClickOutside from "~/hooks/useOnClickOutside"; import { fadeAndScaleIn } from "~/styles/animations"; type Props = PopoverProps & { @@ -29,6 +30,7 @@ const Popover: React.FC = ({ mobilePosition, ...rest }: Props) => { + const contentRef = React.useRef(null); const isMobile = useMobile(); // Custom Escape handler rather than using hideOnEsc from reakit so we can @@ -46,6 +48,16 @@ const Popover: React.FC = ({ } ); + // Custom click outside handling rather than using `hideOnClickOutside` from reakit so that we can + // respect event.defaultPrevented. + useOnClickOutside(contentRef, (event) => { + if (rest.visible && !event.defaultPrevented) { + event.stopPropagation(); + event.preventDefault(); + rest.hide(); + } + }); + if (isMobile) { return ( @@ -62,8 +74,9 @@ const Popover: React.FC = ({ } return ( - +