Skip to content

Commit f853d79

Browse files
committed
refactor: merge multi select picker into select picker
1 parent c43d662 commit f853d79

File tree

2 files changed

+97
-30
lines changed

2 files changed

+97
-30
lines changed

src/Shared/Components/SelectPicker/SelectPicker.component.tsx

Lines changed: 96 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import ReactSelect, { ControlProps, MenuListProps, OptionProps, ValueContainerProps } from 'react-select'
1+
import ReactSelect, {
2+
ControlProps,
3+
GroupHeadingProps,
4+
MenuListProps,
5+
MultiValueProps,
6+
OptionProps,
7+
ValueContainerProps,
8+
} from 'react-select'
9+
import CreatableSelect from 'react-select/creatable'
210
import { ReactElement, useCallback, useMemo } from 'react'
311
import { ReactComponent as ErrorIcon } from '@Icons/ic-warning.svg'
412
import { ReactComponent as ICInfoFilledOverride } from '@Icons/ic-info-filled-override.svg'
@@ -7,9 +15,12 @@ import { ConditionalWrap } from '@Common/Helper'
715
import Tippy from '@tippyjs/react'
816
import { getCommonSelectStyle } from './utils'
917
import {
18+
MultiValueLabel,
19+
MultiValueRemove,
1020
SelectPickerClearIndicator,
1121
SelectPickerControl,
1222
SelectPickerDropdownIndicator,
23+
SelectPickerGroupHeading,
1324
SelectPickerLoadingIndicator,
1425
SelectPickerMenuList,
1526
SelectPickerOption,
@@ -112,9 +123,16 @@ const SelectPicker = ({
112123
menuSize,
113124
variant = SelectPickerVariantType.DEFAULT,
114125
disableDescriptionEllipsis = false,
126+
getIsOptionValid = () => true,
127+
isCreatable = false,
128+
isGroupHeadingSelectable = false,
129+
isMulti,
115130
...props
116131
}: SelectPickerProps) => {
117-
const { inputId, required, isDisabled } = props
132+
const { inputId, required, isDisabled, controlShouldRenderValue } = props
133+
// Only large variant is supported for multi select picker
134+
const selectSize = isMulti && !controlShouldRenderValue ? ComponentSizeType.large : size
135+
const shouldShowSelectedOptionIcon = !isMulti && showSelectedOptionIcon
118136

119137
const labelId = `${inputId}-label`
120138
const errorElementId = `${inputId}-error-msg`
@@ -123,18 +141,22 @@ const SelectPicker = ({
123141
() =>
124142
getCommonSelectStyle({
125143
error,
126-
size,
144+
size: selectSize,
127145
menuSize,
128146
variant,
147+
getIsOptionValid,
148+
isGroupHeadingSelectable,
129149
}),
130-
[error, size, menuSize, variant],
150+
[error, selectSize, menuSize, variant, isGroupHeadingSelectable],
131151
)
132152

153+
const isValidNewOption = (inputValue: string) => isCreatable && !!inputValue?.trim()
154+
133155
const renderControl = useCallback(
134156
(controlProps: ControlProps<SelectPickerOptionType>) => (
135-
<SelectPickerControl {...controlProps} icon={icon} showSelectedOptionIcon={showSelectedOptionIcon} />
157+
<SelectPickerControl {...controlProps} icon={icon} showSelectedOptionIcon={shouldShowSelectedOptionIcon} />
136158
),
137-
[icon, showSelectedOptionIcon],
159+
[icon, shouldShowSelectedOptionIcon],
138160
)
139161

140162
const renderMenuList = useCallback(
@@ -146,7 +168,7 @@ const SelectPicker = ({
146168

147169
const renderValueContainer = useCallback(
148170
(valueContainerProps: ValueContainerProps<SelectPickerOptionType>) => (
149-
<SelectPickerValueContainer {...valueContainerProps} showSelectedOptionsCount={false} />
171+
<SelectPickerValueContainer {...valueContainerProps} showSelectedOptionsCount={showSelectedOptionsCount} />
150172
),
151173
[showSelectedOptionsCount],
152174
)
@@ -158,6 +180,17 @@ const SelectPicker = ({
158180
[disableDescriptionEllipsis],
159181
)
160182

183+
const renderMultiValueLabel = (multiValueLabelProps: MultiValueProps<SelectPickerOptionType, true>) => (
184+
<MultiValueLabel {...multiValueLabelProps} getIsOptionValid={getIsOptionValid} />
185+
)
186+
187+
const renderGroupHeading = useCallback(
188+
(groupHeadingProps: GroupHeadingProps<SelectPickerOptionType>) => (
189+
<SelectPickerGroupHeading {...groupHeadingProps} isGroupHeadingSelectable={isGroupHeadingSelectable} />
190+
),
191+
[isGroupHeadingSelectable],
192+
)
193+
161194
const renderDisabledTippy = (children: ReactElement) => (
162195
<Tippy content={disabledTippyContent} placement="top" className="default-tt" arrow={false}>
163196
{children}
@@ -187,29 +220,62 @@ const SelectPicker = ({
187220
)}
188221
<ConditionalWrap condition={isDisabled && !!disabledTippyContent} wrap={renderDisabledTippy}>
189222
<div className="w-100">
190-
<ReactSelect<SelectPickerOptionType, boolean>
191-
{...props}
192-
placeholder={placeholder}
193-
components={{
194-
IndicatorSeparator: null,
195-
LoadingIndicator: SelectPickerLoadingIndicator,
196-
DropdownIndicator: SelectPickerDropdownIndicator,
197-
Control: renderControl,
198-
Option: renderOption,
199-
MenuList: renderMenuList,
200-
ClearIndicator: SelectPickerClearIndicator,
201-
ValueContainer: renderValueContainer,
202-
}}
203-
styles={selectStyles}
204-
menuPlacement="auto"
205-
menuPosition="fixed"
206-
menuShouldScrollIntoView
207-
backspaceRemovesValue={false}
208-
aria-errormessage={errorElementId}
209-
aria-invalid={!!error}
210-
aria-labelledby={labelId}
211-
hideSelectedOptions={false}
212-
/>
223+
{isMulti ? (
224+
<CreatableSelect<SelectPickerOptionType, true>
225+
{...props}
226+
isMulti
227+
placeholder={placeholder}
228+
components={{
229+
IndicatorSeparator: null,
230+
LoadingIndicator: SelectPickerLoadingIndicator,
231+
DropdownIndicator: SelectPickerDropdownIndicator,
232+
Control: renderControl,
233+
Option: renderOption,
234+
MenuList: renderMenuList,
235+
ClearIndicator: SelectPickerClearIndicator,
236+
ValueContainer: renderValueContainer,
237+
MultiValueLabel: renderMultiValueLabel,
238+
MultiValueRemove,
239+
GroupHeading: renderGroupHeading,
240+
}}
241+
styles={selectStyles}
242+
menuPlacement="auto"
243+
menuPosition="fixed"
244+
menuShouldScrollIntoView
245+
backspaceRemovesValue
246+
aria-errormessage={errorElementId}
247+
aria-invalid={!!error}
248+
aria-labelledby={labelId}
249+
closeMenuOnSelect={false}
250+
allowCreateWhileLoading={false}
251+
hideSelectedOptions={false}
252+
isValidNewOption={isValidNewOption}
253+
/>
254+
) : (
255+
<ReactSelect<SelectPickerOptionType, false>
256+
{...props}
257+
placeholder={placeholder}
258+
components={{
259+
IndicatorSeparator: null,
260+
LoadingIndicator: SelectPickerLoadingIndicator,
261+
DropdownIndicator: SelectPickerDropdownIndicator,
262+
Control: renderControl,
263+
Option: renderOption,
264+
MenuList: renderMenuList,
265+
ClearIndicator: SelectPickerClearIndicator,
266+
ValueContainer: renderValueContainer,
267+
}}
268+
styles={selectStyles}
269+
menuPlacement="auto"
270+
menuPosition="fixed"
271+
menuShouldScrollIntoView
272+
backspaceRemovesValue={false}
273+
aria-errormessage={errorElementId}
274+
aria-invalid={!!error}
275+
aria-labelledby={labelId}
276+
hideSelectedOptions={false}
277+
/>
278+
)}
213279
</div>
214280
</ConditionalWrap>
215281
</div>

src/Shared/Components/SelectPicker/type.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,5 @@ export interface SelectPickerProps
128128
* @default false
129129
*/
130130
isGroupHeadingSelectable?: boolean
131+
// TODO: Improve typing and add support for custom option list; isMenuOpen prop expose?
131132
}

0 commit comments

Comments
 (0)