Skip to content

Commit 4a3f9b8

Browse files
adids1221M-i-k-e-l
andauthored
Picker - migrate prop deprecation end (#3737)
* Remove deprecated 'migrate' prop and related migration code from Picker component * refactor: PickerItem removed itemValue * docs: update Picker documentation to remove `migrate` prop and clarify value format * refactor: remove deprecated getItemLabel and getItemValue props from Picker components * refactor: simplify Picker API and enhance type safety by removing deprecated props and updating value handling * refactor: remove getItemLabel usage from Picker components and simplify label handling * docs: update Picker migration notes * refactor: remove unused getItemValue/Label --------- Co-authored-by: Miki Leib <[email protected]>
1 parent 6771fde commit 4a3f9b8

File tree

13 files changed

+52
-174
lines changed

13 files changed

+52
-174
lines changed

docs/getting-started/v8.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,22 @@ Removed
7676
Use the `backgroundColor` prop instead of `contentContainerStyle={{backgroundColor}}`
7777
Fix card being transparent on Android
7878
`onCollapseChanged` will now be called after the animation has ended (as was intended)
79+
80+
### Picker
81+
The component was refactored to simplify its API and improve type safety.
82+
83+
#### Migration Steps
84+
85+
**Picker:**
86+
87+
- `value` - The picker now only supports primitive values (string | number) instead of object-based values
88+
- `migrate` - Removed
89+
90+
**Picker.Item:**
91+
92+
- Items structure remains the same: `{label: string, value: primitive}` format is unchanged
93+
- All items now use the `label` prop directly - no custom label transformation needed
94+
- `getItemLabel` - Removed (use `item.label` to get label)
95+
- `getItemValue` - Removed (use `item.value` to get value)
96+
97+
Check out the full API: https://wix.github.io/react-native-ui-lib/docs/components/form/Picker

src/components/picker/PickerItem.tsx

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import View from '../view';
99
import TouchableOpacity from '../touchableOpacity';
1010
import Image from '../image';
1111
import Text from '../text';
12-
import {getItemLabel, isItemSelected} from './PickerPresenter';
12+
import {isItemSelected} from './PickerPresenter';
1313
import PickerContext from './PickerContext';
14-
import {PickerItemProps, PickerSingleValue} from './types';
14+
import {PickerItemProps} from './types';
1515

1616
/**
1717
* @description: Picker.Item, for configuring the Picker's selectable options
@@ -29,12 +29,8 @@ const PickerItem = (props: PickerItemProps) => {
2929
testID
3030
} = props;
3131
const context = useContext(PickerContext);
32-
const {migrate} = context;
3332
const customRenderItem = props.renderItem || context.renderItem;
34-
// @ts-expect-error TODO: fix after removing migrate prop completely
35-
const itemValue = !migrate && typeof value === 'object' ? value?.value : value;
36-
const isSelected = isItemSelected(itemValue, context.value);
37-
const itemLabel = getItemLabel(label, value, props.getItemLabel || context.getItemLabel);
33+
const isSelected = isItemSelected(value, context.value);
3834
const selectedCounter = context.selectionLimit && _.isArray(context.value) && context.value?.length;
3935
const accessibilityProps = {
4036
accessibilityState: isSelected ? {selected: true} : undefined,
@@ -65,16 +61,12 @@ const PickerItem = (props: PickerItemProps) => {
6561
const _onPress = useCallback(async (props: any) => {
6662
// Using !(await onPress?.(item)) does not work properly when onPress is not sent
6763
// We have to explicitly state `false` so a synchronous void (undefined) will still work as expected
68-
if (onPress && await onPress(context.isMultiMode ? !isSelected : undefined, props) === false) {
64+
if (onPress && (await onPress(context.isMultiMode ? !isSelected : undefined, props)) === false) {
6965
return;
7066
}
71-
if (migrate) {
72-
context.onPress(value);
73-
} else {
74-
// @ts-expect-error TODO: fix after removing migrate prop completely
75-
context.onPress(typeof value === 'object' || context.isMultiMode ? value : ({value, label: itemLabel}) as PickerSingleValue);
76-
}
77-
}, [migrate, value, context.onPress, onPress]);
67+
context.onPress(value);
68+
},
69+
[value, context.onPress, onPress]);
7870

7971
const onSelectedLayout = useCallback((...args: any[]) => {
8072
_.invoke(context, 'onSelectedLayout', ...args);
@@ -84,7 +76,7 @@ const PickerItem = (props: PickerItemProps) => {
8476
return (
8577
<View style={styles.container} flex row spread centerV>
8678
<Text numberOfLines={1} style={itemLabelStyle}>
87-
{itemLabel}
79+
{label}
8880
</Text>
8981
{selectedIndicator}
9082
</View>
@@ -102,7 +94,7 @@ const PickerItem = (props: PickerItemProps) => {
10294
customValue={props.customValue}
10395
{...accessibilityProps}
10496
>
105-
{customRenderItem ? customRenderItem(value, {...props, isSelected, isItemDisabled}, itemLabel) : _renderItem()}
97+
{customRenderItem ? customRenderItem(value, {...props, isSelected, isItemDisabled}, label) : _renderItem()}
10698
</TouchableOpacity>
10799
);
108100
};

src/components/picker/PickerPresenter.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,14 @@ export function isItemSelected(childValue: PickerSingleValue, selectedValue?: Pi
1919
if (Array.isArray(selectedValue)) {
2020
isSelected =
2121
_.find(selectedValue, v => {
22-
// @ts-expect-error TODO: fix after removing migrate prop completely
23-
return v === childValue || (typeof v === 'object' && v?.value === childValue);
22+
return v === childValue;
2423
}) !== undefined;
2524
} else {
2625
isSelected = childValue === selectedValue;
2726
}
2827
return isSelected;
2928
}
3029

31-
// export function getItemValue(props) {
32-
// if (_.isArray(props.value)) {
33-
// return props.getItemValue ? _.map(props.value, item => props.getItemValue(item)) : _.map(props.value, 'value');
34-
// } else if (!_.isObject(props.value)) {
35-
// return props.value;
36-
// }
37-
// return _.invoke(props, 'getItemValue', props.value) || _.get(props.value, 'value');
38-
// }
39-
40-
export function getItemLabel(label: string, value: PickerValue, getItemLabel?: PickerProps['getItemLabel']) {
41-
if (_.isObject(value)) {
42-
if (getItemLabel) {
43-
return getItemLabel(value);
44-
}
45-
return _.get(value, 'label');
46-
}
47-
return label;
48-
}
49-
5030
export function shouldFilterOut(searchValue: string, itemLabel?: string) {
5131
return !_.includes(_.lowerCase(itemLabel), _.lowerCase(searchValue));
5232
}

src/components/picker/__tests__/PickerPresenter.spec.js

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -12,47 +12,4 @@ describe('components/PickerPresenter', () => {
1212
expect(uut.isItemSelected('value', [])).toBe(false);
1313
expect(uut.isItemSelected('value', undefined)).toBe(false);
1414
});
15-
16-
// describe('getItemValue', () => {
17-
// it('should return item value when item has value prop', () => {
18-
// expect(uut.getItemValue({value: {value: 'item value'}})).toBe('item value');
19-
// });
20-
21-
// it('should return item value for multiple values', () => {
22-
// const itemProps = {value: [{value: '1'}, {value: '2'}, {value: '3'}]};
23-
// expect(uut.getItemValue(itemProps)).toEqual(['1', '2', '3']);
24-
// });
25-
26-
// it('should return item value when item has getItemValue prop', () => {
27-
// const itemProps = {value: {name: 'value', age: 12}, getItemValue: item => item.name};
28-
// expect(uut.getItemValue(itemProps)).toBe('value');
29-
// });
30-
31-
// it('should return item value for multiple values when item has getItemValue prop', () => {
32-
// const itemProps = {value: [{name: 'david'}, {name: 'sarah'}, {name: 'jack'}], getItemValue: item => item.name};
33-
// expect(uut.getItemValue(itemProps)).toEqual(['david', 'sarah', 'jack']);
34-
// });
35-
36-
// it('should support backward compatibility for when child item value was not an object', () => {
37-
// const itemProps = {value: 'item-value'};
38-
// expect(uut.getItemValue(itemProps)).toEqual('item-value');
39-
// });
40-
// });
41-
42-
describe('getItemLabel', () => {
43-
it('should return item label when value is not an object', () => {
44-
expect(uut.getItemLabel('label', 'value', undefined)).toEqual('label');
45-
});
46-
47-
it('should return item label when value is an object', () => {
48-
const itemProps = {value: {value: 'value', label: 'label'}};
49-
expect(uut.getItemLabel(undefined, itemProps.value, undefined)).toEqual('label');
50-
});
51-
52-
it('should return item label according to getLabel function ', () => {
53-
const getLabel = itemValue => `${itemValue.value} - ${itemValue.label}`;
54-
const itemProps = {value: {value: 'value', label: 'label'}, getLabel};
55-
expect(uut.getItemLabel(undefined, itemProps.value, getLabel)).toEqual('value - label');
56-
});
57-
});
5815
});

src/components/picker/api/picker.api.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
"https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/Picker/CustomPicker.gif?raw=true"
1212
],
1313
"props": [
14-
{"name": "migrate", "type": "boolean", "description": "Temporary prop required for migration to Picker's new API"},
1514
{"name": "value", "type": "string | number", "description": "Picker current value"},
1615
{
1716
"name": "onChange",

src/components/picker/api/pickerItem.api.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@
1313
{"name": "value", "type": "string | number", "description": "Item's value"},
1414
{"name": "label", "type": "string", "description": "Item's label"},
1515
{"name": "labelStyle", "type": "ViewStyle", "description": "Item's label style"},
16-
{
17-
"name": "getItemLabel",
18-
"type": "(value: string | number) => string",
19-
"description": "Custom function for the item label"
20-
},
2116
{"name": "isSelected", "type": "boolean", "description": "Is the item selected"},
2217
{"name": "selectedIcon", "type": "string", "description": "Pass to change the selected icon"},
2318
{"name": "selectedIconColor", "type": "ImageSource", "description": "Pass to change the selected icon color"},

src/components/picker/helpers/__tests__/usePickerLabel.spec.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ describe('usePickerLabel hook tests', () => {
2626
value,
2727
items,
2828
getLabel
29-
// getItemLabel,
3029
// accessibilityLabel,
3130
// accessibilityHint,
3231
// placeholder
@@ -36,7 +35,6 @@ describe('usePickerLabel hook tests', () => {
3635
value,
3736
items,
3837
getLabel
39-
// getItemLabel,
4038
// accessibilityLabel,
4139
// accessibilityHint,
4240
// placeholder

src/components/picker/helpers/usePickerLabel.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ import {PickerProps, PickerValue} from '../types';
55
interface UsePickerLabelProps
66
extends Pick<
77
PickerProps,
8-
'value' | 'getLabel' | 'getItemLabel' | 'placeholder' | 'accessibilityLabel' | 'accessibilityHint'
8+
'value' | 'getLabel' | 'placeholder' | 'accessibilityLabel' | 'accessibilityHint'
99
> {
1010
items: {value: string | number; label: string}[] | null | undefined;
1111
}
1212

1313
const usePickerLabel = (props: UsePickerLabelProps) => {
14-
const {value, items, getLabel, getItemLabel, placeholder, accessibilityLabel, accessibilityHint} = props;
14+
const {value, items, getLabel, placeholder, accessibilityLabel, accessibilityHint} = props;
1515

1616
const getLabelsFromArray = useCallback((value: PickerValue) => {
1717
const itemsByValue = _.keyBy(items, 'value');
1818
return _.flow(arr =>
19-
_.map(arr, item => (_.isPlainObject(item) ? getItemLabel?.(item) || item?.label : itemsByValue[item]?.label)),
19+
_.map(arr, item => (_.isPlainObject(item) ? item?.label : itemsByValue[item]?.label)),
2020
arr => _.join(arr, ', '))(value);
21-
}, [getItemLabel, items]);
21+
}, [items]);
2222

2323
const _getLabel = useCallback((value: PickerValue) => {
2424
if (_.isFunction(getLabel) && !_.isUndefined(getLabel(value))) {

src/components/picker/helpers/usePickerMigrationWarnings.tsx

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,15 @@ import {LogService} from '../../../services';
33
import {PickerProps} from '../types';
44

55
// TODO: Remove this whole file when migration is completed
6-
type UsePickerMigrationWarnings = Pick<
7-
PickerProps,
8-
'children' | 'migrate' | 'getItemLabel' | 'getItemValue' | 'onShow'
9-
>;
6+
type UsePickerMigrationWarnings = Pick<PickerProps, 'children' | 'onShow'>;
107

118
const usePickerMigrationWarnings = (props: UsePickerMigrationWarnings) => {
12-
const {children, migrate, getItemLabel, getItemValue, onShow} = props;
9+
const {children, onShow} = props;
1310
useEffect(() => {
1411
if (children) {
1512
LogService.warn(`UILib Picker will stop supporting the 'children' prop in the next major version, please pass 'items' prop instead`);
1613
}
1714

18-
if (migrate) {
19-
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.`);
20-
}
21-
22-
if (getItemLabel) {
23-
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`);
24-
}
25-
26-
if (getItemValue) {
27-
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`);
28-
}
29-
3015
if (onShow) {
3116
LogService.warn(`UILib Picker will stop supporting the 'onShow' prop in the next major version, please pass the 'onShow' prop from the 'pickerModalProps' instead`);
3217
}

src/components/picker/helpers/usePickerSearch.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
11
import {useCallback, useState, useMemo} from 'react';
22
import _ from 'lodash';
33
import {PickerProps} from '../types';
4-
import {getItemLabel as getItemLabelPresenter, shouldFilterOut} from '../PickerPresenter';
4+
import {shouldFilterOut} from '../PickerPresenter';
55

6-
type UsePickerSearchProps = Pick<PickerProps, 'showSearch' | 'onSearchChange' | 'children' | 'getItemLabel' | 'items'>;
6+
type UsePickerSearchProps = Pick<PickerProps, 'showSearch' | 'onSearchChange' | 'children' | 'items'>;
77

88
const usePickerSearch = (props: UsePickerSearchProps) => {
9-
const {showSearch, onSearchChange, children, getItemLabel, items} = props;
9+
const {showSearch, onSearchChange, children, items} = props;
1010
const [searchValue, setSearchValue] = useState('');
1111

1212
const filterItems = useCallback((items: any) => {
1313
if (showSearch && !_.isEmpty(searchValue)) {
1414
return _.filter(items, item => {
15-
const {label, value, getItemLabel: childGetItemLabel} = item.props || item;
16-
const itemLabel = getItemLabelPresenter(label, value, childGetItemLabel || getItemLabel);
17-
return !shouldFilterOut(searchValue, itemLabel);
15+
const {label} = item.props || item;
16+
return !shouldFilterOut(searchValue, label);
1817
});
1918
}
2019
return items;
2120
},
22-
[showSearch, searchValue, getItemLabel]);
21+
[showSearch, searchValue]);
2322

2423
const filteredItems = useMemo(() => {
2524
return filterItems(children || items);

src/components/picker/helpers/usePickerSelection.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import _ from 'lodash';
33
import {PickerProps, PickerValue, PickerSingleValue, PickerMultiValue, PickerModes} from '../types';
44

55
interface UsePickerSelectionProps
6-
extends Pick<PickerProps, 'migrate' | 'value' | 'onChange' | 'getItemValue' | 'topBarProps' | 'mode' | 'items'> {
6+
extends Pick<PickerProps, 'value' | 'onChange' | 'topBarProps' | 'mode' | 'items'> {
77
pickerExpandableRef: RefObject<any>;
88
setSearchValue: (searchValue: string) => void;
99
}
1010

1111
const usePickerSelection = (props: UsePickerSelectionProps) => {
12-
const {migrate, value, onChange, topBarProps, pickerExpandableRef, getItemValue, setSearchValue, mode, items} = props;
12+
const {value, onChange, topBarProps, pickerExpandableRef, setSearchValue, mode, items} = props;
1313
const [multiDraftValue, setMultiDraftValue] = useState(value as PickerMultiValue);
1414
const [multiFinalValue, setMultiFinalValue] = useState(value as PickerMultiValue);
1515

@@ -29,17 +29,11 @@ const usePickerSelection = (props: UsePickerSelectionProps) => {
2929
[onChange]);
3030

3131
const toggleItemSelection = useCallback((item: PickerSingleValue) => {
32-
let newValue;
3332
const itemAsArray = [item];
34-
if (!migrate) {
35-
newValue = _.xorBy(multiDraftValue, itemAsArray, getItemValue || 'value');
36-
} else {
37-
newValue = _.xor(multiDraftValue, itemAsArray);
38-
}
39-
33+
const newValue = _.xor(multiDraftValue, itemAsArray);
4034
setMultiDraftValue(newValue);
4135
},
42-
[multiDraftValue, getItemValue]);
36+
[multiDraftValue]);
4337

4438
const cancelSelect = useCallback(() => {
4539
setSearchValue('');

0 commit comments

Comments
 (0)