From 7835fc7a4c3d1530f8c41780db6a891945a5689f Mon Sep 17 00:00:00 2001 From: ibolton336 Date: Tue, 23 Jan 2024 16:04:16 -0500 Subject: [PATCH] integrate text input into filter for multi select --- .../MultiselectFilterControl.tsx | 198 ++++++++++++++++-- 1 file changed, 175 insertions(+), 23 deletions(-) diff --git a/client/src/app/components/FilterToolbar/MultiselectFilterControl.tsx b/client/src/app/components/FilterToolbar/MultiselectFilterControl.tsx index 271fb152e8..66178bf866 100644 --- a/client/src/app/components/FilterToolbar/MultiselectFilterControl.tsx +++ b/client/src/app/components/FilterToolbar/MultiselectFilterControl.tsx @@ -1,12 +1,15 @@ import * as React from "react"; import { - Badge, + Button, MenuToggle, MenuToggleElement, Select, SelectGroup, SelectOption, SelectOptionProps, + TextInputGroup, + TextInputGroupMain, + TextInputGroupUtilities, ToolbarChip, ToolbarFilter, Tooltip, @@ -17,7 +20,7 @@ import { FilterSelectOptionProps, } from "./FilterToolbar"; import { css } from "@patternfly/react-styles"; -import spacing from "@patternfly/react-styles/css/utilities/Spacing/spacing"; +import { TimesIcon } from "@patternfly/react-icons"; import "./select-overrides.css"; @@ -161,29 +164,178 @@ export const MultiselectFilterControl = ({ // }); // }; - const toggle = (toggleRef: React.Ref) => { - return ( - { - setIsFilterDropdownOpen(!isFilterDropdownOpen); - }} - isExpanded={isFilterDropdownOpen} - isDisabled={isDisabled || category.selectOptions.length === 0} - > - {category.placeholderText} - {filterValue?.length ? ( - - {filterValue?.length} - - ) : null} - - ); + // const toggle = (toggleRef: React.Ref) => { + // return ( + // { + // setIsFilterDropdownOpen(!isFilterDropdownOpen); + // }} + // isExpanded={isFilterDropdownOpen} + // isDisabled={isDisabled || category.selectOptions.length === 0} + // variant="typeahead" + // > + // {category.placeholderText} + // {filterValue?.length ? ( + // + // {filterValue?.length} + // + // ) : null} + // + // ); + // }; + const [focusedItemIndex, setFocusedItemIndex] = React.useState( + null + ); + const [selected, setSelected] = React.useState(""); + const [activeItem, setActiveItem] = React.useState(null); + const textInputRef = React.useRef(); + const [inputValue, setInputValue] = React.useState(""); + // const [selectOptions, setSelectOptions] = + // React.useState(initialSelectOptions); + + const onSelect = ( + _event: React.MouseEvent | undefined, + value: string | number | undefined + ) => { + // eslint-disable-next-line no-console + console.log("selected", value); + + if (value && value !== "no results") { + setInputValue(value as string); + setFilterValue(null); + setSelected(value as string); + } + setIsFilterDropdownOpen(false); + setFocusedItemIndex(null); + setActiveItem(null); + }; + + const handleMenuArrowKeys = (key: string) => { + if (isFilterDropdownOpen && Array.isArray(selectOptions)) { + let indexToFocus: number = focusedItemIndex ?? -1; // default to -1 if null + + if (key === "ArrowUp") { + indexToFocus = + indexToFocus <= 0 ? selectOptions.length - 1 : indexToFocus - 1; + } else if (key === "ArrowDown") { + indexToFocus = + indexToFocus >= selectOptions.length - 1 ? 0 : indexToFocus + 1; + } + + // Skip disabled options + while (selectOptions[indexToFocus].isDisabled) { + indexToFocus = key === "ArrowUp" ? indexToFocus - 1 : indexToFocus + 1; + // Handle wrapping around the array + if (indexToFocus < 0) { + indexToFocus = selectOptions.length - 1; + } else if (indexToFocus >= selectOptions.length) { + indexToFocus = 0; + } + } + + // Update the focus index and active item + setFocusedItemIndex(indexToFocus); + const focusedItem = selectOptions[indexToFocus]; + setActiveItem(`select-typeahead-${focusedItem.value.replace(" ", "-")}`); + } }; + const onInputKeyDown = (event: React.KeyboardEvent) => { + const enabledMenuItems = Array.isArray(selectOptions) + ? selectOptions.filter((option) => !option.isDisabled) + : []; + const [firstMenuItem] = enabledMenuItems; + const focusedItem = focusedItemIndex + ? enabledMenuItems[focusedItemIndex] + : firstMenuItem; + + switch (event.key) { + // Select the first available option + case "Enter": + if (isFilterDropdownOpen && focusedItem.value !== "no results") { + setInputValue(String(focusedItem.children)); + setFilterValue(null); + setSelected(String(focusedItem.children)); + } + + setIsFilterDropdownOpen((prevIsOpen) => !prevIsOpen); + setFocusedItemIndex(null); + setActiveItem(null); + + break; + case "Tab": + case "Escape": + setIsFilterDropdownOpen(false); + setActiveItem(null); + break; + case "ArrowUp": + case "ArrowDown": + event.preventDefault(); + handleMenuArrowKeys(event.key); + break; + } + }; + + const onTextInputChange = ( + _event: React.FormEvent, + value: string + ) => { + setInputValue(value); + setFilterValue([value]); + }; + + const toggle = (toggleRef: React.Ref) => ( + { + setIsFilterDropdownOpen(!isFilterDropdownOpen); + }} + isExpanded={isFilterDropdownOpen} + isFullWidth + > + + { + setIsFilterDropdownOpen(!isFilterDropdownOpen); + }} + onChange={onTextInputChange} + onKeyDown={onInputKeyDown} + id="typeahead-select-input" + autoComplete="off" + innerRef={textInputRef} + placeholder="Select a state" + {...(activeItem && { "aria-activedescendant": activeItem })} + role="combobox" + isExpanded={isFilterDropdownOpen} + aria-controls="select-typeahead-listbox" + /> + + + {!!inputValue && ( + + )} + + + + ); + return (