From 8b884ab68113324619dda58d57ad3297f77d4fcf Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Wed, 10 Jan 2024 12:21:24 +0530 Subject: [PATCH] chore: modal and dropdown improvement (#3332) * dev: dropdown key down custom hook added * chore: plane ui dropdowns updated * chore: cycle and module tab index added in modals * chore: view and page tab index added in modals * chore: issue modal tab indexing added * chore: project modal tab indexing added * fix: build fix * build-error: build error in pages new structure and reverted back to old page structure --------- Co-authored-by: gurusainath --- packages/ui/src/dropdowns/custom-menu.tsx | 80 ++-- .../ui/src/dropdowns/custom-search-select.tsx | 168 ++++---- packages/ui/src/dropdowns/custom-select.tsx | 65 +++- packages/ui/src/dropdowns/helper.tsx | 1 + .../ui/src/hooks/use-dropdown-key-down.tsx | 24 ++ .../src/hooks/use-outside-click-detector.tsx | 19 + web/components/core/image-picker-popover.tsx | 34 +- web/components/cycles/form.tsx | 9 +- web/components/dropdowns/cycle.tsx | 114 +++--- web/components/dropdowns/date.tsx | 55 ++- web/components/dropdowns/estimate.tsx | 114 +++--- .../dropdowns/member/project-member.tsx | 113 +++--- web/components/dropdowns/member/types.d.ts | 1 + web/components/dropdowns/module.tsx | 114 +++--- web/components/dropdowns/priority.tsx | 109 +++--- web/components/dropdowns/project.tsx | 114 +++--- web/components/dropdowns/state.tsx | 114 +++--- web/components/emoji-icon-picker/types.d.ts | 1 + .../filters/header/helpers/dropdown.tsx | 38 +- web/components/issues/issue-modal/form.tsx | 24 +- web/components/issues/select/label.tsx | 257 ++++++------ web/components/modules/form.tsx | 13 +- web/components/modules/select/status.tsx | 4 +- web/components/pages/page-form.tsx | 8 +- .../project/create-project-modal.tsx | 12 +- web/components/views/form.tsx | 8 +- web/hooks/use-dropdown-key-down.tsx | 24 ++ web/store/page.store.ts | 365 ++++++++++++++---- web/store/project-page.store.ts | 12 +- web/store/root.store.ts | 3 +- 30 files changed, 1300 insertions(+), 717 deletions(-) create mode 100644 packages/ui/src/hooks/use-dropdown-key-down.tsx create mode 100644 packages/ui/src/hooks/use-outside-click-detector.tsx create mode 100644 web/hooks/use-dropdown-key-down.tsx diff --git a/packages/ui/src/dropdowns/custom-menu.tsx b/packages/ui/src/dropdowns/custom-menu.tsx index 7c2d95a18..d6b0281ce 100644 --- a/packages/ui/src/dropdowns/custom-menu.tsx +++ b/packages/ui/src/dropdowns/custom-menu.tsx @@ -2,6 +2,9 @@ import * as React from "react"; // react-poppper import { usePopper } from "react-popper"; +// hooks +import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down"; +import useOutsideClickDetector from "../hooks/use-outside-click-detector"; // headless ui import { Menu } from "@headlessui/react"; // type @@ -27,16 +30,35 @@ const CustomMenu = (props: ICustomMenuDropdownProps) => { verticalEllipsis = false, width = "auto", menuButtonOnClick, + tabIndex, } = props; const [referenceElement, setReferenceElement] = React.useState(null); const [popperElement, setPopperElement] = React.useState(null); + const [isOpen, setIsOpen] = React.useState(false); + // refs + const dropdownRef = React.useRef(null); const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: placement ?? "auto", }); + + const openDropdown = () => { + setIsOpen(true); + if (referenceElement) referenceElement.focus(); + }; + const closeDropdown = () => setIsOpen(false); + const handleKeyDown = useDropdownKeyDown(openDropdown, closeDropdown, isOpen); + useOutsideClickDetector(dropdownRef, closeDropdown); + return ( - + {({ open }) => ( <> {customButton ? ( @@ -44,7 +66,10 @@ const CustomMenu = (props: ICustomMenuDropdownProps) => { diff --git a/packages/ui/src/dropdowns/custom-search-select.tsx b/packages/ui/src/dropdowns/custom-search-select.tsx index 759a88349..f9af00c93 100644 --- a/packages/ui/src/dropdowns/custom-search-select.tsx +++ b/packages/ui/src/dropdowns/custom-search-select.tsx @@ -1,7 +1,10 @@ -import React, { useState } from "react"; +import React, { useRef, useState } from "react"; // react-popper import { usePopper } from "react-popper"; +// hooks +import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down"; +import useOutsideClickDetector from "../hooks/use-outside-click-detector"; // headless ui import { Combobox } from "@headlessui/react"; // icons @@ -29,11 +32,15 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { optionsClassName = "", value, width = "auto", + tabIndex, } = props; const [query, setQuery] = useState(""); const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); + const [isOpen, setIsOpen] = useState(false); + // refs + const dropdownRef = useRef(null); const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: placement ?? "bottom-start", @@ -50,8 +57,23 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { if (multiple) comboboxProps.multiple = true; + const openDropdown = () => { + setIsOpen(true); + if (referenceElement) referenceElement.focus(); + }; + const closeDropdown = () => setIsOpen(false); + const handleKeyDown = useDropdownKeyDown(openDropdown, closeDropdown, isOpen); + useOutsideClickDetector(dropdownRef, closeDropdown); + return ( - + {({ open }: { open: boolean }) => { if (open && onOpen) onOpen(); @@ -67,6 +89,7 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer hover:bg-custom-background-80" } ${customButtonClassName}`} + onClick={openDropdown} > {customButton} @@ -83,86 +106,89 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer hover:bg-custom-background-80" } ${buttonClassName}`} + onClick={openDropdown} > {label} {!noChevron && !disabled &&