Skip to content

Commit 0d0d9cf

Browse files
committed
feat: add openPopover function to usePopover hook and integrate with GroupedFilterSelectPicker
1 parent 00a8b68 commit 0d0d9cf

5 files changed

Lines changed: 116 additions & 24 deletions

File tree

src/Shared/Components/Popover/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ export interface UsePopoverReturnType {
115115
* A function to close the popover.
116116
*/
117117
closePopover: () => void
118+
/**
119+
* A function to open the popover.
120+
*/
121+
openPopover: () => void
118122
}
119123

120124
export type PopoverProps = Pick<UsePopoverReturnType, 'open' | 'overlayProps' | 'popoverProps' | 'triggerProps'> & {

src/Shared/Components/Popover/usePopover.hook.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ export const usePopover = ({
6565
updateOpenState(!open)
6666
}
6767

68+
const openPopover = () => {
69+
updateOpenState(true)
70+
}
71+
6872
const closePopover = () => {
6973
if (disableClose) {
7074
return
@@ -141,7 +145,7 @@ export const usePopover = ({
141145

142146
// eslint-disable-next-line consistent-return
143147
return () => {
144-
scrollableRef.current.removeEventListener('wheel', handleWheel)
148+
scrollableRef.current?.removeEventListener('wheel', handleWheel)
145149
window.removeEventListener('resize', updatePopoverPosition)
146150
}
147151
}, [open, position, alignment, isBackdropMounted])
@@ -181,5 +185,6 @@ export const usePopover = ({
181185
},
182186
scrollableRef,
183187
closePopover,
188+
openPopover,
184189
}
185190
}

src/Shared/Components/SelectPicker/GroupedFilterSelectPicker.tsx

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { KeyboardEvent, useEffect, useRef, useState } from 'react'
17+
import { KeyboardEvent, useEffect, useMemo, useRef, useState } from 'react'
1818

1919
import { useRegisterShortcut } from '@Common/Hooks'
2020

2121
import { ActionMenu, ActionMenuItemType, ActionMenuProps } from '../ActionMenu'
2222
import { Icon } from '../Icon'
23+
import { Popover, usePopover } from '../Popover'
2324
import FilterSelectPicker from './FilterSelectPicker'
24-
import { GroupedFilterSelectPickerProps } from './type'
25+
import { FilterSelectPickerMapSelectPickerVariant, GroupedFilterSelectPickerProps } from './type'
2526

2627
import './selectPicker.scss'
2728

@@ -41,6 +42,29 @@ export const GroupedFilterSelectPicker = <T extends string | number>({
4142
// HOOKS
4243
const { registerShortcut, unregisterShortcut } = useRegisterShortcut()
4344

45+
const selectedItemConfig = selectedActionMenuItem ? filterSelectPickerPropsMap[selectedActionMenuItem] : null
46+
const isPopOverVariant = selectedItemConfig?.variant === 'popOver'
47+
48+
const {
49+
open: isFilterPopoverOpen,
50+
triggerProps: filterPopoverTriggerProps,
51+
overlayProps: filterPopoverOverlayProps,
52+
popoverProps: filterPopoverContentProps,
53+
openPopover: openFilterPopover,
54+
closePopover: closeFilterPopover,
55+
scrollableRef: filterPopoverScrollableRef,
56+
} = usePopover({
57+
id: `${id}-grouped-filter-popover`,
58+
position: isPopOverVariant ? selectedItemConfig.popoverConfig?.position : undefined,
59+
alignment: isPopOverVariant ? selectedItemConfig.popoverConfig?.alignment : undefined,
60+
width: isPopOverVariant ? selectedItemConfig.popoverConfig?.width : undefined,
61+
onOpen: (open) => {
62+
if (!open) {
63+
setSelectedActionMenuItem(null)
64+
}
65+
},
66+
})
67+
4468
useEffect(() => {
4569
const shortcutCallback = () => {
4670
triggerButtonRef.current?.click()
@@ -60,6 +84,12 @@ export const GroupedFilterSelectPicker = <T extends string | number>({
6084
}
6185
}, [selectedActionMenuItem])
6286

87+
useEffect(() => {
88+
if (selectedActionMenuItem && isPopOverVariant) {
89+
openFilterPopover()
90+
}
91+
}, [selectedActionMenuItem])
92+
6393
// HANDLERS
6494
const handleMenuItemClick: ActionMenuProps<T>['onClick'] = (item) => {
6595
setSelectedActionMenuItem(item.id)
@@ -76,18 +106,8 @@ export const GroupedFilterSelectPicker = <T extends string | number>({
76106
}
77107
}
78108

79-
return selectedActionMenuItem ? (
80-
<div className="grouped-filter-select-picker w-200">
81-
<FilterSelectPicker
82-
{...filterSelectPickerPropsMap[selectedActionMenuItem]}
83-
menuIsOpen
84-
onMenuClose={handleMenuClose}
85-
onKeyDown={handleKeyDown}
86-
autoFocus
87-
/>
88-
</div>
89-
) : (
90-
<ActionMenu {...restProps} id={id} isSearchable onClick={handleMenuItemClick}>
109+
const filterTriggerButton = useMemo(
110+
() => (
91111
<button
92112
type="button"
93113
ref={triggerButtonRef}
@@ -98,6 +118,51 @@ export const GroupedFilterSelectPicker = <T extends string | number>({
98118
<span className="fs-12 lh-20 fw-6 cn-9">Filter</span>
99119
<kbd className="icon-dim-20 flex bg__primary border__primary br-2 shadow__key fs-12 lh-20 cn-7">f</kbd>
100120
</button>
101-
</ActionMenu>
121+
),
122+
[isFilterApplied],
102123
)
124+
125+
const renderContent = () => {
126+
if (selectedActionMenuItem && isPopOverVariant) {
127+
return (
128+
<Popover
129+
open={isFilterPopoverOpen}
130+
triggerProps={filterPopoverTriggerProps}
131+
overlayProps={filterPopoverOverlayProps}
132+
popoverProps={{ ...filterPopoverContentProps, role: 'dialog' }}
133+
buttonProps={null}
134+
triggerElement={filterTriggerButton}
135+
>
136+
{selectedItemConfig.component(closeFilterPopover, filterPopoverScrollableRef)}
137+
</Popover>
138+
)
139+
}
140+
141+
if (selectedActionMenuItem) {
142+
const config = filterSelectPickerPropsMap[selectedActionMenuItem]
143+
if (config.variant !== 'popOver') {
144+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
145+
const { variant: _variant, ...filterProps } = config as FilterSelectPickerMapSelectPickerVariant
146+
return (
147+
<div className="grouped-filter-select-picker w-200">
148+
<FilterSelectPicker
149+
{...filterProps}
150+
menuIsOpen
151+
onMenuClose={handleMenuClose}
152+
onKeyDown={handleKeyDown}
153+
autoFocus
154+
/>
155+
</div>
156+
)
157+
}
158+
}
159+
160+
return (
161+
<ActionMenu {...restProps} id={id} isSearchable onClick={handleMenuItemClick}>
162+
{filterTriggerButton}
163+
</ActionMenu>
164+
)
165+
}
166+
167+
return <div>{renderContent()}</div>
103168
}

src/Shared/Components/SelectPicker/type.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { MutableRefObject, ReactElement, ReactNode } from 'react'
17+
import { LegacyRef, MutableRefObject, ReactElement, ReactNode } from 'react'
1818
import { GroupBase, GroupHeadingProps, Props as ReactSelectProps, SelectInstance } from 'react-select'
1919
// This import allows to extend the base interface in react-select module via module augmentation
2020
import type {} from 'react-select/base'
@@ -29,6 +29,7 @@ import { ComponentSizeType } from '@Shared/constants'
2929
import { ActionMenuProps } from '../ActionMenu'
3030
import { ButtonComponentType, ButtonProps, ButtonVariantType } from '../Button'
3131
import { FormFieldWrapperProps } from '../FormFieldWrapper/types'
32+
import { UsePopoverProps } from '../Popover'
3233

3334
export interface SelectPickerOptionType<OptionValue = string | number, OptionLabel = ReactNode>
3435
extends OptionType<OptionValue, OptionLabel> {
@@ -373,14 +374,32 @@ export type SelectPickerTextAreaProps = Omit<
373374
> &
374375
Pick<ResizableTagTextAreaProps, 'maxHeight' | 'minHeight' | 'refVar' | 'dependentRefs'>
375376

377+
export type FilterSelectPickerMapSelectPickerVariant = {
378+
variant?: 'selectPicker'
379+
} & Omit<FilterSelectPickerProps, 'autoFocus' | 'menuIsOpen' | 'onMenuClose' | 'onKeyDown' | 'selectRef'>
380+
381+
type FilterSelectPickerMapPopOverVariant = {
382+
variant: 'popOver'
383+
/**
384+
* Component rendered inside the Popover.
385+
* Receives `closePopover` as a prop to allow programmatic closing.
386+
*/
387+
component: (closePopover: () => void, scrollableRef: MutableRefObject<any> | LegacyRef<any>) => ReactElement
388+
/**
389+
* Optional positioning config forwarded to usePopover
390+
*/
391+
popoverConfig?: Pick<UsePopoverProps, 'position' | 'alignment' | 'width'>
392+
}
393+
394+
export type GroupedFilterSelectPickerMapValue =
395+
| FilterSelectPickerMapSelectPickerVariant
396+
| FilterSelectPickerMapPopOverVariant
397+
376398
export interface GroupedFilterSelectPickerProps<T extends string | number = string | number>
377399
extends Omit<
378400
ActionMenuProps<T>,
379401
'onClick' | 'disableDescriptionEllipsis' | 'children' | 'buttonProps' | 'isSearchable'
380402
> {
381403
isFilterApplied?: boolean
382-
filterSelectPickerPropsMap: Record<
383-
T,
384-
Omit<FilterSelectPickerProps, 'autoFocus' | 'menuIsOpen' | 'onMenuClose' | 'onKeyDown' | 'selectRef'>
385-
>
404+
filterSelectPickerPropsMap: Record<T, GroupedFilterSelectPickerMapValue>
386405
}

src/Shared/Components/Table/InternalTable.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,8 @@ const InternalTable = <
201201
<GenericFilterEmptyState
202202
{...emptyStateConfig.noRowsForFilterConfig}
203203
handleClearFilters={
204-
emptyStateConfig.noRowsForFilterConfig.handleClearFilters !== undefined
205-
? emptyStateConfig.noRowsForFilterConfig.handleClearFilters
206-
: (userGivenUrlClearFilters ?? clearFilters)
204+
emptyStateConfig.noRowsForFilterConfig?.handleClearFilters ||
205+
(userGivenUrlClearFilters ?? clearFilters)
207206
}
208207
/>
209208
) : (

0 commit comments

Comments
 (0)