Skip to content

Commit 5eb49f4

Browse files
authored
feat: add support for multiple default options (#263)
* feat: add support for multiple default options * docs: add usage example * docs: update prop list * docs: update v2 changelog * chore: add changeset
1 parent 7fa489d commit 5eb49f4

File tree

12 files changed

+187
-37
lines changed

12 files changed

+187
-37
lines changed

.changeset/loud-comics-learn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@mobile-reality/react-native-select-pro': minor
3+
---
4+
5+
Added support for multiple defaultOption objects

apps/expo/src/constants/routes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { CustomStyles } from '../examples/custom-styles';
88
import { ModalExample } from '../examples/modal-example';
99
import { MultiSelect } from '../examples/multiselect';
1010
import { MultiSelectSearchableWithSeparatedOptions } from '../examples/multiselect-searchable-with-separated-options';
11+
import { MultiSelectWithDefaultOptions } from '../examples/multiselect-with-default-options';
1112
import { MultiSelectWithHiddenOptions } from '../examples/multiselect-with-hidden-options';
1213
import { MultiSelectWithSearchable } from '../examples/multiselect-with-searchable';
1314
import { MultiSelectWithSeparatedOptions } from '../examples/multiselect-with-separated-options';
@@ -115,6 +116,10 @@ export const ROUTES = [
115116
name: 'MultiSelect with Hidden Options',
116117
screen: MultiSelectWithHiddenOptions,
117118
},
119+
{
120+
name: 'MultiSelect with Default Options',
121+
screen: MultiSelectWithDefaultOptions,
122+
},
118123
{
119124
name: 'TextInputProps',
120125
screen: TextInputProps,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
import { Select } from '@mobile-reality/react-native-select-pro';
3+
4+
import { SafeAreaViewWrapper } from '../components/safe-area-view-wrapper';
5+
import { DATA } from '../constants/data';
6+
7+
export const MultiSelectWithDefaultOptions = () => {
8+
return (
9+
<SafeAreaViewWrapper>
10+
<Select options={DATA} multiple={true} defaultOption={[DATA[0], DATA[1]]} />
11+
</SafeAreaViewWrapper>
12+
);
13+
};
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { OptionType } from '../../types';
2+
import { getDefaultSelectionIndex } from '../index';
3+
4+
describe('getDefaultSelectionIndex', () => {
5+
const options: OptionType[] = [
6+
{ label: 'Option 1', value: 'value1' },
7+
{ label: 'Option 2', value: 'value2' },
8+
{ label: 'Option 3', value: 'value3' },
9+
];
10+
11+
it('should return the correct index for a single default option', () => {
12+
const defaultOption = { label: 'Option 2', value: 'value2' };
13+
expect(getDefaultSelectionIndex(options, defaultOption)).toBe(1);
14+
});
15+
16+
it('should return -1 if the single default option is not found', () => {
17+
const defaultOption = { label: 'Option 4', value: 'value4' };
18+
expect(getDefaultSelectionIndex(options, defaultOption)).toBe(-1);
19+
});
20+
21+
it('should return an array of indices for multiple default options', () => {
22+
const defaultOptions = [
23+
{ label: 'Option 1', value: 'value1' },
24+
{ label: 'Option 3', value: 'value3' },
25+
];
26+
expect(getDefaultSelectionIndex(options, defaultOptions)).toEqual([0, 2]);
27+
});
28+
29+
it('should return only found indices for multiple default options', () => {
30+
const defaultOptions = [
31+
{ label: 'Option 1', value: 'value1' },
32+
{ label: 'Option 4', value: 'value4' },
33+
];
34+
expect(getDefaultSelectionIndex(options, defaultOptions)).toEqual([0]);
35+
});
36+
37+
it('should return an empty array if no default options are found', () => {
38+
const defaultOptions = [
39+
{ label: 'Option 4', value: 'value4' },
40+
{ label: 'Option 5', value: 'value5' },
41+
];
42+
expect(getDefaultSelectionIndex(options, defaultOptions)).toEqual([]);
43+
});
44+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { OptionType } from '../types';
2+
3+
export const getDefaultSelectionIndex = <T>(
4+
flatOptions: OptionType<T>[],
5+
defaultOption: OptionType<T> | OptionType<T>[],
6+
): number | number[] => {
7+
if (Array.isArray(defaultOption)) {
8+
const foundIndices = defaultOption
9+
.map((option) => flatOptions.findIndex((item) => item.value === option.value))
10+
.filter((index) => index !== -1);
11+
12+
if (foundIndices.length > 0) {
13+
return foundIndices;
14+
}
15+
} else {
16+
const foundIndex = flatOptions.findIndex((item) => item.value === defaultOption.value);
17+
18+
if (foundIndex !== -1) {
19+
return foundIndex;
20+
}
21+
}
22+
23+
return Array.isArray(defaultOption) ? [] : -1;
24+
};

packages/react-native-select-pro/src/helpers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { dimensionPercentageToDP } from './dimension-percentage-to-dp';
2+
export { getDefaultSelectionIndex } from './get-default-selection-index';
23
export { getReducedSectionData } from './get-reduced-section-data';
34
export { getSectionOptionsIndexes } from './get-section-options-indexes';
45
export { isAndroid } from './is-android';

packages/react-native-select-pro/src/state/reducer.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { LayoutAnimation } from 'react-native';
22

33
import { ANIMATION_DURATION } from '../constants';
44
import { ERRORS, getReducedSectionData, isValidDefaultOption, searchNormalize } from '../helpers';
5+
import { getDefaultSelectionIndex } from '../helpers/get-default-selection-index';
56
import type { OptionsType, OptionType } from '../types';
67
import { isSectionOptionsType } from '../types';
78

@@ -88,25 +89,29 @@ export const reducer = <T>(state: State<T>, action: ActionType<T>): State<T> =>
8889
}
8990
};
9091

91-
const setDefaultOption = <T>(options: OptionsType<T>, defaultOption: OptionType<T> | undefined) => {
92-
if (isValidDefaultOption(defaultOption) && options.length > 0) {
92+
const setDefaultOption = <T>(
93+
options: OptionsType<T>,
94+
defaultOption: OptionType<T> | OptionType<T>[] | undefined,
95+
) => {
96+
if (
97+
(Array.isArray(defaultOption)
98+
? defaultOption.every(isValidDefaultOption)
99+
: isValidDefaultOption(defaultOption)) &&
100+
options.length > 0 &&
101+
defaultOption !== undefined
102+
) {
93103
const isSectionedOptions = isSectionOptionsType(options);
104+
const flatOptions = isSectionedOptions ? getReducedSectionData(options) : options;
94105

95-
const foundIndex = isSectionedOptions
96-
? getReducedSectionData(options).findIndex((item) => item.value === defaultOption.value)
97-
: options.findIndex((item) => item.value === defaultOption.value);
98-
99-
if (foundIndex !== -1) {
100-
return {
101-
selectedOption: defaultOption,
102-
selectedOptionIndex: foundIndex,
103-
};
104-
}
106+
return {
107+
selectedOption: defaultOption,
108+
selectedOptionIndex: getDefaultSelectionIndex(flatOptions, defaultOption),
109+
};
105110
}
106111

107112
return {
108113
selectedOption: null,
109-
selectedOptionIndex: -1,
114+
selectedOptionIndex: Array.isArray(defaultOption) ? [] : -1,
110115
};
111116
};
112117

@@ -121,7 +126,8 @@ export const createInitialState = <T>({
121126
}
122127

123128
const { selectedOption, selectedOptionIndex } = setDefaultOption(options, defaultOption);
124-
const defaultSearchValue = defaultOption?.label ?? '';
129+
const defaultSearchOption = Array.isArray(defaultOption) ? defaultOption[0] : defaultOption;
130+
const defaultSearchValue = defaultSearchOption?.label ?? '';
125131

126132
return {
127133
isOpened: false,

packages/react-native-select-pro/src/state/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,5 @@ export type CreateInitialStateType<T> = {
7373
searchable: boolean;
7474
animation: boolean | number;
7575

76-
defaultOption: OptionType<T> | undefined;
76+
defaultOption: OptionType<T> | OptionType<T>[] | undefined;
7777
};

packages/react-native-select-pro/src/types/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,9 @@ export interface SelectProps<T = unknown> {
7070
closeOptionsListOnSelect?: boolean;
7171

7272
/**
73-
* An object that represents the default option for a `Select`.
73+
* An object or array of objects that represents the default option(s) for a `Select`.
7474
*/
75-
defaultOption?: OptionType<T>;
75+
defaultOption?: OptionType<T> | OptionType<T>[];
7676

7777
/**
7878
* Disable a `Select` pressable.

0 commit comments

Comments
 (0)