diff --git a/docs/getting-started/v8.md b/docs/getting-started/v8.md
index 2ffbc1fda0..586c2e3ad1 100644
--- a/docs/getting-started/v8.md
+++ b/docs/getting-started/v8.md
@@ -76,3 +76,22 @@ Removed
Use the `backgroundColor` prop instead of `contentContainerStyle={{backgroundColor}}`
Fix card being transparent on Android
`onCollapseChanged` will now be called after the animation has ended (as was intended)
+
+### Picker
+The component was refactored to simplify its API and improve type safety.
+
+#### Migration Steps
+
+**Picker:**
+
+- `value` - The picker now only supports primitive values (string | number) instead of object-based values
+- `migrate` - Removed
+
+**Picker.Item:**
+
+- Items structure remains the same: `{label: string, value: primitive}` format is unchanged
+- All items now use the `label` prop directly - no custom label transformation needed
+- `getItemLabel` - Removed (use `item.label` to get label)
+- `getItemValue` - Removed (use `item.value` to get value)
+
+Check out the full API: https://wix.github.io/react-native-ui-lib/docs/components/form/Picker
diff --git a/src/components/picker/PickerItem.tsx b/src/components/picker/PickerItem.tsx
index 2b4405258d..7afef0b2ba 100644
--- a/src/components/picker/PickerItem.tsx
+++ b/src/components/picker/PickerItem.tsx
@@ -9,9 +9,9 @@ import View from '../view';
import TouchableOpacity from '../touchableOpacity';
import Image from '../image';
import Text from '../text';
-import {getItemLabel, isItemSelected} from './PickerPresenter';
+import {isItemSelected} from './PickerPresenter';
import PickerContext from './PickerContext';
-import {PickerItemProps, PickerSingleValue} from './types';
+import {PickerItemProps} from './types';
/**
* @description: Picker.Item, for configuring the Picker's selectable options
@@ -29,12 +29,8 @@ const PickerItem = (props: PickerItemProps) => {
testID
} = props;
const context = useContext(PickerContext);
- const {migrate} = context;
const customRenderItem = props.renderItem || context.renderItem;
- // @ts-expect-error TODO: fix after removing migrate prop completely
- const itemValue = !migrate && typeof value === 'object' ? value?.value : value;
- const isSelected = isItemSelected(itemValue, context.value);
- const itemLabel = getItemLabel(label, value, props.getItemLabel || context.getItemLabel);
+ const isSelected = isItemSelected(value, context.value);
const selectedCounter = context.selectionLimit && _.isArray(context.value) && context.value?.length;
const accessibilityProps = {
accessibilityState: isSelected ? {selected: true} : undefined,
@@ -65,16 +61,12 @@ const PickerItem = (props: PickerItemProps) => {
const _onPress = useCallback(async (props: any) => {
// Using !(await onPress?.(item)) does not work properly when onPress is not sent
// We have to explicitly state `false` so a synchronous void (undefined) will still work as expected
- if (onPress && await onPress(context.isMultiMode ? !isSelected : undefined, props) === false) {
+ if (onPress && (await onPress(context.isMultiMode ? !isSelected : undefined, props)) === false) {
return;
}
- if (migrate) {
- context.onPress(value);
- } else {
- // @ts-expect-error TODO: fix after removing migrate prop completely
- context.onPress(typeof value === 'object' || context.isMultiMode ? value : ({value, label: itemLabel}) as PickerSingleValue);
- }
- }, [migrate, value, context.onPress, onPress]);
+ context.onPress(value);
+ },
+ [value, context.onPress, onPress]);
const onSelectedLayout = useCallback((...args: any[]) => {
_.invoke(context, 'onSelectedLayout', ...args);
@@ -84,7 +76,7 @@ const PickerItem = (props: PickerItemProps) => {
return (
- {itemLabel}
+ {label}
{selectedIndicator}
@@ -102,7 +94,7 @@ const PickerItem = (props: PickerItemProps) => {
customValue={props.customValue}
{...accessibilityProps}
>
- {customRenderItem ? customRenderItem(value, {...props, isSelected, isItemDisabled}, itemLabel) : _renderItem()}
+ {customRenderItem ? customRenderItem(value, {...props, isSelected, isItemDisabled}, label) : _renderItem()}
);
};
diff --git a/src/components/picker/PickerPresenter.ts b/src/components/picker/PickerPresenter.ts
index 482047b3c3..4d27a5fbb4 100644
--- a/src/components/picker/PickerPresenter.ts
+++ b/src/components/picker/PickerPresenter.ts
@@ -19,8 +19,7 @@ export function isItemSelected(childValue: PickerSingleValue, selectedValue?: Pi
if (Array.isArray(selectedValue)) {
isSelected =
_.find(selectedValue, v => {
- // @ts-expect-error TODO: fix after removing migrate prop completely
- return v === childValue || (typeof v === 'object' && v?.value === childValue);
+ return v === childValue;
}) !== undefined;
} else {
isSelected = childValue === selectedValue;
@@ -28,25 +27,6 @@ export function isItemSelected(childValue: PickerSingleValue, selectedValue?: Pi
return isSelected;
}
-// export function getItemValue(props) {
-// if (_.isArray(props.value)) {
-// return props.getItemValue ? _.map(props.value, item => props.getItemValue(item)) : _.map(props.value, 'value');
-// } else if (!_.isObject(props.value)) {
-// return props.value;
-// }
-// return _.invoke(props, 'getItemValue', props.value) || _.get(props.value, 'value');
-// }
-
-export function getItemLabel(label: string, value: PickerValue, getItemLabel?: PickerProps['getItemLabel']) {
- if (_.isObject(value)) {
- if (getItemLabel) {
- return getItemLabel(value);
- }
- return _.get(value, 'label');
- }
- return label;
-}
-
export function shouldFilterOut(searchValue: string, itemLabel?: string) {
return !_.includes(_.lowerCase(itemLabel), _.lowerCase(searchValue));
}
diff --git a/src/components/picker/__tests__/PickerPresenter.spec.js b/src/components/picker/__tests__/PickerPresenter.spec.js
index c23883855e..19b90be019 100644
--- a/src/components/picker/__tests__/PickerPresenter.spec.js
+++ b/src/components/picker/__tests__/PickerPresenter.spec.js
@@ -12,47 +12,4 @@ describe('components/PickerPresenter', () => {
expect(uut.isItemSelected('value', [])).toBe(false);
expect(uut.isItemSelected('value', undefined)).toBe(false);
});
-
- // describe('getItemValue', () => {
- // it('should return item value when item has value prop', () => {
- // expect(uut.getItemValue({value: {value: 'item value'}})).toBe('item value');
- // });
-
- // it('should return item value for multiple values', () => {
- // const itemProps = {value: [{value: '1'}, {value: '2'}, {value: '3'}]};
- // expect(uut.getItemValue(itemProps)).toEqual(['1', '2', '3']);
- // });
-
- // it('should return item value when item has getItemValue prop', () => {
- // const itemProps = {value: {name: 'value', age: 12}, getItemValue: item => item.name};
- // expect(uut.getItemValue(itemProps)).toBe('value');
- // });
-
- // it('should return item value for multiple values when item has getItemValue prop', () => {
- // const itemProps = {value: [{name: 'david'}, {name: 'sarah'}, {name: 'jack'}], getItemValue: item => item.name};
- // expect(uut.getItemValue(itemProps)).toEqual(['david', 'sarah', 'jack']);
- // });
-
- // it('should support backward compatibility for when child item value was not an object', () => {
- // const itemProps = {value: 'item-value'};
- // expect(uut.getItemValue(itemProps)).toEqual('item-value');
- // });
- // });
-
- describe('getItemLabel', () => {
- it('should return item label when value is not an object', () => {
- expect(uut.getItemLabel('label', 'value', undefined)).toEqual('label');
- });
-
- it('should return item label when value is an object', () => {
- const itemProps = {value: {value: 'value', label: 'label'}};
- expect(uut.getItemLabel(undefined, itemProps.value, undefined)).toEqual('label');
- });
-
- it('should return item label according to getLabel function ', () => {
- const getLabel = itemValue => `${itemValue.value} - ${itemValue.label}`;
- const itemProps = {value: {value: 'value', label: 'label'}, getLabel};
- expect(uut.getItemLabel(undefined, itemProps.value, getLabel)).toEqual('value - label');
- });
- });
});
diff --git a/src/components/picker/api/picker.api.json b/src/components/picker/api/picker.api.json
index ef9381df89..87d5e82dfe 100644
--- a/src/components/picker/api/picker.api.json
+++ b/src/components/picker/api/picker.api.json
@@ -11,7 +11,6 @@
"https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/Picker/CustomPicker.gif?raw=true"
],
"props": [
- {"name": "migrate", "type": "boolean", "description": "Temporary prop required for migration to Picker's new API"},
{"name": "value", "type": "string | number", "description": "Picker current value"},
{
"name": "onChange",
diff --git a/src/components/picker/api/pickerItem.api.json b/src/components/picker/api/pickerItem.api.json
index 865925c4b0..88d8156fce 100644
--- a/src/components/picker/api/pickerItem.api.json
+++ b/src/components/picker/api/pickerItem.api.json
@@ -13,11 +13,6 @@
{"name": "value", "type": "string | number", "description": "Item's value"},
{"name": "label", "type": "string", "description": "Item's label"},
{"name": "labelStyle", "type": "ViewStyle", "description": "Item's label style"},
- {
- "name": "getItemLabel",
- "type": "(value: string | number) => string",
- "description": "Custom function for the item label"
- },
{"name": "isSelected", "type": "boolean", "description": "Is the item selected"},
{"name": "selectedIcon", "type": "string", "description": "Pass to change the selected icon"},
{"name": "selectedIconColor", "type": "ImageSource", "description": "Pass to change the selected icon color"},
diff --git a/src/components/picker/helpers/__tests__/usePickerLabel.spec.ts b/src/components/picker/helpers/__tests__/usePickerLabel.spec.ts
index c2db686de5..e1667ce951 100644
--- a/src/components/picker/helpers/__tests__/usePickerLabel.spec.ts
+++ b/src/components/picker/helpers/__tests__/usePickerLabel.spec.ts
@@ -26,7 +26,6 @@ describe('usePickerLabel hook tests', () => {
value,
items,
getLabel
- // getItemLabel,
// accessibilityLabel,
// accessibilityHint,
// placeholder
@@ -36,7 +35,6 @@ describe('usePickerLabel hook tests', () => {
value,
items,
getLabel
- // getItemLabel,
// accessibilityLabel,
// accessibilityHint,
// placeholder
diff --git a/src/components/picker/helpers/usePickerLabel.tsx b/src/components/picker/helpers/usePickerLabel.tsx
index 12224e1a08..0ab495aade 100644
--- a/src/components/picker/helpers/usePickerLabel.tsx
+++ b/src/components/picker/helpers/usePickerLabel.tsx
@@ -5,20 +5,20 @@ import {PickerProps, PickerValue} from '../types';
interface UsePickerLabelProps
extends Pick<
PickerProps,
- 'value' | 'getLabel' | 'getItemLabel' | 'placeholder' | 'accessibilityLabel' | 'accessibilityHint'
+ 'value' | 'getLabel' | 'placeholder' | 'accessibilityLabel' | 'accessibilityHint'
> {
items: {value: string | number; label: string}[] | null | undefined;
}
const usePickerLabel = (props: UsePickerLabelProps) => {
- const {value, items, getLabel, getItemLabel, placeholder, accessibilityLabel, accessibilityHint} = props;
+ const {value, items, getLabel, placeholder, accessibilityLabel, accessibilityHint} = props;
const getLabelsFromArray = useCallback((value: PickerValue) => {
const itemsByValue = _.keyBy(items, 'value');
return _.flow(arr =>
- _.map(arr, item => (_.isPlainObject(item) ? getItemLabel?.(item) || item?.label : itemsByValue[item]?.label)),
+ _.map(arr, item => (_.isPlainObject(item) ? item?.label : itemsByValue[item]?.label)),
arr => _.join(arr, ', '))(value);
- }, [getItemLabel, items]);
+ }, [items]);
const _getLabel = useCallback((value: PickerValue) => {
if (_.isFunction(getLabel) && !_.isUndefined(getLabel(value))) {
diff --git a/src/components/picker/helpers/usePickerMigrationWarnings.tsx b/src/components/picker/helpers/usePickerMigrationWarnings.tsx
index 95f074deca..799955cd78 100644
--- a/src/components/picker/helpers/usePickerMigrationWarnings.tsx
+++ b/src/components/picker/helpers/usePickerMigrationWarnings.tsx
@@ -3,30 +3,15 @@ import {LogService} from '../../../services';
import {PickerProps} from '../types';
// TODO: Remove this whole file when migration is completed
-type UsePickerMigrationWarnings = Pick<
- PickerProps,
- 'children' | 'migrate' | 'getItemLabel' | 'getItemValue' | 'onShow'
->;
+type UsePickerMigrationWarnings = Pick;
const usePickerMigrationWarnings = (props: UsePickerMigrationWarnings) => {
- const {children, migrate, getItemLabel, getItemValue, onShow} = props;
+ const {children, onShow} = props;
useEffect(() => {
if (children) {
LogService.warn(`UILib Picker will stop supporting the 'children' prop in the next major version, please pass 'items' prop instead`);
}
- if (migrate) {
- LogService.warn(`UILib Picker will stop supporting the 'migrate' prop in the next major version, please stop using it. The picker uses the new implementation by default.`);
- }
-
- if (getItemLabel) {
- LogService.warn(`UILib Picker will stop supporting the 'getItemLabel' prop in the next major version, please pass the 'getItemLabel' prop to the specific item instead`);
- }
-
- if (getItemValue) {
- LogService.warn(`UILib Picker will stop supporting the 'getItemValue' prop in the next major version, please stop using it. The value will be extract from 'items' prop instead`);
- }
-
if (onShow) {
LogService.warn(`UILib Picker will stop supporting the 'onShow' prop in the next major version, please pass the 'onShow' prop from the 'pickerModalProps' instead`);
}
diff --git a/src/components/picker/helpers/usePickerSearch.tsx b/src/components/picker/helpers/usePickerSearch.tsx
index 8a52a85b8f..b390788ee4 100644
--- a/src/components/picker/helpers/usePickerSearch.tsx
+++ b/src/components/picker/helpers/usePickerSearch.tsx
@@ -1,25 +1,24 @@
import {useCallback, useState, useMemo} from 'react';
import _ from 'lodash';
import {PickerProps} from '../types';
-import {getItemLabel as getItemLabelPresenter, shouldFilterOut} from '../PickerPresenter';
+import {shouldFilterOut} from '../PickerPresenter';
-type UsePickerSearchProps = Pick;
+type UsePickerSearchProps = Pick;
const usePickerSearch = (props: UsePickerSearchProps) => {
- const {showSearch, onSearchChange, children, getItemLabel, items} = props;
+ const {showSearch, onSearchChange, children, items} = props;
const [searchValue, setSearchValue] = useState('');
const filterItems = useCallback((items: any) => {
if (showSearch && !_.isEmpty(searchValue)) {
return _.filter(items, item => {
- const {label, value, getItemLabel: childGetItemLabel} = item.props || item;
- const itemLabel = getItemLabelPresenter(label, value, childGetItemLabel || getItemLabel);
- return !shouldFilterOut(searchValue, itemLabel);
+ const {label} = item.props || item;
+ return !shouldFilterOut(searchValue, label);
});
}
return items;
},
- [showSearch, searchValue, getItemLabel]);
+ [showSearch, searchValue]);
const filteredItems = useMemo(() => {
return filterItems(children || items);
diff --git a/src/components/picker/helpers/usePickerSelection.tsx b/src/components/picker/helpers/usePickerSelection.tsx
index f8ae064a52..177048b107 100644
--- a/src/components/picker/helpers/usePickerSelection.tsx
+++ b/src/components/picker/helpers/usePickerSelection.tsx
@@ -3,13 +3,13 @@ import _ from 'lodash';
import {PickerProps, PickerValue, PickerSingleValue, PickerMultiValue, PickerModes} from '../types';
interface UsePickerSelectionProps
- extends Pick {
+ extends Pick {
pickerExpandableRef: RefObject;
setSearchValue: (searchValue: string) => void;
}
const usePickerSelection = (props: UsePickerSelectionProps) => {
- const {migrate, value, onChange, topBarProps, pickerExpandableRef, getItemValue, setSearchValue, mode, items} = props;
+ const {value, onChange, topBarProps, pickerExpandableRef, setSearchValue, mode, items} = props;
const [multiDraftValue, setMultiDraftValue] = useState(value as PickerMultiValue);
const [multiFinalValue, setMultiFinalValue] = useState(value as PickerMultiValue);
@@ -29,17 +29,11 @@ const usePickerSelection = (props: UsePickerSelectionProps) => {
[onChange]);
const toggleItemSelection = useCallback((item: PickerSingleValue) => {
- let newValue;
const itemAsArray = [item];
- if (!migrate) {
- newValue = _.xorBy(multiDraftValue, itemAsArray, getItemValue || 'value');
- } else {
- newValue = _.xor(multiDraftValue, itemAsArray);
- }
-
+ const newValue = _.xor(multiDraftValue, itemAsArray);
setMultiDraftValue(newValue);
},
- [multiDraftValue, getItemValue]);
+ [multiDraftValue]);
const cancelSelect = useCallback(() => {
setSearchValue('');
diff --git a/src/components/picker/index.tsx b/src/components/picker/index.tsx
index 3cf21536d8..30e75786b5 100644
--- a/src/components/picker/index.tsx
+++ b/src/components/picker/index.tsx
@@ -67,13 +67,9 @@ const Picker = React.forwardRef((props: PickerProps, ref) => {
listProps,
value,
getLabel,
- getItemLabel,
- getItemValue,
renderItem,
children,
useSafeArea,
- // TODO: Remove migrate props and migrate code
- migrate = true,
accessibilityLabel,
accessibilityHint,
items: propItems,
@@ -90,9 +86,6 @@ const Picker = React.forwardRef((props: PickerProps, ref) => {
const pickerExpandable = useRef(null);
const pickerRef = useImperativePickerHandle(ref, pickerExpandable);
- // TODO: Remove this when migration is completed, starting of v8
- // usePickerMigrationWarnings({children, migrate, getItemLabel, getItemValue});
-
useEffect(() => {
if (propItems) {
setItems(propItems);
@@ -103,7 +96,7 @@ const Picker = React.forwardRef((props: PickerProps, ref) => {
filteredItems,
setSearchValue,
onSearchChange: _onSearchChange
- } = usePickerSearch({showSearch, onSearchChange, getItemLabel, children, items});
+ } = usePickerSearch({showSearch, onSearchChange, children, items});
const {
multiDraftValue,
onDoneSelecting,
@@ -113,11 +106,9 @@ const Picker = React.forwardRef((props: PickerProps, ref) => {
selectedCount,
toggleAllItemsSelection
} = usePickerSelection({
- migrate,
value,
onChange,
pickerExpandableRef: pickerExpandable,
- getItemValue,
topBarProps,
setSearchValue,
mode,
@@ -128,8 +119,10 @@ const Picker = React.forwardRef((props: PickerProps, ref) => {
if (propItems) {
return filteredItems.map((item: PickerItemProps) => ({
...item,
- onPress: useWheelPicker && Constants.accessibility.isScreenReaderEnabled ?
- () => onDoneSelecting(item.value) : undefined
+ onPress:
+ useWheelPicker && Constants.accessibility.isScreenReaderEnabled
+ ? () => onDoneSelecting(item.value)
+ : undefined
}));
}
return filteredItems;
@@ -138,7 +131,6 @@ const Picker = React.forwardRef((props: PickerProps, ref) => {
const {label, accessibilityInfo} = usePickerLabel({
value,
items,
- getItemLabel,
getLabel,
accessibilityLabel,
accessibilityHint,
@@ -163,15 +155,10 @@ const Picker = React.forwardRef((props: PickerProps, ref) => {
}, []);
const contextValue = useMemo(() => {
- // @ts-expect-error cleanup after removing migrate prop
- const pickerValue = !migrate && typeof value === 'object' && !_.isArray(value) ? value?.value : value;
return {
- migrate,
- value: mode === PickerModes.MULTI ? multiDraftValue : pickerValue,
+ value: mode === PickerModes.MULTI ? multiDraftValue : value,
onPress: mode === PickerModes.MULTI ? toggleItemSelection : onDoneSelecting,
isMultiMode: mode === PickerModes.MULTI,
- getItemValue,
- getItemLabel,
onSelectedLayout: onSelectedItemLayout,
renderItem,
selectionLimit,
@@ -180,13 +167,10 @@ const Picker = React.forwardRef((props: PickerProps, ref) => {
toggleAllItemsSelection
};
}, [
- migrate,
mode,
value,
multiDraftValue,
renderItem,
- getItemValue,
- getItemLabel,
selectionLimit,
onSelectedItemLayout,
toggleItemSelection,
diff --git a/src/components/picker/types.ts b/src/components/picker/types.ts
index f92a75a4ed..f7cf075484 100644
--- a/src/components/picker/types.ts
+++ b/src/components/picker/types.ts
@@ -50,21 +50,6 @@ export interface PickerSearchStyle {
}
type PickerPropsDeprecation = {
- /**
- * @deprecated
- * Temporary prop required for migration to Picker's new API
- */
- migrate?: boolean;
- /**
- * @deprecated
- * A function that extract the unique value out of the value prop in case value has a custom structure (e.g. {myValue, myLabel})
- */
- getItemValue?: (value: PickerValue) => any;
- /**
- * @deprecated
- * A function that extract the label out of the value prop in case value has a custom structure (e.g. {myValue, myLabel})
- */
- getItemLabel?: (value: PickerValue) => string;
/**
* @deprecated
* Callback for modal onShow event
@@ -315,14 +300,6 @@ export interface PickerItemProps extends Pick;
- /**
- * Custom function for the item label (e.g (value) => customLabel)
- */
- getItemLabel?: (value: PickerValue) => string;
- /**
- * @deprecated Function to return the value out of the item value prop when value is custom shaped.
- */
- getItemValue?: PickerProps['getItemValue'];
/**
* Render custom item
*/
@@ -351,8 +328,7 @@ export interface PickerItemProps extends Pick {
+export interface PickerContextProps extends Pick {
onPress: (value: PickerSingleValue) => void;
isMultiMode: boolean;
onSelectedLayout: (event: any) => any;