Skip to content

Commit 89a2479

Browse files
committed
feat: add multi select filter button
1 parent f01f775 commit 89a2479

File tree

5 files changed

+145
-0
lines changed

5 files changed

+145
-0
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright (c) 2024. Devtron Inc.
3+
*/
4+
5+
import React, { cloneElement, useState } from 'react'
6+
import ReactSelect, { MenuListProps, components } from 'react-select'
7+
import { Option } from '@Common/MultiSelectCustomization'
8+
import { OptionType } from '@Common/Types'
9+
import { ReactComponent as ICCaretDown } from '../../../Assets/Icon/ic-caret-down.svg'
10+
import { getFilterStyle } from './utils'
11+
import { FilterButtonPropsType } from './types'
12+
13+
const ValueContainer = (props: any) => {
14+
const { selectProps, getValue, children } = props
15+
const selectedProjectLen = getValue().length
16+
17+
return (
18+
<components.ValueContainer {...props}>
19+
{!selectProps.inputValue &&
20+
(!selectProps.menuIsOpen ? (
21+
<div className="flexbox dc__gap-8 dc__align-items-center">
22+
<div className="fs-13 fw-4 cn-9">{selectProps.placeholder}</div>
23+
{selectedProjectLen > 0 && (
24+
<div className="bcb-5 dc__border-radius-4-imp pl-5 pr-5 cn-0 fs-12 fw-6 lh-18">
25+
{selectedProjectLen}
26+
</div>
27+
)}
28+
</div>
29+
) : (
30+
<span className="dc__position-abs cn-5 ml-2">{selectProps.placeholder}</span>
31+
))}
32+
33+
{cloneElement(children[1])}
34+
</components.ValueContainer>
35+
)
36+
}
37+
38+
const FilterSelectMenuList: React.FC<MenuListProps> = (props) => {
39+
const {
40+
children,
41+
// @ts-ignore NOTE: handleApplyFilter is passed from FilterButton
42+
selectProps: { handleApplyFilter },
43+
} = props
44+
45+
return (
46+
<components.MenuList {...props}>
47+
{children}
48+
<div className="p-8 dc__position-sticky dc__bottom-0 dc__border-top-n1 bcn-0">
49+
<button
50+
type="button"
51+
className="dc__unset-button-styles w-100 br-4 h-28 flex bcb-5 cn-0 fw-6 lh-28 fs-12 h-28 br-4 pt-5 pr-12 pb-5 pl-12"
52+
onClick={handleApplyFilter}
53+
aria-label="Apply filters"
54+
>
55+
Apply
56+
</button>
57+
</div>
58+
</components.MenuList>
59+
)
60+
}
61+
62+
const DropdownIndicator = (props) => (
63+
<components.DropdownIndicator {...props}>
64+
<ICCaretDown className="icon-dim-20 icon-n5" />
65+
</components.DropdownIndicator>
66+
)
67+
68+
const FilterButton: React.FC<FilterButtonPropsType> = ({
69+
placeholder,
70+
appliedFilters,
71+
options,
72+
disabled,
73+
handleApplyChange,
74+
getFormattedFilterLabelValue,
75+
menuAlignFromRight,
76+
}) => {
77+
const [selectedOptions, setSelectedOptions] = useState<OptionType[]>(
78+
appliedFilters.map((filter) => ({ value: filter, label: getFormattedFilterLabelValue?.(filter) || filter })),
79+
)
80+
81+
const handleSelectOnChange: React.ComponentProps<typeof ReactSelect>['onChange'] = (selected: OptionType[]) => {
82+
setSelectedOptions([...selected])
83+
}
84+
85+
const handleApply = () => {
86+
handleApplyChange(Object.values(selectedOptions).map((option) => option.value))
87+
}
88+
89+
return (
90+
<ReactSelect
91+
placeholder={placeholder}
92+
isMulti
93+
isDisabled={disabled}
94+
options={options}
95+
value={selectedOptions}
96+
onChange={handleSelectOnChange}
97+
closeMenuOnSelect={false}
98+
controlShouldRenderValue={false}
99+
hideSelectedOptions={false}
100+
maxMenuHeight={300}
101+
components={{
102+
MenuList: FilterSelectMenuList,
103+
IndicatorSeparator: null,
104+
ClearIndicator: null,
105+
DropdownIndicator,
106+
Option,
107+
ValueContainer,
108+
}}
109+
styles={getFilterStyle(menuAlignFromRight)}
110+
// @ts-ignore NOTE: passing this to use in FilterSelectMenuList
111+
handleApplyFilter={handleApply}
112+
/>
113+
)
114+
}
115+
116+
export default FilterButton
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as FilterButton } from './FilterButton'
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { OptionType } from '@Common/Types'
2+
3+
export interface FilterButtonPropsType {
4+
placeholder: string
5+
appliedFilters: string[]
6+
options: OptionType[]
7+
handleApplyChange: (selectedOptions: string[]) => void
8+
disabled?: boolean
9+
getFormattedFilterLabelValue?: (identifier: string) => string
10+
menuAlignFromRight?: boolean
11+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { CommonGroupedDropdownStyles } from '../ReactSelect'
2+
3+
export const getFilterStyle = (menuAlignFromRight: boolean) => ({
4+
...CommonGroupedDropdownStyles,
5+
menu: (base) => ({
6+
...base,
7+
width: '200px',
8+
...(menuAlignFromRight && { right: 0 }),
9+
}),
10+
control: (base, state) => ({
11+
...CommonGroupedDropdownStyles.control(base, state),
12+
width: 'max-content',
13+
maxWidth: '200px',
14+
minWidth: '150px',
15+
}),
16+
})

src/Shared/Components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,4 @@ export * from './Plugin'
5050
export * from './KeyValueTable'
5151
export * from './SelectPicker'
5252
export * from './DetectBottom'
53+
export * from './FilterButton'

0 commit comments

Comments
 (0)