From 67c7fb89596b5af58df298abdb6aa46c0aec79dd Mon Sep 17 00:00:00 2001 From: adids1221 Date: Tue, 20 May 2025 19:00:12 +0300 Subject: [PATCH 1/8] Remove deprecated 'migrate' prop and related migration code from Picker component --- src/components/picker/PickerItem.tsx | 19 +++++++------------ src/components/picker/PickerPresenter.ts | 3 +-- src/components/picker/api/picker.api.json | 1 - .../helpers/usePickerMigrationWarnings.tsx | 11 ++--------- .../picker/helpers/usePickerSelection.tsx | 12 +++--------- src/components/picker/index.tsx | 17 ++++++----------- src/components/picker/types.ts | 7 +------ 7 files changed, 20 insertions(+), 50 deletions(-) diff --git a/src/components/picker/PickerItem.tsx b/src/components/picker/PickerItem.tsx index 2b4405258d..2982362beb 100644 --- a/src/components/picker/PickerItem.tsx +++ b/src/components/picker/PickerItem.tsx @@ -11,7 +11,7 @@ import Image from '../image'; import Text from '../text'; import {getItemLabel, 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,10 +29,9 @@ 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 itemValue = value; const isSelected = isItemSelected(itemValue, context.value); const itemLabel = getItemLabel(label, value, props.getItemLabel || context.getItemLabel); const selectedCounter = context.selectionLimit && _.isArray(context.value) && context.value?.length; @@ -65,16 +64,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); diff --git a/src/components/picker/PickerPresenter.ts b/src/components/picker/PickerPresenter.ts index 482047b3c3..29872dad23 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; 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/helpers/usePickerMigrationWarnings.tsx b/src/components/picker/helpers/usePickerMigrationWarnings.tsx index 95f074deca..96ec12e774 100644 --- a/src/components/picker/helpers/usePickerMigrationWarnings.tsx +++ b/src/components/picker/helpers/usePickerMigrationWarnings.tsx @@ -3,22 +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, getItemLabel, getItemValue, 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`); } diff --git a/src/components/picker/helpers/usePickerSelection.tsx b/src/components/picker/helpers/usePickerSelection.tsx index f8ae064a52..7c37d61ba2 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, getItemValue, setSearchValue, mode, items} = props; const [multiDraftValue, setMultiDraftValue] = useState(value as PickerMultiValue); const [multiFinalValue, setMultiFinalValue] = useState(value as PickerMultiValue); @@ -29,14 +29,8 @@ 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]); diff --git a/src/components/picker/index.tsx b/src/components/picker/index.tsx index 3cf21536d8..dbb776b50c 100644 --- a/src/components/picker/index.tsx +++ b/src/components/picker/index.tsx @@ -72,8 +72,6 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { renderItem, children, useSafeArea, - // TODO: Remove migrate props and migrate code - migrate = true, accessibilityLabel, accessibilityHint, items: propItems, @@ -91,7 +89,7 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { const pickerRef = useImperativePickerHandle(ref, pickerExpandable); // TODO: Remove this when migration is completed, starting of v8 - // usePickerMigrationWarnings({children, migrate, getItemLabel, getItemValue}); + // usePickerMigrationWarnings({children, getItemLabel, getItemValue}); useEffect(() => { if (propItems) { @@ -113,7 +111,6 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { selectedCount, toggleAllItemsSelection } = usePickerSelection({ - migrate, value, onChange, pickerExpandableRef: pickerExpandable, @@ -128,8 +125,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; @@ -163,11 +162,8 @@ 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, @@ -180,7 +176,6 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { toggleAllItemsSelection }; }, [ - migrate, mode, value, multiDraftValue, diff --git a/src/components/picker/types.ts b/src/components/picker/types.ts index f92a75a4ed..7b68f39852 100644 --- a/src/components/picker/types.ts +++ b/src/components/picker/types.ts @@ -50,11 +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}) @@ -352,7 +347,7 @@ export interface PickerItemProps extends Pick { + extends Pick { onPress: (value: PickerSingleValue) => void; isMultiMode: boolean; onSelectedLayout: (event: any) => any; From 7aba1074a6445362a137ffab0fd4c9db6ad2620f Mon Sep 17 00:00:00 2001 From: adids1221 Date: Tue, 3 Jun 2025 10:02:48 +0300 Subject: [PATCH 2/8] refactor: PickerItem removed itemValue --- src/components/picker/PickerItem.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/picker/PickerItem.tsx b/src/components/picker/PickerItem.tsx index 2982362beb..10092e438d 100644 --- a/src/components/picker/PickerItem.tsx +++ b/src/components/picker/PickerItem.tsx @@ -31,8 +31,7 @@ const PickerItem = (props: PickerItemProps) => { const context = useContext(PickerContext); const customRenderItem = props.renderItem || context.renderItem; - const itemValue = value; - const isSelected = isItemSelected(itemValue, context.value); + const isSelected = isItemSelected(value, context.value); const itemLabel = getItemLabel(label, value, props.getItemLabel || context.getItemLabel); const selectedCounter = context.selectionLimit && _.isArray(context.value) && context.value?.length; const accessibilityProps = { From 34acc4ee3db5b615c83e0d019acf41d174f1322b Mon Sep 17 00:00:00 2001 From: adids1221 Date: Tue, 3 Jun 2025 13:40:25 +0300 Subject: [PATCH 3/8] docs: update Picker documentation to remove `migrate` prop and clarify value format --- docs/getting-started/v8.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/getting-started/v8.md b/docs/getting-started/v8.md index 589e2a6737..707067c270 100644 --- a/docs/getting-started/v8.md +++ b/docs/getting-started/v8.md @@ -73,3 +73,6 @@ Removed (use `PanView` instead) 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 +Removed `migrate` prop - the picker now only supports the new value format (direct values instead of `{value, label}` objects). \ No newline at end of file From e85b3ed3e0d1aa47e58c786d4ba50260336caf67 Mon Sep 17 00:00:00 2001 From: adids1221 Date: Sun, 8 Jun 2025 12:30:52 +0300 Subject: [PATCH 4/8] refactor: remove deprecated getItemLabel and getItemValue props from Picker components --- src/components/picker/PickerItem.tsx | 3 +-- src/components/picker/PickerPresenter.ts | 12 +++++------- .../picker/__tests__/PickerPresenter.spec.js | 17 ++++++++--------- .../picker/helpers/usePickerLabel.tsx | 8 ++++---- .../helpers/usePickerMigrationWarnings.tsx | 12 ++---------- .../picker/helpers/usePickerSearch.tsx | 10 +++++----- .../picker/helpers/usePickerSelection.tsx | 6 +++--- src/components/picker/index.tsx | 13 +------------ src/components/picker/types.ts | 17 +---------------- 9 files changed, 30 insertions(+), 68 deletions(-) diff --git a/src/components/picker/PickerItem.tsx b/src/components/picker/PickerItem.tsx index 10092e438d..5409b4cfc4 100644 --- a/src/components/picker/PickerItem.tsx +++ b/src/components/picker/PickerItem.tsx @@ -30,9 +30,8 @@ const PickerItem = (props: PickerItemProps) => { } = props; const context = useContext(PickerContext); const customRenderItem = props.renderItem || context.renderItem; - const isSelected = isItemSelected(value, context.value); - const itemLabel = getItemLabel(label, value, props.getItemLabel || context.getItemLabel); + const itemLabel = getItemLabel(label, value, props.getItemLabel); const selectedCounter = context.selectionLimit && _.isArray(context.value) && context.value?.length; const accessibilityProps = { accessibilityState: isSelected ? {selected: true} : undefined, diff --git a/src/components/picker/PickerPresenter.ts b/src/components/picker/PickerPresenter.ts index 29872dad23..0e8d227a9e 100644 --- a/src/components/picker/PickerPresenter.ts +++ b/src/components/picker/PickerPresenter.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; import React from 'react'; -import {PickerProps, PickerSingleValue, PickerValue} from './types'; +import {PickerProps, PickerSingleValue, PickerValue, PickerItemProps} from './types'; export function extractPickerItems(props: PickerProps) { const {children} = props; @@ -36,12 +36,10 @@ export function isItemSelected(childValue: PickerSingleValue, selectedValue?: Pi // 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'); +export function getItemLabel(label: string, value: PickerValue, getItemLabel?: PickerItemProps['getItemLabel']) { + if (getItemLabel) { + const customLabel = getItemLabel(value); + return customLabel !== undefined ? customLabel : label; } return label; } diff --git a/src/components/picker/__tests__/PickerPresenter.spec.js b/src/components/picker/__tests__/PickerPresenter.spec.js index c23883855e..c99fe06066 100644 --- a/src/components/picker/__tests__/PickerPresenter.spec.js +++ b/src/components/picker/__tests__/PickerPresenter.spec.js @@ -40,19 +40,18 @@ describe('components/PickerPresenter', () => { // }); 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 no custom getItemLabel function is provided', () => { + expect(uut.getItemLabel('JavaScript', 'js', undefined)).toEqual('JavaScript'); }); - 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 custom label when getItemLabel function is provided', () => { + const customGetItemLabel = (value) => `Custom: ${value}`; + expect(uut.getItemLabel('JavaScript', 'js', customGetItemLabel)).toEqual('Custom: js'); }); - 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'); + it('should return original label when getItemLabel function returns undefined', () => { + const customGetItemLabel = () => undefined; + expect(uut.getItemLabel('JavaScript', 'js', customGetItemLabel)).toEqual('JavaScript'); }); }); }); 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 96ec12e774..799955cd78 100644 --- a/src/components/picker/helpers/usePickerMigrationWarnings.tsx +++ b/src/components/picker/helpers/usePickerMigrationWarnings.tsx @@ -3,23 +3,15 @@ import {LogService} from '../../../services'; import {PickerProps} from '../types'; // TODO: Remove this whole file when migration is completed -type UsePickerMigrationWarnings = Pick; +type UsePickerMigrationWarnings = Pick; const usePickerMigrationWarnings = (props: UsePickerMigrationWarnings) => { - const {children, 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 (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..eacf2aaba1 100644 --- a/src/components/picker/helpers/usePickerSearch.tsx +++ b/src/components/picker/helpers/usePickerSearch.tsx @@ -3,23 +3,23 @@ import _ from 'lodash'; import {PickerProps} from '../types'; import {getItemLabel as getItemLabelPresenter, 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); + const {label, value, getItemLabel} = item.props || item; + const itemLabel = getItemLabelPresenter(label, value, getItemLabel); return !shouldFilterOut(searchValue, itemLabel); }); } 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 7c37d61ba2..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 {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); @@ -33,7 +33,7 @@ const usePickerSelection = (props: UsePickerSelectionProps) => { 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 dbb776b50c..30e75786b5 100644 --- a/src/components/picker/index.tsx +++ b/src/components/picker/index.tsx @@ -67,8 +67,6 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { listProps, value, getLabel, - getItemLabel, - getItemValue, renderItem, children, useSafeArea, @@ -88,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, getItemLabel, getItemValue}); - useEffect(() => { if (propItems) { setItems(propItems); @@ -101,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, @@ -114,7 +109,6 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { value, onChange, pickerExpandableRef: pickerExpandable, - getItemValue, topBarProps, setSearchValue, mode, @@ -137,7 +131,6 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { const {label, accessibilityInfo} = usePickerLabel({ value, items, - getItemLabel, getLabel, accessibilityLabel, accessibilityHint, @@ -166,8 +159,6 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { value: mode === PickerModes.MULTI ? multiDraftValue : value, onPress: mode === PickerModes.MULTI ? toggleItemSelection : onDoneSelecting, isMultiMode: mode === PickerModes.MULTI, - getItemValue, - getItemLabel, onSelectedLayout: onSelectedItemLayout, renderItem, selectionLimit, @@ -180,8 +171,6 @@ const Picker = React.forwardRef((props: PickerProps, ref) => { value, multiDraftValue, renderItem, - getItemValue, - getItemLabel, selectionLimit, onSelectedItemLayout, toggleItemSelection, diff --git a/src/components/picker/types.ts b/src/components/picker/types.ts index 7b68f39852..ce2642f146 100644 --- a/src/components/picker/types.ts +++ b/src/components/picker/types.ts @@ -50,16 +50,6 @@ export interface PickerSearchStyle { } type PickerPropsDeprecation = { - /** - * @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 @@ -314,10 +304,6 @@ export interface PickerItemProps extends Pick 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 */ @@ -346,8 +332,7 @@ export interface PickerItemProps extends Pick { +export interface PickerContextProps extends Pick { onPress: (value: PickerSingleValue) => void; isMultiMode: boolean; onSelectedLayout: (event: any) => any; From f6bb46c18bed99f0adda460734615ec14f2e149d Mon Sep 17 00:00:00 2001 From: adids1221 Date: Sun, 8 Jun 2025 12:39:00 +0300 Subject: [PATCH 5/8] refactor: simplify Picker API and enhance type safety by removing deprecated props and updating value handling --- docs/getting-started/v8.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/getting-started/v8.md b/docs/getting-started/v8.md index 707067c270..c6d5406f39 100644 --- a/docs/getting-started/v8.md +++ b/docs/getting-started/v8.md @@ -75,4 +75,24 @@ Fix card being transparent on Android `onCollapseChanged` will now be called after the animation has ended (as was intended) ### Picker -Removed `migrate` prop - the picker now only supports the new value format (direct values instead of `{value, label}` objects). \ No newline at end of file +The component was refactored to simplify its API and improve type safety. + +Props migration: +- `migrate` - Removed (new API is default) +- `getItemValue` - Removed (value extraction is now automatic from `item.value`) +- `getItemLabel` - Removed (label extraction is now automatic from `item.label`) +- `renderPicker` → `renderInput` +- `renderCustomModal` → `renderOverlay` +- `renderCustomDialogHeader` → `renderHeader` +- `pickerModalProps` → `customPickerProps.modalProps` +- `onShow` → `customPickerProps.modalProps.onShow` +- `children` → `items` (recommended) + +Additional notes: +- The picker now only supports primitive values (string | number) instead of object-based values +- For individual items, use `getItemLabel` prop on `Picker.Item` instead of the removed picker-level `getItemLabel` +- Items structure remains the same: `{label: string, value: primitive}` format is unchanged +- The `getLabel` prop still works for custom label formatting of the selected value +- Better TypeScript support and improved performance + +Check out the full API: https://wix.github.io/react-native-ui-lib/docs/components/form/Picker \ No newline at end of file From d1e6b707781f6408c3dd1d09d4923005e581741e Mon Sep 17 00:00:00 2001 From: adids1221 Date: Tue, 10 Jun 2025 10:41:11 +0300 Subject: [PATCH 6/8] refactor: remove getItemLabel usage from Picker components and simplify label handling --- src/components/picker/PickerItem.tsx | 7 +++---- src/components/picker/PickerPresenter.ts | 10 +--------- .../picker/__tests__/PickerPresenter.spec.js | 16 ---------------- src/components/picker/api/pickerItem.api.json | 5 ----- .../picker/helpers/usePickerSearch.tsx | 7 +++---- src/components/picker/types.ts | 4 ---- 6 files changed, 7 insertions(+), 42 deletions(-) diff --git a/src/components/picker/PickerItem.tsx b/src/components/picker/PickerItem.tsx index 5409b4cfc4..7afef0b2ba 100644 --- a/src/components/picker/PickerItem.tsx +++ b/src/components/picker/PickerItem.tsx @@ -9,7 +9,7 @@ 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} from './types'; @@ -31,7 +31,6 @@ const PickerItem = (props: PickerItemProps) => { const context = useContext(PickerContext); const customRenderItem = props.renderItem || context.renderItem; const isSelected = isItemSelected(value, context.value); - const itemLabel = getItemLabel(label, value, props.getItemLabel); const selectedCounter = context.selectionLimit && _.isArray(context.value) && context.value?.length; const accessibilityProps = { accessibilityState: isSelected ? {selected: true} : undefined, @@ -77,7 +76,7 @@ const PickerItem = (props: PickerItemProps) => { return ( - {itemLabel} + {label} {selectedIndicator} @@ -95,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 0e8d227a9e..b6c3ae461f 100644 --- a/src/components/picker/PickerPresenter.ts +++ b/src/components/picker/PickerPresenter.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; import React from 'react'; -import {PickerProps, PickerSingleValue, PickerValue, PickerItemProps} from './types'; +import {PickerProps, PickerSingleValue, PickerValue} from './types'; export function extractPickerItems(props: PickerProps) { const {children} = props; @@ -36,14 +36,6 @@ export function isItemSelected(childValue: PickerSingleValue, selectedValue?: Pi // return _.invoke(props, 'getItemValue', props.value) || _.get(props.value, 'value'); // } -export function getItemLabel(label: string, value: PickerValue, getItemLabel?: PickerItemProps['getItemLabel']) { - if (getItemLabel) { - const customLabel = getItemLabel(value); - return customLabel !== undefined ? customLabel : 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 c99fe06066..ac54ebad11 100644 --- a/src/components/picker/__tests__/PickerPresenter.spec.js +++ b/src/components/picker/__tests__/PickerPresenter.spec.js @@ -38,20 +38,4 @@ describe('components/PickerPresenter', () => { // expect(uut.getItemValue(itemProps)).toEqual('item-value'); // }); // }); - - describe('getItemLabel', () => { - it('should return item label when no custom getItemLabel function is provided', () => { - expect(uut.getItemLabel('JavaScript', 'js', undefined)).toEqual('JavaScript'); - }); - - it('should return custom label when getItemLabel function is provided', () => { - const customGetItemLabel = (value) => `Custom: ${value}`; - expect(uut.getItemLabel('JavaScript', 'js', customGetItemLabel)).toEqual('Custom: js'); - }); - - it('should return original label when getItemLabel function returns undefined', () => { - const customGetItemLabel = () => undefined; - expect(uut.getItemLabel('JavaScript', 'js', customGetItemLabel)).toEqual('JavaScript'); - }); - }); }); 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/usePickerSearch.tsx b/src/components/picker/helpers/usePickerSearch.tsx index eacf2aaba1..b390788ee4 100644 --- a/src/components/picker/helpers/usePickerSearch.tsx +++ b/src/components/picker/helpers/usePickerSearch.tsx @@ -1,7 +1,7 @@ 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; @@ -12,9 +12,8 @@ const usePickerSearch = (props: UsePickerSearchProps) => { const filterItems = useCallback((items: any) => { if (showSearch && !_.isEmpty(searchValue)) { return _.filter(items, item => { - const {label, value, getItemLabel} = item.props || item; - const itemLabel = getItemLabelPresenter(label, value, getItemLabel); - return !shouldFilterOut(searchValue, itemLabel); + const {label} = item.props || item; + return !shouldFilterOut(searchValue, label); }); } return items; diff --git a/src/components/picker/types.ts b/src/components/picker/types.ts index ce2642f146..f7cf075484 100644 --- a/src/components/picker/types.ts +++ b/src/components/picker/types.ts @@ -300,10 +300,6 @@ export interface PickerItemProps extends Pick; - /** - * Custom function for the item label (e.g (value) => customLabel) - */ - getItemLabel?: (value: PickerValue) => string; /** * Render custom item */ From e21c4fccc369e4974e05c0a0602106fd915d19cb Mon Sep 17 00:00:00 2001 From: adids1221 Date: Tue, 10 Jun 2025 11:07:17 +0300 Subject: [PATCH 7/8] docs: update Picker migration notes --- docs/getting-started/v8.md | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/docs/getting-started/v8.md b/docs/getting-started/v8.md index c6d5406f39..a2fc25190b 100644 --- a/docs/getting-started/v8.md +++ b/docs/getting-started/v8.md @@ -77,22 +77,18 @@ Fix card being transparent on Android ### Picker The component was refactored to simplify its API and improve type safety. -Props migration: -- `migrate` - Removed (new API is default) -- `getItemValue` - Removed (value extraction is now automatic from `item.value`) -- `getItemLabel` - Removed (label extraction is now automatic from `item.label`) -- `renderPicker` → `renderInput` -- `renderCustomModal` → `renderOverlay` -- `renderCustomDialogHeader` → `renderHeader` -- `pickerModalProps` → `customPickerProps.modalProps` -- `onShow` → `customPickerProps.modalProps.onShow` -- `children` → `items` (recommended) - -Additional notes: -- The picker now only supports primitive values (string | number) instead of object-based values -- For individual items, use `getItemLabel` prop on `Picker.Item` instead of the removed picker-level `getItemLabel` +#### 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 -- The `getLabel` prop still works for custom label formatting of the selected value -- Better TypeScript support and improved performance +- 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 \ No newline at end of file +Check out the full API: https://wix.github.io/react-native-ui-lib/docs/components/form/Picker From 84589eb0f802de811330fe94f338ad33d141b843 Mon Sep 17 00:00:00 2001 From: adids1221 Date: Thu, 12 Jun 2025 11:53:40 +0300 Subject: [PATCH 8/8] refactor: remove unused getItemValue/Label --- src/components/picker/PickerPresenter.ts | 9 ------- .../picker/__tests__/PickerPresenter.spec.js | 26 ------------------- .../helpers/__tests__/usePickerLabel.spec.ts | 2 -- 3 files changed, 37 deletions(-) diff --git a/src/components/picker/PickerPresenter.ts b/src/components/picker/PickerPresenter.ts index b6c3ae461f..4d27a5fbb4 100644 --- a/src/components/picker/PickerPresenter.ts +++ b/src/components/picker/PickerPresenter.ts @@ -27,15 +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 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 ac54ebad11..19b90be019 100644 --- a/src/components/picker/__tests__/PickerPresenter.spec.js +++ b/src/components/picker/__tests__/PickerPresenter.spec.js @@ -12,30 +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'); - // }); - // }); }); 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