Skip to content

Commit

Permalink
feat: add support for multiple default options (#263)
Browse files Browse the repository at this point in the history
* feat: add support for multiple default options

* docs: add usage example

* docs: update prop list

* docs: update v2 changelog

* chore: add changeset
  • Loading branch information
markusunger authored Oct 18, 2024
1 parent 7fa489d commit 5eb49f4
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 37 deletions.
5 changes: 5 additions & 0 deletions .changeset/loud-comics-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@mobile-reality/react-native-select-pro': minor
---

Added support for multiple defaultOption objects
5 changes: 5 additions & 0 deletions apps/expo/src/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { CustomStyles } from '../examples/custom-styles';
import { ModalExample } from '../examples/modal-example';
import { MultiSelect } from '../examples/multiselect';
import { MultiSelectSearchableWithSeparatedOptions } from '../examples/multiselect-searchable-with-separated-options';
import { MultiSelectWithDefaultOptions } from '../examples/multiselect-with-default-options';
import { MultiSelectWithHiddenOptions } from '../examples/multiselect-with-hidden-options';
import { MultiSelectWithSearchable } from '../examples/multiselect-with-searchable';
import { MultiSelectWithSeparatedOptions } from '../examples/multiselect-with-separated-options';
Expand Down Expand Up @@ -115,6 +116,10 @@ export const ROUTES = [
name: 'MultiSelect with Hidden Options',
screen: MultiSelectWithHiddenOptions,
},
{
name: 'MultiSelect with Default Options',
screen: MultiSelectWithDefaultOptions,
},
{
name: 'TextInputProps',
screen: TextInputProps,
Expand Down
13 changes: 13 additions & 0 deletions apps/expo/src/examples/multiselect-with-default-options.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import { Select } from '@mobile-reality/react-native-select-pro';

import { SafeAreaViewWrapper } from '../components/safe-area-view-wrapper';
import { DATA } from '../constants/data';

export const MultiSelectWithDefaultOptions = () => {
return (
<SafeAreaViewWrapper>
<Select options={DATA} multiple={true} defaultOption={[DATA[0], DATA[1]]} />
</SafeAreaViewWrapper>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { OptionType } from '../../types';
import { getDefaultSelectionIndex } from '../index';

describe('getDefaultSelectionIndex', () => {
const options: OptionType[] = [
{ label: 'Option 1', value: 'value1' },
{ label: 'Option 2', value: 'value2' },
{ label: 'Option 3', value: 'value3' },
];

it('should return the correct index for a single default option', () => {
const defaultOption = { label: 'Option 2', value: 'value2' };
expect(getDefaultSelectionIndex(options, defaultOption)).toBe(1);
});

it('should return -1 if the single default option is not found', () => {
const defaultOption = { label: 'Option 4', value: 'value4' };
expect(getDefaultSelectionIndex(options, defaultOption)).toBe(-1);
});

it('should return an array of indices for multiple default options', () => {
const defaultOptions = [
{ label: 'Option 1', value: 'value1' },
{ label: 'Option 3', value: 'value3' },
];
expect(getDefaultSelectionIndex(options, defaultOptions)).toEqual([0, 2]);
});

it('should return only found indices for multiple default options', () => {
const defaultOptions = [
{ label: 'Option 1', value: 'value1' },
{ label: 'Option 4', value: 'value4' },
];
expect(getDefaultSelectionIndex(options, defaultOptions)).toEqual([0]);
});

it('should return an empty array if no default options are found', () => {
const defaultOptions = [
{ label: 'Option 4', value: 'value4' },
{ label: 'Option 5', value: 'value5' },
];
expect(getDefaultSelectionIndex(options, defaultOptions)).toEqual([]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { OptionType } from '../types';

export const getDefaultSelectionIndex = <T>(
flatOptions: OptionType<T>[],
defaultOption: OptionType<T> | OptionType<T>[],
): number | number[] => {
if (Array.isArray(defaultOption)) {
const foundIndices = defaultOption
.map((option) => flatOptions.findIndex((item) => item.value === option.value))
.filter((index) => index !== -1);

if (foundIndices.length > 0) {
return foundIndices;
}
} else {
const foundIndex = flatOptions.findIndex((item) => item.value === defaultOption.value);

if (foundIndex !== -1) {
return foundIndex;
}
}

return Array.isArray(defaultOption) ? [] : -1;
};
1 change: 1 addition & 0 deletions packages/react-native-select-pro/src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { dimensionPercentageToDP } from './dimension-percentage-to-dp';
export { getDefaultSelectionIndex } from './get-default-selection-index';
export { getReducedSectionData } from './get-reduced-section-data';
export { getSectionOptionsIndexes } from './get-section-options-indexes';
export { isAndroid } from './is-android';
Expand Down
34 changes: 20 additions & 14 deletions packages/react-native-select-pro/src/state/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { LayoutAnimation } from 'react-native';

import { ANIMATION_DURATION } from '../constants';
import { ERRORS, getReducedSectionData, isValidDefaultOption, searchNormalize } from '../helpers';
import { getDefaultSelectionIndex } from '../helpers/get-default-selection-index';
import type { OptionsType, OptionType } from '../types';
import { isSectionOptionsType } from '../types';

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

const setDefaultOption = <T>(options: OptionsType<T>, defaultOption: OptionType<T> | undefined) => {
if (isValidDefaultOption(defaultOption) && options.length > 0) {
const setDefaultOption = <T>(
options: OptionsType<T>,
defaultOption: OptionType<T> | OptionType<T>[] | undefined,
) => {
if (
(Array.isArray(defaultOption)
? defaultOption.every(isValidDefaultOption)
: isValidDefaultOption(defaultOption)) &&
options.length > 0 &&
defaultOption !== undefined
) {
const isSectionedOptions = isSectionOptionsType(options);
const flatOptions = isSectionedOptions ? getReducedSectionData(options) : options;

const foundIndex = isSectionedOptions
? getReducedSectionData(options).findIndex((item) => item.value === defaultOption.value)
: options.findIndex((item) => item.value === defaultOption.value);

if (foundIndex !== -1) {
return {
selectedOption: defaultOption,
selectedOptionIndex: foundIndex,
};
}
return {
selectedOption: defaultOption,
selectedOptionIndex: getDefaultSelectionIndex(flatOptions, defaultOption),
};
}

return {
selectedOption: null,
selectedOptionIndex: -1,
selectedOptionIndex: Array.isArray(defaultOption) ? [] : -1,
};
};

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

const { selectedOption, selectedOptionIndex } = setDefaultOption(options, defaultOption);
const defaultSearchValue = defaultOption?.label ?? '';
const defaultSearchOption = Array.isArray(defaultOption) ? defaultOption[0] : defaultOption;
const defaultSearchValue = defaultSearchOption?.label ?? '';

return {
isOpened: false,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-select-pro/src/state/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,5 @@ export type CreateInitialStateType<T> = {
searchable: boolean;
animation: boolean | number;

defaultOption: OptionType<T> | undefined;
defaultOption: OptionType<T> | OptionType<T>[] | undefined;
};
4 changes: 2 additions & 2 deletions packages/react-native-select-pro/src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ export interface SelectProps<T = unknown> {
closeOptionsListOnSelect?: boolean;

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

/**
* Disable a `Select` pressable.
Expand Down
Loading

0 comments on commit 5eb49f4

Please sign in to comment.