-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
3370 - Select Component - Add toggleAll prop (#3737)
* 3370 - Select Component - Add toggleAll prop * 3370 - Fix deepscan * 3370 - Move MultiSelectOption component to its own file * 3370 - Add separator * 3370 - Update useToggleAllConfig hook * 3370 - Set optionComponent from hook --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
- Loading branch information
1 parent
51263d8
commit 497a1d7
Showing
12 changed files
with
194 additions
and
15 deletions.
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
src/client/components/Inputs/Select/Indicators/MultiSelectOption/MultiSelectOption.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
@import 'src/client/style/partials'; | ||
|
||
.select__toggleAllOption-checkbox { | ||
accent-color: $ui-accent; | ||
margin-right: $spacing-xxs; | ||
vertical-align: middle; | ||
|
||
&:checked { | ||
background-color: $ui-accent; | ||
border: none; | ||
} | ||
} | ||
|
||
.select__toggleAllOption-label { | ||
vertical-align: middle; | ||
} |
41 changes: 41 additions & 0 deletions
41
src/client/components/Inputs/Select/Indicators/MultiSelectOption/MultiSelectOption.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import './MultiSelectOption.scss' | ||
import React, { useEffect, useRef } from 'react' | ||
import { components, OptionProps } from 'react-select' | ||
|
||
import Hr from 'client/components/Hr' | ||
import { Option } from 'client/components/Inputs/Select' | ||
|
||
import { useMultiSelectOptionConfig } from './hooks/useMultiSelectOptionConfig' | ||
|
||
export const MultiSelectOption: React.FC<OptionProps<Option>> = (props) => { | ||
const { data, label } = props | ||
|
||
const { checked, isInputIndeterminate, isSelectAllOption } = useMultiSelectOptionConfig(props) | ||
|
||
const inputRef = useRef<HTMLInputElement>(null) | ||
useEffect(() => { | ||
if (inputRef.current) { | ||
inputRef.current.indeterminate = isInputIndeterminate | ||
} | ||
}, [isInputIndeterminate]) | ||
|
||
return ( | ||
<> | ||
{/* eslint-disable-next-line react/jsx-props-no-spreading */} | ||
<components.Option {...props}> | ||
<input | ||
key={`${data.value}-${isInputIndeterminate}`} | ||
ref={inputRef} | ||
checked={checked} | ||
className="select__toggleAllOption-checkbox" | ||
onChange={() => undefined} | ||
type="checkbox" | ||
/> | ||
<span className="select__toggleAllOption-label">{label}</span> | ||
</components.Option> | ||
{isSelectAllOption && <Hr />} | ||
</> | ||
) | ||
} | ||
|
||
export default MultiSelectOption |
38 changes: 38 additions & 0 deletions
38
...components/Inputs/Select/Indicators/MultiSelectOption/hooks/useMultiSelectOptionConfig.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { useMemo } from 'react' | ||
import { OptionProps } from 'react-select' | ||
|
||
import { Option, OptionsGroup, selectAllOptionValue } from 'client/components/Inputs/Select/types' | ||
|
||
type Props = OptionProps<Option> | ||
|
||
type Returned = { | ||
checked: boolean | ||
isInputIndeterminate: boolean | ||
isSelectAllOption: boolean | ||
} | ||
|
||
export const useMultiSelectOptionConfig = (props: Props): Returned => { | ||
const { data, isSelected, options, selectProps } = props | ||
|
||
const isSelectAllOption = data.value === selectAllOptionValue | ||
const allOptionsCount = useMemo<number>(() => { | ||
if (!isSelectAllOption) return 0 | ||
return options.reduce((acc, optionOrGroup) => { | ||
if (Object.hasOwn(optionOrGroup, 'options')) { | ||
const group = optionOrGroup as OptionsGroup | ||
return acc + group.options.length | ||
} | ||
return acc + 1 | ||
}, 0) | ||
}, [isSelectAllOption, options]) | ||
|
||
return useMemo<Returned>(() => { | ||
const selectedValuesLength = Array.isArray(selectProps.value) ? selectProps.value.length : 0 | ||
const allSelected = selectedValuesLength === allOptionsCount - 1 | ||
const checked = isSelected || (isSelectAllOption && allSelected) | ||
|
||
const isInputIndeterminate = isSelectAllOption && !allSelected && selectedValuesLength > 0 | ||
|
||
return { checked, isInputIndeterminate, isSelectAllOption } | ||
}, [allOptionsCount, isSelectAllOption, isSelected, selectProps.value]) | ||
} |
1 change: 1 addition & 0 deletions
1
src/client/components/Inputs/Select/Indicators/MultiSelectOption/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { MultiSelectOption } from './MultiSelectOption' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export { ClearIndicator, DropdownIndicator, IndicatorsContainer } from './Indicators' | ||
export { MultiSelectOption } from './MultiSelectOption' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,39 @@ | ||
import { useCallback } from 'react' | ||
|
||
import { Option, SelectProps } from 'client/components/Inputs/Select/types' | ||
import { Option, OptionsGroup, selectAllOptionValue, SelectProps } from 'client/components/Inputs/Select/types' | ||
|
||
type Returned = (option?: Option | Array<Option>) => void | ||
|
||
export const useOnChange = (props: SelectProps): Returned => { | ||
const { isMulti, onChange } = props | ||
const { isMulti, onChange, options: selectOptions, toggleAll } = props | ||
|
||
return useCallback<Returned>( | ||
(option?: Option | Array<Option>) => { | ||
if (isMulti && Array.isArray(option)) { | ||
onChange(option.map(({ value }) => value)) | ||
} else { | ||
onChange((option as Option)?.value ?? null) | ||
const selectedValues = option.map(({ value }) => value) | ||
if (!toggleAll) return onChange(selectedValues) | ||
|
||
const includesSelectAll = selectedValues.includes(selectAllOptionValue) | ||
// Update with only selected values (excluding "Select All") | ||
if (!includesSelectAll) return onChange(selectedValues) | ||
|
||
// If "(Un) Select All" is toggled while some items are selected, deselect all | ||
if (selectedValues.length > 1) return onChange([]) | ||
|
||
// If "Select All" is toggled with no selection, select all original options | ||
if (selectedValues.length === 1) { | ||
const allValues = selectOptions.flatMap((optionOrGroup) => { | ||
if (Object.hasOwn(optionOrGroup, 'options')) { | ||
return (optionOrGroup as OptionsGroup).options.map(({ value }) => value) | ||
} | ||
return (optionOrGroup as Option).value | ||
}) | ||
return onChange(allValues) | ||
} | ||
} | ||
// Handle Single-Select | ||
return onChange((option as Option)?.value ?? null) | ||
}, | ||
[isMulti, onChange] | ||
[isMulti, onChange, selectOptions, toggleAll] | ||
) | ||
} |
38 changes: 38 additions & 0 deletions
38
src/client/components/Inputs/Select/hooks/useToggleAllConfig.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { useMemo } from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
|
||
import { MultiSelectOption } from 'client/components/Inputs/Select/Indicators' | ||
import { OptionsOrGroups, selectAllOptionValue } from 'client/components/Inputs/Select/types' | ||
|
||
import { ValueSelect } from './useValue' | ||
|
||
type Props = { | ||
isMulti: boolean | ||
options: OptionsOrGroups | ||
toggleAll: boolean | ||
value: ValueSelect | ||
} | ||
|
||
type Returned = { | ||
optionComponent: React.FC | undefined | ||
options: OptionsOrGroups | ||
} | ||
|
||
export const useToggleAllConfig = (props: Props): Returned => { | ||
const { isMulti, options, toggleAll, value } = props | ||
const { t } = useTranslation() | ||
|
||
return useMemo<Returned>(() => { | ||
if (!isMulti) return { optionComponent: undefined, options } | ||
|
||
const optionComponent = MultiSelectOption | ||
|
||
if (!toggleAll) return { optionComponent, options } | ||
|
||
const selectAllOption = { | ||
value: selectAllOptionValue, | ||
label: Array.isArray(value) && value.length === 0 ? t('common.selectAll') : t('common.unselectAll'), | ||
} | ||
return { optionComponent, options: [selectAllOption, ...options] } | ||
}, [isMulti, options, t, toggleAll, value]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters