From b1c9d34eb2f16dcefc92b7ddb2e74432e9e6d582 Mon Sep 17 00:00:00 2001 From: snmln Date: Thu, 19 Dec 2024 07:59:57 -0500 Subject: [PATCH] implemenying custom hook --- .../nav/create-dynamic-nav-menu-list.tsx | 21 ++++------------- .../page-header/nav/nav-dropdown-button.tsx | 23 ++++++++++++------- .../common/page-header/nav/nav-item-cta.tsx | 8 ++----- .../common/page-header/nav/nav-item-links.tsx | 10 ++------ app/scripts/utils/use-click-outside.ts | 15 ++++++++++++ 5 files changed, 38 insertions(+), 39 deletions(-) create mode 100644 app/scripts/utils/use-click-outside.ts diff --git a/app/scripts/components/common/page-header/nav/create-dynamic-nav-menu-list.tsx b/app/scripts/components/common/page-header/nav/create-dynamic-nav-menu-list.tsx index d17bb2aa3..764853687 100644 --- a/app/scripts/components/common/page-header/nav/create-dynamic-nav-menu-list.tsx +++ b/app/scripts/components/common/page-header/nav/create-dynamic-nav-menu-list.tsx @@ -12,11 +12,6 @@ export const createDynamicNavMenuList = ( isOpen?: boolean[], setIsOpen?: SetState ): JSX.Element[] => { - const setDropDownState = () => { - if (isOpen !== undefined && setIsOpen !== undefined) { - return setIsOpen(() => isOpen.fill(false)); - } - }; return navItems.map((item, index) => { switch (item.type) { case NavItemType.DROPDOWN: @@ -28,8 +23,7 @@ export const createDynamicNavMenuList = ( isOpen, setIsOpen, index, - linkProperties, - setDropDownState + linkProperties }} /> ); @@ -37,22 +31,15 @@ export const createDynamicNavMenuList = ( case NavItemType.INTERNAL_LINK: return ( linkProperties && ( - + ) ); case NavItemType.EXTERNAL_LINK: - return ( - - ); + return ; case NavItemType.ACTION: - return ; + return ; default: return <>; diff --git a/app/scripts/components/common/page-header/nav/nav-dropdown-button.tsx b/app/scripts/components/common/page-header/nav/nav-dropdown-button.tsx index 7d475e0f4..5b7f1f5c6 100644 --- a/app/scripts/components/common/page-header/nav/nav-dropdown-button.tsx +++ b/app/scripts/components/common/page-header/nav/nav-dropdown-button.tsx @@ -1,10 +1,11 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { USWDSNavDropDownButton } from '../../uswds/header/nav-drop-down-button'; import { USWDSMenu } from '../../uswds/header/menu'; import { DropdownNavLink } from '../types'; import { createDynamicNavMenuList } from './create-dynamic-nav-menu-list'; import { SetState } from '$types/aliases'; import { LinkProperties } from '$types/veda'; +import { useClickOutside } from '$utils/use-click-outside'; interface NavDropDownButtonProps { item: DropdownNavLink; @@ -12,7 +13,6 @@ interface NavDropDownButtonProps { setIsOpen: SetState; index: number; linkProperties: LinkProperties; - setDropDownState: () => void; } export const NavDropDownButton = ({ @@ -20,8 +20,7 @@ export const NavDropDownButton = ({ isOpen, setIsOpen, index, - linkProperties, - setDropDownState + linkProperties }: NavDropDownButtonProps) => { const onToggle = (index: number, setIsOpen: SetState): void => { setIsOpen((prevIsOpen) => { @@ -34,11 +33,20 @@ export const NavDropDownButton = ({ return newIsOpen; }); }; - + const handleClickOutside = useCallback(() => { + if (isOpen[index]) { + setIsOpen((prevIsOpen) => { + const newIsOpen = [...prevIsOpen]; + newIsOpen[index] = false; + return newIsOpen; + }); + } + }, [index, isOpen, setIsOpen]); + const dropdownRef = useClickOutside(handleClickOutside); const submenuItems = createDynamicNavMenuList(item.children, linkProperties); return ( - +
onToggle(index, setIsOpen)} menuId={item.title} @@ -49,8 +57,7 @@ export const NavDropDownButton = ({ items={submenuItems} isOpen={isOpen[index]} id={`${item.id}-dropdown`} - onClick={setDropDownState()} /> - +
); }; diff --git a/app/scripts/components/common/page-header/nav/nav-item-cta.tsx b/app/scripts/components/common/page-header/nav/nav-item-cta.tsx index 66246877e..33d5b50c3 100644 --- a/app/scripts/components/common/page-header/nav/nav-item-cta.tsx +++ b/app/scripts/components/common/page-header/nav/nav-item-cta.tsx @@ -5,12 +5,10 @@ import { ActionNavItem } from '../types'; interface NavItemCTAProps { item: ActionNavItem; - setDropDownState: () => void; } export const NavItemCTA: React.FC = ({ - item, - setDropDownState + item }): JSX.Element => { const { isRevealed, show, hide } = useFeedbackModal(); return ( @@ -22,9 +20,7 @@ export const NavItemCTA: React.FC = ({ type='button' tabIndex={0} id={item.id} - onClick={() => { - setDropDownState(), show; - }} + onClick={show} style={{ background: 'none', border: 'none', cursor: 'pointer' }} > {item.title} diff --git a/app/scripts/components/common/page-header/nav/nav-item-links.tsx b/app/scripts/components/common/page-header/nav/nav-item-links.tsx index a0b13842a..76537c885 100644 --- a/app/scripts/components/common/page-header/nav/nav-item-links.tsx +++ b/app/scripts/components/common/page-header/nav/nav-item-links.tsx @@ -4,18 +4,15 @@ import { LinkProperties } from '$types/veda'; interface NavItemExternalLinkProps { item: ExternalNavLink; - setDropDownState: () => void; } interface NavItemInternalLinkProps { item: InternalNavLink; linkProperties: LinkProperties; - setDropDownState: () => void; } export const NavItemExternalLink: React.FC = ({ - item, - setDropDownState + item }): JSX.Element => { return ( = ({ href={item.href} className='usa-nav__link' id={item.id} - onClick={() => setDropDownState()} > {item.title} @@ -34,8 +30,7 @@ export const NavItemExternalLink: React.FC = ({ export const NavItemInternalLink: React.FC = ({ item, - linkProperties, - setDropDownState + linkProperties }): JSX.Element | null => { if (linkProperties.LinkElement) { const path = { @@ -48,7 +43,6 @@ export const NavItemInternalLink: React.FC = ({ {...path} className='usa-nav__link' id={item.id} - onClick={() => setDropDownState()} > {item.title} diff --git a/app/scripts/utils/use-click-outside.ts b/app/scripts/utils/use-click-outside.ts new file mode 100644 index 000000000..4c0247903 --- /dev/null +++ b/app/scripts/utils/use-click-outside.ts @@ -0,0 +1,15 @@ +import { useEffect, useRef } from 'react'; +export const useClickOutside = (onClose: () => void) => { + const ref = useRef(null); + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (ref.current && !ref.current.contains(event.target as Node)) { + onClose(); + } + }; + document.addEventListener('click', handleClickOutside, true); + return () => + document.removeEventListener('click', handleClickOutside, true); + }, [onClose]); + return ref; +}; \ No newline at end of file