For some reason clicking outside the dropdown with out selecting item, dropdown is not closing
Doesn’t downshift by default closes when click outside the rendered dropdown?
I am using react-virtual to render the option. As i am looking over, i feel like isOpen prop that downshift provide is not getting applied properly, but i don’t know where to use it.
import React, { useState, useRef, ReactNode, useCallback, forwardRef } from 'react'
import { useCombobox } from 'downshift'
import { useVirtual } from 'react-virtual'
import cx from 'classnames'
import './single-select.css'
import { getSingleSelectFilteredItems, groupItemsByKey } from './helpers'
import { SearchSmall, SortArrownDown } from '../icons'
import { TextInput } from '../form'
export interface ComboBoxProps<T> {
filterKeys: string[]
groupBy: string
items: T[]
itemToString: (item: any | null) => string
optionListWindowHeight: number
placeholder: string
renderRow: (item: T, index: number, props: any) => ReactNode
setOpenDropdown: (value: boolean) => void
}
const SingleSelectDropdownWithAccordions = forwardRef(<T extends { id: string }>({
filterKeys,
groupBy,
items,
itemToString,
optionListWindowHeight,
placeholder,
renderRow,
setOpenDropdown,
}: ComboBoxProps<T>, ref) => {
const listRef = useRef(null)
const processedItems = groupBy ? groupItemsByKey(items, groupBy) : items
const [inputItems, setInputItems] = useState(processedItems)
const [expandedHeaders, setExpandedHeaders] = useState<Record<string, boolean>>({})
const rowVirtualizer = useVirtual({
size: inputItems.length,
parentRef: listRef,
estimateSize: useCallback(() => 35, []),
overscan: inputItems.length,
})
const {
isOpen = false,
getMenuProps,
getInputProps,
getItemProps,
highlightedIndex,
selectedItem,
} = useCombobox({
items: inputItems,
itemToString,
onInputValueChange: ({ inputValue }) => {
let newItems
if (inputValue) {
newItems = getSingleSelectFilteredItems(inputValue, items, groupBy, filterKeys)
} else {
newItems = groupBy ? groupItemsByKey(items, groupBy) : items
}
setInputItems(newItems)
},
scrollIntoView() { },
})
return (
<div className="cz-single-select">
<TextInput
icon={<SearchSmall />}
{...getInputProps()}
placeholder={placeholder}
/>
<ul
{...getMenuProps({ ref })}
className={`cz-single-select__window ${isOpen ? '' : 'hidden'}`}
style={{ maxHeight: optionListWindowHeight }}
>
{rowVirtualizer.virtualItems.map((virtualItem) => {
const itemProps = getItemProps({
item: inputItems[virtualItem.index],
index: virtualItem.index,
})
const item = inputItems[virtualItem.index]
if (item.header) {
const isExpanded = expandedHeaders[item.header]
const handleHeaderClick = () => {
setExpandedHeaders((prev) => ({
...prev,
[item.header]: !prev[item.header],
}))
}
return (
<>
<div
{...itemProps}
className="cz-single-select__window--accordion-header"
role="menuitem"
tabIndex={0}
key={virtualItem.index}
onClick={handleHeaderClick}
onKeyDown={handleHeaderClick}
>
{item.header}
<span className="cz-single-select__expander-indicator">
{isExpanded ? <SortArrownDown /> : <SortArrownDown />}
</span>
</div>
<p>hi</p>
</>
)
} else if (!expandedHeaders[item[groupBy]]) {
return renderRow(item, virtualItem.index, {
...itemProps,
className: cx(
'cz-single-select__option-row',
highlightedIndex === virtualItem.index && 'cz-single-select__highlighted',
selectedItem === inputItems[virtualItem.index] && 'cz-single-select__selected',
),
onClick: () => setOpenDropdown(false),
onKeyDown: () => setOpenDropdown(false),
})
}
return null
})}
</ul>
</div>
)
})
export { SingleSelectDropdownWithAccordions }
usages
const [openDropdown, setOpenDropdown] = useState(false)
{openDropdown && (
<SingleSelectDropdownWithAccordions
items={items}
itemToString={
(item) => (item ? item.name : '')}
renderRow={(item, index, props) => {
return (
<li
{...props}
key={index}
tabIndex={0}
role="menuitem"
onClick={() => handleItemSelected(item)}
onKeyDown={() => handleItemSelected(item)}
>
<span>{item.name}</span>
</li>
)
}}
groupBy="group"
filterKeys={['name']}
/>
)}
```