From ef741c05530a824f7f39add165a52777e98a417b Mon Sep 17 00:00:00 2001 From: Ethan Sharabi <1780255+ethanshar@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:31:03 +0300 Subject: [PATCH 01/35] ReExport calendars component in Incubator (#2555) --- src/incubator/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/incubator/index.ts b/src/incubator/index.ts index 0a3a2845d4..52587399d0 100644 --- a/src/incubator/index.ts +++ b/src/incubator/index.ts @@ -1,4 +1,4 @@ -// export {default as Calendar} from './Calendar'; +export {default as Calendar} from './Calendar'; export {default as ExpandableOverlay} from './expandableOverlay'; // @ts-ignore export { From 7a1c746a9d0b2f7e91b46e4f0be1b322cbf2544c Mon Sep 17 00:00:00 2001 From: Inbal Tish <33805983+Inbal-Tish@users.noreply.github.com> Date: Sun, 16 Apr 2023 11:46:50 +0300 Subject: [PATCH 02/35] Hint - fix anchor position for x = 0 (#2556) --- src/components/hint/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/hint/index.tsx b/src/components/hint/index.tsx index 5baba45250..b261977108 100644 --- a/src/components/hint/index.tsx +++ b/src/components/hint/index.tsx @@ -291,7 +291,7 @@ class Hint extends Component { } getTargetPositionOnScreen() { - if (this.targetLayout?.x && this.targetLayout?.width) { + if (this.targetLayout?.x !== undefined && this.targetLayout?.width) { const targetMidPosition = this.targetLayout.x + this.targetLayout.width / 2; if (targetMidPosition > this.containerWidth * (2 / 3)) { @@ -314,7 +314,7 @@ class Hint extends Component { const {position} = this.props; const hintPositionStyle: HintPositionStyle = {alignItems: 'center'}; - if (this.targetLayout?.x) { + if (this.targetLayout?.x !== undefined) { hintPositionStyle.left = -this.targetLayout.x; } @@ -337,7 +337,7 @@ class Hint extends Component { getHintPadding() { const paddings: Paddings = {paddingVertical: this.hintOffset, paddingHorizontal: this.edgeMargins}; - if (this.useSideTip && this.targetLayout?.x) { + if (this.useSideTip && this.targetLayout?.x !== undefined) { const targetPositionOnScreen = this.getTargetPositionOnScreen(); if (targetPositionOnScreen === TARGET_POSITIONS.LEFT) { paddings.paddingLeft = this.targetLayout.x; @@ -376,7 +376,7 @@ class Hint extends Component { const layoutWidth = this.targetLayout?.width || 0; - if (this.targetLayout?.x) { + if (this.targetLayout?.x !== undefined) { const targetMidWidth = layoutWidth / 2; const tipMidWidth = this.tipSize.width / 2; From 17e97adbe89afe2a03e49c5cb283c7b10c84d049 Mon Sep 17 00:00:00 2001 From: Adi Mordo Date: Mon, 17 Apr 2023 15:27:22 +0300 Subject: [PATCH 03/35] FlashList optional dependency (#2559) * FlashList optional dependecy * Update src/optionalDependencies/index.web.ts Co-authored-by: Lidor Dafna <66782556+lidord-wix@users.noreply.github.com> --------- Co-authored-by: Lidor Dafna <66782556+lidord-wix@users.noreply.github.com> --- src/optionalDependencies/index.web.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/optionalDependencies/index.web.ts b/src/optionalDependencies/index.web.ts index 3890b17746..9c87fef755 100644 --- a/src/optionalDependencies/index.web.ts +++ b/src/optionalDependencies/index.web.ts @@ -4,4 +4,5 @@ export {default as SvgPackage} from './SvgPackage'; export {createShimmerPlaceholder} from './ShimmerPackage'; export {default as LinearGradientPackage} from './LinearGradientPackage'; export {default as PostCssPackage} from './PostCssPackage'; +export {default as FlashListPackage} from './FlashListPackage'; From 9d2bc021926a700866533cb029018b8b8ab9bf52 Mon Sep 17 00:00:00 2001 From: Lidor Dafna <66782556+lidord-wix@users.noreply.github.com> Date: Tue, 18 Apr 2023 08:49:36 +0300 Subject: [PATCH 04/35] support labelStyle in ActionSheet options (#2564) --- src/components/actionSheet/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/actionSheet/index.tsx b/src/components/actionSheet/index.tsx index 2803a547be..736d2e5d7c 100644 --- a/src/components/actionSheet/index.tsx +++ b/src/components/actionSheet/index.tsx @@ -174,7 +174,7 @@ class ActionSheet extends Component { > {this.handleRenderIcon(option)} - + {option.label} From a332a07d832fc15d9bf63175b692ed69b617b110 Mon Sep 17 00:00:00 2001 From: Inbal Tish Date: Wed, 19 Apr 2023 10:20:22 +0300 Subject: [PATCH 05/35] Calendar - fix week number line break --- src/incubator/Calendar/Week.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/incubator/Calendar/Week.tsx b/src/incubator/Calendar/Week.tsx index 3b1de13e2a..f0f2cc2caf 100644 --- a/src/incubator/Calendar/Week.tsx +++ b/src/incubator/Calendar/Week.tsx @@ -9,7 +9,7 @@ import CalendarContext from './CalendarContext'; import Day from './Day'; -const WEEK_NUMBER_WIDTH = 18; +const WEEK_NUMBER_WIDTH = 20; const Week = (props: WeekProps) => { const {weekNumber, year} = props; @@ -22,7 +22,7 @@ const Week = (props: WeekProps) => { const renderWeekNumbers = () => { if (showWeeksNumbers) { - return {weekNumber}; + return {weekNumber}; } }; From 0d913c75779d1825388b224bbedc9c9ecb0a9c11 Mon Sep 17 00:00:00 2001 From: Miki Leib <38354019+M-i-k-e-l@users.noreply.github.com> Date: Wed, 19 Apr 2023 10:42:02 +0300 Subject: [PATCH 06/35] SortableList - add driver and test (#2568) * SortableList - add driver and test * Review fixes --- GestureDetectorMock.tsx | 59 ++++++------- jest-setup.js | 5 ++ src/components/button/Button.driver.ts | 2 +- .../sortableList/SortableListItem.driver.ts | 35 ++++++++ .../sortableList/__tests__/index.spec.tsx | 83 +++++++++++++++++++ src/incubator/Dialog/__tests__/index.spec.tsx | 4 +- src/testkit/Component.driver.ts | 10 ++- src/testkit/UniDriver.ts | 12 +++ src/testkit/drivers/TestingLibraryDriver.tsx | 11 ++- src/testkit/index.ts | 3 +- 10 files changed, 182 insertions(+), 42 deletions(-) create mode 100644 src/components/sortableList/SortableListItem.driver.ts create mode 100644 src/components/sortableList/__tests__/index.spec.tsx diff --git a/GestureDetectorMock.tsx b/GestureDetectorMock.tsx index d92a5f2802..57e2827a5c 100644 --- a/GestureDetectorMock.tsx +++ b/GestureDetectorMock.tsx @@ -6,6 +6,17 @@ type Props = { children: any; }; +const DEFAULT_DATA = { + absoluteX: 0, + absoluteY: 0, + translationX: 0, + translationY: 0, + velocityX: 0, + velocityY: 0, + x: 0, + y: 0 +}; + export class GestureDetectorMock extends React.Component { render() { switch (this.props.gesture.type) { @@ -13,9 +24,9 @@ export class GestureDetectorMock extends React.Component { return ( { - this.props.gesture._handlers.onTouchesDown(); - this.props.gesture._handlers.onEnd(); - this.props.gesture._handlers.onFinalize(); + this.props.gesture._handlers.onTouchesDown?.(); + this.props.gesture._handlers.onEnd?.(); + this.props.gesture._handlers.onFinalize?.(); }} > {this.props.children} @@ -24,37 +35,17 @@ export class GestureDetectorMock extends React.Component { case 'pan': return ( { - this.props.gesture._handlers.onStart({ - absoluteX: 0, - absoluteY: 0, - translationX: 0, - translationY: 0, - velocityX: 0, - velocityY: 0, - x: 0, - y: 0 - }); - this.props.gesture._handlers.onUpdate({ - absoluteX: 0, - absoluteY: 0, - translationX: 0, - translationY: 0, - velocityX: 0, - velocityY: 0, - x: 0, - y: 0 - }); - this.props.gesture._handlers.onEnd({ - absoluteX: 0, - absoluteY: 0, - translationX: 0, - translationY: 0, - velocityX: 0, - velocityY: 0, - x: 0, - y: 0 - }); + onPress={(data) => { + this.props.gesture._handlers.onStart?.(DEFAULT_DATA); + if (Array.isArray(data)) { + data.forEach(info => { + this.props.gesture._handlers.onUpdate?.({...DEFAULT_DATA, ...info}); + }); + } else { + this.props.gesture._handlers.onUpdate?.({...DEFAULT_DATA, ...data}); + } + this.props.gesture._handlers.onEnd?.(DEFAULT_DATA); + this.props.gesture._handlers.onFinalize?.(DEFAULT_DATA); }} > {this.props.children} diff --git a/jest-setup.js b/jest-setup.js index 37144457c5..9b230cc5f1 100644 --- a/jest-setup.js +++ b/jest-setup.js @@ -74,6 +74,11 @@ jest.mock('react-native-gesture-handler', PanMock.onStart = getDefaultMockedHandler('onStart'); PanMock.onUpdate = getDefaultMockedHandler('onUpdate'); PanMock.onEnd = getDefaultMockedHandler('onEnd'); + PanMock.onFinalize = getDefaultMockedHandler('onFinalize'); + PanMock.activateAfterLongPress = getDefaultMockedHandler('activateAfterLongPress'); + PanMock.enabled = getDefaultMockedHandler('enabled'); + PanMock.onTouchesMove = getDefaultMockedHandler('onTouchesMove'); + PanMock.prepare = jest.fn(); PanMock.initialize = jest.fn(); PanMock.toGestureArray = jest.fn(() => { return [PanMock]; diff --git a/src/components/button/Button.driver.ts b/src/components/button/Button.driver.ts index 914a353b42..f300d84a2b 100644 --- a/src/components/button/Button.driver.ts +++ b/src/components/button/Button.driver.ts @@ -4,7 +4,7 @@ import {TextDriver} from '../text/Text.driver'; /** * Please run clear after each test - * */ + */ export class ButtonDriver extends ComponentDriver { private readonly labelDriver: TextDriver; private readonly iconDriver: ImageDriver; diff --git a/src/components/sortableList/SortableListItem.driver.ts b/src/components/sortableList/SortableListItem.driver.ts new file mode 100644 index 0000000000..88e7d9dfee --- /dev/null +++ b/src/components/sortableList/SortableListItem.driver.ts @@ -0,0 +1,35 @@ +import _ from 'lodash'; +import {ComponentDriver} from '../../testkit/Component.driver'; + +/** + * Please run clear after each test + */ +export class SortableListItemDriver extends ComponentDriver { + dragUp = async (indices: number) => { + this.validateIndices(indices); + const data = _.times(indices, index => { + return { + translationY: -52 * (index + 1) + }; + }); + + await this.uniDriver.selectorByTestId(this.testID).then(driver => driver.drag(data)); + }; + + dragDown = async (indices: number) => { + this.validateIndices(indices); + const data = _.times(indices, index => { + return { + translationY: 52 * (index + 1) + }; + }); + + await this.uniDriver.selectorByTestId(this.testID).then(driver => driver.drag(data)); + }; + + private validateIndices = (indices: number) => { + if (indices <= 0 || !Number.isInteger(indices)) { + throw Error('indices must be a positive integer'); + } + }; +} diff --git a/src/components/sortableList/__tests__/index.spec.tsx b/src/components/sortableList/__tests__/index.spec.tsx new file mode 100644 index 0000000000..3c9a86607c --- /dev/null +++ b/src/components/sortableList/__tests__/index.spec.tsx @@ -0,0 +1,83 @@ +import _ from 'lodash'; +import React, {useCallback} from 'react'; +import Text from '../../text'; +import View from '../../view'; +import SortableList from '../index'; +import {ComponentDriver, SortableListItemDriver} from '../../../testkit'; + +const defaultProps = { + testID: 'sortableList' +}; + +const ITEMS = _.times(5, index => { + return { + text: `${index}`, + id: `${index}` + }; +}); + +const TestCase = props => { + const renderItem = useCallback(({item}) => { + return ( + + {item.text} + + ); + }, []); + + return ( + + + + + + ); +}; + +describe('SortableList', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + afterEach(() => { + ComponentDriver.clear(); + SortableListItemDriver.clear(); + }); + + it('SortableList onOrderChange is called - down', async () => { + const onOrderChange = jest.fn(); + const component = ; + const sortableListDriver = new ComponentDriver({component, testID: 'sortableList'}); + expect(await sortableListDriver.exists()).toBeTruthy(); + expect(onOrderChange).toHaveBeenCalledTimes(0); + const item1Driver = new SortableListItemDriver({component, testID: 'item1'}); + expect(await item1Driver.exists()).toBeTruthy(); + await item1Driver.dragDown(1); + expect(onOrderChange).toHaveBeenCalledTimes(1); + expect(onOrderChange).toHaveBeenCalledWith([ + {id: '0', text: '0'}, + {id: '2', text: '2'}, + {id: '1', text: '1'}, + {id: '3', text: '3'}, + {id: '4', text: '4'} + ]); + }); + + it('SortableList onOrderChange is called - up', async () => { + const onOrderChange = jest.fn(); + const component = ; + const sortableListDriver = new ComponentDriver({component, testID: 'sortableList'}); + expect(await sortableListDriver.exists()).toBeTruthy(); + expect(onOrderChange).toHaveBeenCalledTimes(0); + const item4Driver = new SortableListItemDriver({component, testID: 'item4'}); + expect(await item4Driver.exists()).toBeTruthy(); + await item4Driver.dragUp(3); + expect(onOrderChange).toHaveBeenCalledTimes(1); + expect(onOrderChange).toHaveBeenCalledWith([ + {id: '0', text: '0'}, + {id: '4', text: '4'}, + {id: '1', text: '1'}, + {id: '2', text: '2'}, + {id: '3', text: '3'} + ]); + }); +}); diff --git a/src/incubator/Dialog/__tests__/index.spec.tsx b/src/incubator/Dialog/__tests__/index.spec.tsx index c58da529fb..5482deea8f 100644 --- a/src/incubator/Dialog/__tests__/index.spec.tsx +++ b/src/incubator/Dialog/__tests__/index.spec.tsx @@ -41,12 +41,10 @@ const TestCase = props => { ); }; -const _TestCase = props => ; - describe('Incubator.Dialog', () => { it('Incubator.Dialog should exist only if visible', async () => { const onDismiss = jest.fn(); - const component = _TestCase({onDismiss}); + const component = ; const dialogDriver = new ComponentDriver({component, testID: 'dialog'}); expect(await dialogDriver.exists()).toBeFalsy(); const openButtonDriver = new ButtonDriver({component, testID: 'openButton'}); diff --git a/src/testkit/Component.driver.ts b/src/testkit/Component.driver.ts index 8125c113f5..ddd98fee61 100644 --- a/src/testkit/Component.driver.ts +++ b/src/testkit/Component.driver.ts @@ -1,4 +1,4 @@ -import {UniDriver, UniDriverClass} from './UniDriver'; +import {DragData, UniDriver, UniDriverClass} from './UniDriver'; import {TestingLibraryDriver} from './drivers/TestingLibraryDriver'; export type ComponentDriverArgs = { @@ -9,7 +9,7 @@ export type ComponentDriverArgs = { /** * Please run clear after each test - * */ + */ export class ComponentDriver { protected readonly testID: string; protected readonly uniDriver: UniDriver; @@ -45,6 +45,12 @@ export class ComponentDriver { .then((driver) => driver.press()); }; + drag = async (data: DragData | DragData[]) => { + return this.uniDriver + .selectorByTestId(this.testID) + .then((driver) => driver.drag(data)); + }; + focus = async () => { return this.uniDriver .selectorByTestId(this.testID) diff --git a/src/testkit/UniDriver.ts b/src/testkit/UniDriver.ts index 12525caf5e..f2d1e2bf1d 100644 --- a/src/testkit/UniDriver.ts +++ b/src/testkit/UniDriver.ts @@ -1,3 +1,14 @@ +export type DragData = { + absoluteX?: number; + absoluteY?: number; + translationX?: number; + translationY?: number; + velocityX?: number; + velocityY?: number; + x?: number; + y?: number; +}; + export interface UniDriver { selectorByTestId(testId: string): Promise; selectorByText(text: string): Promise; @@ -7,6 +18,7 @@ export interface UniDriver { instance(): Promise; getInstanceProps(): Promise; press(): void; + drag(data: DragData | DragData[]): void; focus(): void; blur(): void; typeText(text: string): Promise; diff --git a/src/testkit/drivers/TestingLibraryDriver.tsx b/src/testkit/drivers/TestingLibraryDriver.tsx index 865e47044f..a51d5cb6dc 100644 --- a/src/testkit/drivers/TestingLibraryDriver.tsx +++ b/src/testkit/drivers/TestingLibraryDriver.tsx @@ -1,4 +1,4 @@ -import {UniDriver} from '../UniDriver'; +import {DragData, UniDriver} from '../UniDriver'; import {fireEvent, render, RenderAPI} from '@testing-library/react-native'; import {ReactTestInstance} from 'react-test-renderer'; import {act} from '@testing-library/react-hooks'; @@ -85,6 +85,15 @@ export class TestingLibraryDriver implements UniDriver { fireEvent.press(this.reactTestInstances[0]); }; + drag = (data: DragData | DragData[]): void => { + if (!this.reactTestInstances) { + throw new NoSelectorException(); + } + this.validateExplicitInstance(); + this.validateSingleInstance(); + fireEvent.press(this.reactTestInstances[0], data); + }; + focus = (): void => { if (!this.reactTestInstances) { throw new NoSelectorException(); diff --git a/src/testkit/index.ts b/src/testkit/index.ts index 790a2c91d0..e1c4c6cc07 100644 --- a/src/testkit/index.ts +++ b/src/testkit/index.ts @@ -1,6 +1,6 @@ export {ComponentDriver} from './Component.driver'; export {ImageDriver} from '../components/image/Image.driver'; -export {TextDriver} from '../components/Text/Text.driver'; +export {TextDriver} from '../components/text/Text.driver'; export {SwitchDriver} from '../components/switch/switch.driver'; export {ButtonDriver} from '../components/button/Button.driver'; export {TextFieldDriver} from '../incubator/TextField/TextField.driver'; @@ -10,3 +10,4 @@ export {CheckboxDriver} from '../components/checkbox/Checkbox.driver'; export {HintDriver} from '../components/hint/Hint.driver'; export {RadioButtonDriver} from '../components/radioButton/RadioButton.driver'; export {RadioGroupDriver} from '../components/radioGroup/RadioGroup.driver'; +export {SortableListItemDriver} from '../components/sortableList/SortableListItem.driver'; From 2cb96c25f4f27324d134156b78aa12efcab16bae Mon Sep 17 00:00:00 2001 From: Lidor Dafna <66782556+lidord-wix@users.noreply.github.com> Date: Wed, 19 Apr 2023 11:45:36 +0300 Subject: [PATCH 07/35] Add segmentStyle prop to SegmentedControl (#2563) * Add segmentStyle prop to SegmentedControl * renaming * renaming #2 --- .../componentScreens/SegmentedControlScreen.tsx | 11 ++++++++++- src/components/segmentedControl/index.tsx | 10 ++++++++-- src/components/segmentedControl/segment.tsx | 11 +++++++++-- .../segmentedControl/segmentedControl.api.json | 1 + 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/demo/src/screens/componentScreens/SegmentedControlScreen.tsx b/demo/src/screens/componentScreens/SegmentedControlScreen.tsx index 4e91fa21b0..7a9bb02f63 100644 --- a/demo/src/screens/componentScreens/SegmentedControlScreen.tsx +++ b/demo/src/screens/componentScreens/SegmentedControlScreen.tsx @@ -14,7 +14,7 @@ const segments = { }, {label: 'Short'} ], - forth: [{label: 'With'}, {label: 'Custom'}, {label: 'Colors'}], + forth: [{label: 'With'}, {label: 'Custom'}, {label: 'Style'}], fifth: [{label: 'Full'}, {label: 'Width'}], sixth: [{label: 'Full'}, {label: 'Width'}, {label: 'With'}, {label: 'A'}, {label: 'Very Long Segment'}] }; @@ -49,6 +49,8 @@ const SegmentedControlScreen = () => { backgroundColor={Colors.$backgroundInverted} activeBackgroundColor={Colors.$backgroundNeutralIdle} inactiveColor={Colors.$textDisabled} + style={styles.customStyle} + segmentsStyle={styles.customSegmentsStyle} /> { const styles = StyleSheet.create({ container: { marginTop: 20 + }, + customStyle: { + height: 50, + width: 300 + }, + customSegmentsStyle: { + height: 50 } }); diff --git a/src/components/segmentedControl/index.tsx b/src/components/segmentedControl/index.tsx index 542a16ae95..580aba1559 100644 --- a/src/components/segmentedControl/index.tsx +++ b/src/components/segmentedControl/index.tsx @@ -13,7 +13,7 @@ import Reanimated, { import {Colors, BorderRadiuses, Spacings} from '../../style'; import {Constants, asBaseComponent} from '../../commons/new'; import View from '../view'; -import Segment, {SegmentedControlItemProps as SegmentProps} from './segment'; +import Segment, {SegmentedControlItemProps} from './segment'; const BORDER_WIDTH = 1; const TIMING_CONFIG: WithTimingConfig = { @@ -21,7 +21,7 @@ const TIMING_CONFIG: WithTimingConfig = { easing: Easing.bezier(0.33, 1, 0.68, 1) }; -export type SegmentedControlItemProps = SegmentProps; +export {SegmentedControlItemProps}; export type SegmentedControlProps = { /** * Array on segments. @@ -71,6 +71,10 @@ export type SegmentedControlProps = { * Trailing throttle time of changing index in ms. */ throttleTime?: number; + /** + * Additional style for the segment + */ + segmentsStyle?: StyleProp; /** * Additional spacing styles for the container */ @@ -98,6 +102,7 @@ const SegmentedControl = (props: SegmentedControlProps) => { outlineColor = activeColor, outlineWidth = BORDER_WIDTH, throttleTime = 0, + segmentsStyle: segmentsStyleProp, testID } = props; const animatedSelectedIndex = useSharedValue(initialIndex); @@ -164,6 +169,7 @@ const SegmentedControl = (props: SegmentedControlProps) => { selectedIndex={animatedSelectedIndex} activeColor={activeColor} inactiveColor={inactiveColor} + style={segmentsStyleProp} {...segments?.[index]} testID={testID} /> diff --git a/src/components/segmentedControl/segment.tsx b/src/components/segmentedControl/segment.tsx index 1354e0d1fe..3ad1d7f57f 100644 --- a/src/components/segmentedControl/segment.tsx +++ b/src/components/segmentedControl/segment.tsx @@ -1,5 +1,5 @@ import React, {useCallback, useMemo} from 'react'; -import {LayoutChangeEvent, ImageSourcePropType, ImageStyle, StyleProp} from 'react-native'; +import {LayoutChangeEvent, ImageSourcePropType, ImageStyle, StyleProp, ViewStyle} from 'react-native'; import Reanimated, {useAnimatedStyle} from 'react-native-reanimated'; import {Spacings, Typography} from '../../style'; import {asBaseComponent} from '../../commons/new'; @@ -49,6 +49,10 @@ export type SegmentProps = SegmentedControlItemProps & { * onLayout function. */ onLayout?: (index: number, event: LayoutChangeEvent) => void; + /** + * Additional style for the segment. + */ + style?: StyleProp; testID?: string; }; @@ -67,6 +71,7 @@ const Segment = React.memo((props: SegmentProps) => { inactiveColor, index, iconOnRight, + style, testID } = props; @@ -80,7 +85,9 @@ const Segment = React.memo((props: SegmentProps) => { return {tintColor}; }); - const segmentStyle = useMemo(() => ({paddingHorizontal: Spacings.s3, paddingVertical: Spacings.s2}), []); + const segmentStyle = useMemo(() => { + return [{paddingHorizontal: Spacings.s3, paddingVertical: Spacings.s2}, style]; + }, [style]); const renderIcon = useCallback(() => { return iconSource && ; diff --git a/src/components/segmentedControl/segmentedControl.api.json b/src/components/segmentedControl/segmentedControl.api.json index 5a27e54cfa..faecdeb6f4 100644 --- a/src/components/segmentedControl/segmentedControl.api.json +++ b/src/components/segmentedControl/segmentedControl.api.json @@ -16,6 +16,7 @@ {"name": "outlineWidth", "type": "number", "description": "The width of the active segment outline"}, {"name": "iconOnRight", "type": "boolean", "description": "Should the icon be on right of the label"}, {"name": "throttleTime", "type": "number", "description": "Trailing throttle time of changing index in ms."}, + {"name": "segmentsStyle", "type": "ViewStyle", "description": "Additional style for the segments"}, {"name": "containerStyle", "type": "ViewStyle", "description": "Additional spacing styles for the container"}, {"name": "style", "type": "ViewStyle", "description": "Custom style to inner container"}, {"name": "testID", "type": "string", "description": "Component test id"} From 4bba273402725790986b2b0a681347c6674db865 Mon Sep 17 00:00:00 2001 From: Adi Mordo Date: Wed, 19 Apr 2023 12:57:04 +0300 Subject: [PATCH 08/35] Changed the FlashList using syntax according to the WebEditor errors (#2569) --- src/incubator/Calendar/Agenda.tsx | 2 +- src/incubator/Calendar/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/incubator/Calendar/Agenda.tsx b/src/incubator/Calendar/Agenda.tsx index 31423cc3b3..a920f0307f 100644 --- a/src/incubator/Calendar/Agenda.tsx +++ b/src/incubator/Calendar/Agenda.tsx @@ -9,7 +9,7 @@ import {isSameDay, isSameMonth} from './helpers/DateUtils'; import {InternalEvent, Event, DateSectionHeader, UpdateSource} from './types'; import CalendarContext from './CalendarContext'; -const {FlashList} = FlashListPackage; +const FlashList = FlashListPackage?.FlashList; // TODO: Fix initial scrolling function Agenda() { diff --git a/src/incubator/Calendar/index.tsx b/src/incubator/Calendar/index.tsx index 5d976933a9..c39cf7ff37 100644 --- a/src/incubator/Calendar/index.tsx +++ b/src/incubator/Calendar/index.tsx @@ -13,7 +13,7 @@ import Agenda from './Agenda'; import TodayButton from './TodayButton'; import Header from './Header'; -const {FlashList} = FlashListPackage; +const FlashList = FlashListPackage?.FlashList; // TODO: Move this logic elsewhere to pre-generate on install? const MONTH_ITEMS = generateMonthItems(2); From 48c3bf2e68fb682664f192dff037dc1eee3389fe Mon Sep 17 00:00:00 2001 From: Inbal Tish <33805983+Inbal-Tish@users.noreply.github.com> Date: Wed, 19 Apr 2023 14:13:29 +0300 Subject: [PATCH 09/35] Feat/calendar (#2503) * Initial creation of new calendar component * Push typings * render initial components for example screen * Add DateUtils with stubs * Add tests for DateUtils * Add stub implementation to getDaysOfWeekNumber * Day - render day from date * Basic implementation of getWeekNumbersOfMonth * Install date-fns * Basic implementation of Month * Add Week * Fix Month component * Change to millis * Add key to week * Fix hardcoded month value we pass to Month component * Add test for invalid month * Render header in calendar item * Header - render header * fix CalendarItem render * Create getDaysOfWeekNumber and dependencies * radability * Add context provider and change firstDayOfWeek to enum * Add date and setDate to context * Add getDateObject worklet * Fix calendar context types and add initialDate prop * Basic Agenda * Header - add months by presing arrow buttons * Add TODOs * Move FirstDayOfWeek from enum to enum+union and fix tests * MILLIS to MS * Add mock data (script still needs work) and link (types need work) * Fix script and types * addMonth - use Date's setMonth function * Moving 'firstDayOfWeek' to be enum * WeekDaysNames - adding new component to Header * add ts notes * update comment for ts version ^4.3.2 * Set WeekDaysNames format in Header * use FirstDayOfWeek enum * use 'DayNamesFormat' enum * remove console * Day - adding onPress Adding style.ts file Calendar - pass date to calendatItem * add todo * Minor fixes and remove todos * Change context's date to selectedDate * remove style file * valueOf to getTime * export Event * Day - adding selection * Wrap CalendarItem with FlashList * Agenda - add headers + scroll on date change * Agenda - set date on scroll * Fix mock and script * UI touchups * sort imports and add test for 'getMonthForIndex' and adding tests to 'isSameDay' * Header - small ui changes * Context - adding 'showWeeksNumbers' * fix indentation * margin * Add TODOs * Add flex to day * Day - mark today * Fix FirstDayOfWeek enum and add update sources * Adding UpdateSource to setDate calls * Implement setDate to store lastUpdateSource * dateutils - adding 'isSameMonth' * Add estimatedItemSize prop to FlashLists * Update mock data * Fix TS errors * Context - add updateSource * fix screen errors * fix updateSource on Context * fix type * Calendar - add scrollToIndex on selectedDate change * Agenda - verify update source and use isSameDay * Calendar - fix scrollToIndex * Agenda initial index * isSameMonth - support MonthProps type for params * remove synchronous calls to isNumber from a worklet * Calendar - add setDate on calendar scroll (by user) to the first of the month * Fix getWeekNumbersOfMonth and add missing tests * Fix typing issues * fix scrolledByUser.value * Calendar - fix scrolledByUser * merge fix * shush ts wrannings * Add todos * DateUtils - add 'getNormalizedDate' function * Change week numbering from ISO to default Change week numbering to use "default" (Jan 1st) and not ISO (Jan 4th) * DateUtils - adding 'isToday' and 'isPastDate' * adding assets * Agenda - fixed height for items and initialScrollIndex * TodayButton * Update calendars mock data * Adding 'staticHeader' prop to calendar and passing it on context * Fix lint * HeaderHeight to context * Utils - use JS Date only inside utils (not as a parameter nor return type) and fix tests to use our inner utils * Fix tests and remove getNormalizedDate * fix lint * Calendar - setDate when 'initialDate' prop changes * Header - fix arrows * fix margins * DateUtils - add 'getTimestamp' from DateObject * fix extra days text color * Adding 'showExtraDays' prop (currently only controls the text color) * re export calendar in incubator * Use useDidUpdate for setDate coming from props change * Fix Android item scroll * Day - fix extra days * hide extraDays * Day - fix inactive date selected * fix CalendatItem default height * Day - fix today background when inactive * Header - arrow press - set day to be the first of the month * demo screen - comment out unused * CalendarProcessor - add past and future range and pass date as start point (other then today) * DateUtils - add 'addYears' to a given date * Day - sync color and backgroundColor animations * Calendar - add infinite scroll * move addPages up * minor changes * Simple POC for scrollable list with native interactions * Add basic implementation of mock server * Add mini example of AgendaList with animated scroll handler * revert playgroundScren changes * fix callback * typo * UpdateSource should not be optional or undefined --------- Co-authored-by: Ethan Sharabi <1780255+ethanshar@users.noreply.github.com> Co-authored-by: M-i-k-e-l --- demo/src/screens/PlaygroundScreen.tsx | 5 - .../IncubatorCalendarScreen/MockServer.ts | 22 +++ .../IncubatorCalendarScreen/index.tsx | 55 +++++--- src/incubator/Calendar/Agenda.tsx | 56 ++++---- src/incubator/Calendar/CalendarItem.tsx | 6 +- src/incubator/Calendar/Day.tsx | 61 +++++---- src/incubator/Calendar/Header.tsx | 24 ++-- src/incubator/Calendar/Month.tsx | 5 +- src/incubator/Calendar/Week.tsx | 13 +- .../Calendar/__tests__/DateUtils.spec.ts | 18 +++ .../Calendar/helpers/CalendarProcessor.ts | 8 +- src/incubator/Calendar/helpers/DateUtils.ts | 19 ++- src/incubator/Calendar/index.tsx | 126 +++++++++++++----- src/incubator/Calendar/types.ts | 19 ++- 14 files changed, 309 insertions(+), 128 deletions(-) create mode 100644 demo/src/screens/incubatorScreens/IncubatorCalendarScreen/MockServer.ts diff --git a/demo/src/screens/PlaygroundScreen.tsx b/demo/src/screens/PlaygroundScreen.tsx index 78b2f1ad21..fd1a8fbf74 100644 --- a/demo/src/screens/PlaygroundScreen.tsx +++ b/demo/src/screens/PlaygroundScreen.tsx @@ -1,5 +1,4 @@ import React, {Component} from 'react'; -import {StyleSheet} from 'react-native'; import {View, Text, Card, TextField, Button} from 'react-native-ui-lib'; //eslint-disable-line export default class PlaygroundScreen extends Component { @@ -19,7 +18,3 @@ export default class PlaygroundScreen extends Component { ); } } - -const styles = StyleSheet.create({ - container: {} -}); diff --git a/demo/src/screens/incubatorScreens/IncubatorCalendarScreen/MockServer.ts b/demo/src/screens/incubatorScreens/IncubatorCalendarScreen/MockServer.ts new file mode 100644 index 0000000000..b942d2885f --- /dev/null +++ b/demo/src/screens/incubatorScreens/IncubatorCalendarScreen/MockServer.ts @@ -0,0 +1,22 @@ +import _ from 'lodash'; +import {data} from './MockData'; + +const PAGE_SIZE = 100; +const FAKE_FETCH_TIME = 1500; + +class MockServer { + async getEvents(date: number): Promise { + return new Promise(resolve => { + const eventIndexByDate = _.findIndex(data, event => { + return event.start > date; + }); + + setTimeout(() => { + const newEvents = _.slice(data, eventIndexByDate - PAGE_SIZE / 2, eventIndexByDate + PAGE_SIZE / 2); + resolve(newEvents); + }, FAKE_FETCH_TIME); + }); + } +} + +export default new MockServer(); diff --git a/demo/src/screens/incubatorScreens/IncubatorCalendarScreen/index.tsx b/demo/src/screens/incubatorScreens/IncubatorCalendarScreen/index.tsx index 845048db69..8762f70c5d 100644 --- a/demo/src/screens/incubatorScreens/IncubatorCalendarScreen/index.tsx +++ b/demo/src/screens/incubatorScreens/IncubatorCalendarScreen/index.tsx @@ -1,27 +1,50 @@ +import _ from 'lodash'; import React, {Component} from 'react'; -import {View, Incubator} from 'react-native-ui-lib'; -import {data} from './MockData'; +import {Incubator} from 'react-native-ui-lib'; +import MockServer from './MockServer'; export default class CalendarScreen extends Component { - // constructor(props) { - // super(props); - - // setTimeout(() => { - // this.setState({date: 1676026748000}); - // }, 2000); - // } + pageIndex = 0; state = { - date: undefined + date: new Date().getTime(), + events: [] as any[], + showLoader: false }; - + + constructor(props: any) { + super(props); + this.loadEvents(this.state.date); + } + + loadEvents = async (date: number) => { + this.setState({showLoader: true}); + const {events} = this.state; + const newEvents = await MockServer.getEvents(date); + this.pageIndex++; + this.setState({events: _.uniqBy([...events, ...newEvents], e => e.id), showLoader: false}); + }; + + onChangeDate = (date: number) => { + console.log('Date change: ', date); + const {events} = this.state; + if (date < events[0]?.start || date > _.last(events)?.start) { + console.log('Load new events'); + this.loadEvents(date); + } + }; + + onEndReached = (date: number) => { + console.log('Reached End: ', date); + this.loadEvents(date); + }; + render() { + const {date, events, showLoader} = this.state; return ( - - - - - + + + ); } } diff --git a/src/incubator/Calendar/Agenda.tsx b/src/incubator/Calendar/Agenda.tsx index a920f0307f..c411804480 100644 --- a/src/incubator/Calendar/Agenda.tsx +++ b/src/incubator/Calendar/Agenda.tsx @@ -1,18 +1,20 @@ import React, {useContext, useCallback, useRef} from 'react'; +import {ActivityIndicator} from 'react-native'; import {runOnJS, useAnimatedReaction, useSharedValue} from 'react-native-reanimated'; import {FlashListPackage} from 'optionalDeps'; import type {FlashList as FlashListType, ViewToken} from '@shopify/flash-list'; -import {BorderRadiuses} from 'style'; +import {BorderRadiuses, Colors} from 'style'; import View from '../../components/view'; import Text from '../../components/text'; import {isSameDay, isSameMonth} from './helpers/DateUtils'; -import {InternalEvent, Event, DateSectionHeader, UpdateSource} from './types'; +import {AgendaProps, InternalEvent, Event, DateSectionHeader, UpdateSource} from './types'; import CalendarContext from './CalendarContext'; const FlashList = FlashListPackage?.FlashList; // TODO: Fix initial scrolling -function Agenda() { +function Agenda(props: AgendaProps) { + const {onEndReached, showLoader} = props; const {data, selectedDate, setDate, updateSource} = useContext(CalendarContext); const flashList = useRef>(null); const closestSectionHeader = useSharedValue(null); @@ -48,14 +50,7 @@ function Agenda() { const renderHeader = useCallback((item: DateSectionHeader) => { return ( - + {item.header} ); @@ -96,7 +91,7 @@ function Agenda() { return selectedDate.value; }, (selected, previous) => { - if (updateSource?.value !== UpdateSource.AGENDA_SCROLL) { + if (updateSource.value !== UpdateSource.AGENDA_SCROLL) { if ( selected !== previous && (closestSectionHeader.value?.date === undefined || !isSameDay(selected, closestSectionHeader.value?.date)) @@ -142,19 +137,32 @@ function Agenda() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const _onEndReached = useCallback(() => { + onEndReached?.(selectedDate.value); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [onEndReached]); + return ( - + + + {showLoader && ( + + + + )} + ); } diff --git a/src/incubator/Calendar/CalendarItem.tsx b/src/incubator/Calendar/CalendarItem.tsx index a571dd7615..90162fe5ed 100644 --- a/src/incubator/Calendar/CalendarItem.tsx +++ b/src/incubator/Calendar/CalendarItem.tsx @@ -7,7 +7,8 @@ import CalendarContext from './CalendarContext'; import Month from './Month'; import Header from './Header'; -const CALENDAR_HEIGHT = 250; + +const CALENDAR_HEIGHT = 270; function CalendarItem(props: CalendarItemProps) { const {year, month} = props; @@ -21,6 +22,7 @@ function CalendarItem(props: CalendarItemProps) { height: CALENDAR_HEIGHT - (staticHeader ? headerHeight.value : 0) } ]; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [staticHeader]); if (month !== undefined) { @@ -34,7 +36,7 @@ function CalendarItem(props: CalendarItemProps) { return null; } -export default CalendarItem; +export default React.memo(CalendarItem); const styles = StyleSheet.create({ container: { diff --git a/src/incubator/Calendar/Day.tsx b/src/incubator/Calendar/Day.tsx index 0c7e8aa252..8bace8c5f9 100644 --- a/src/incubator/Calendar/Day.tsx +++ b/src/incubator/Calendar/Day.tsx @@ -1,7 +1,7 @@ import isNull from 'lodash/isNull'; import React, {useContext, useCallback} from 'react'; import {StyleSheet} from 'react-native'; -import Reanimated, {useSharedValue, useAnimatedStyle, useAnimatedReaction} from 'react-native-reanimated'; +import Reanimated, {useSharedValue, useAnimatedStyle, useAnimatedReaction, withTiming} from 'react-native-reanimated'; import {Colors} from 'style'; import View from '../../components/view'; import TouchableOpacity from '../../components/touchableOpacity'; @@ -11,50 +11,61 @@ import {DayProps, UpdateSource} from './types'; import CalendarContext from './CalendarContext'; +const DAY_SIZE = 32; +const SELECTION_SIZE = 24; +const NO_COLOR = Colors.transparent; +const TEXT_COLOR = Colors.$textPrimary; +const TODAY_BACKGROUND_COLOR = Colors.$backgroundPrimaryLight; +const INACTIVE_TODAY_BACKGROUND_COLOR = Colors.$backgroundNeutral; +const SELECTED_BACKGROUND_COLOR = Colors.$backgroundPrimaryHeavy; +const SELECTED_TEXT_COLOR = Colors.$textDefaultLight; +const INACTIVE_TEXT_COLOR = Colors.$textNeutralLight; + const AnimatedText = Reanimated.createAnimatedComponent(Text); const Day = (props: DayProps) => { - const {date, onPress} = props; - const {selectedDate, setDate} = useContext(CalendarContext); + const {date, onPress, inactive} = props; + const {selectedDate, setDate, showExtraDays} = useContext(CalendarContext); - const shouldMarkSelected = !isNull(date) ? isSameDay(selectedDate.value, date) : false; - const isSelected = useSharedValue(shouldMarkSelected); + const isSelected = useSharedValue(!isNull(date) ? isSameDay(selectedDate.value, date) : false); + const backgroundColor = !isToday(date) ? NO_COLOR : + inactive ? INACTIVE_TODAY_BACKGROUND_COLOR : TODAY_BACKGROUND_COLOR; + const isHidden = !showExtraDays && inactive; - const backgroundColor = isToday(date) ? Colors.$backgroundSuccessHeavy : Colors.transparent; - const textColor = isToday(date) ? Colors.$textDefaultLight : Colors.$backgroundPrimaryHeavy; - - const animatedStyles = useAnimatedStyle(() => { + useAnimatedReaction(() => { + return selectedDate.value; + }, (selected) => { + isSelected.value = !inactive && isSameDay(selected, date!); + }, []); + + const animatedSelectionStyles = useAnimatedStyle(() => { return { - backgroundColor: isSelected.value ? Colors.$backgroundPrimaryHeavy : backgroundColor, - color: isSelected.value ? Colors.$textDefaultLight : textColor + backgroundColor: withTiming(isSelected.value ? SELECTED_BACKGROUND_COLOR : backgroundColor, {duration: 100}) }; }); const animatedTextStyles = useAnimatedStyle(() => { return { - color: isSelected.value ? Colors.$textDefaultLight : textColor + color: withTiming(isSelected.value ? + SELECTED_TEXT_COLOR : inactive ? + showExtraDays ? INACTIVE_TEXT_COLOR : NO_COLOR : TEXT_COLOR, {duration: 100}) }; }); - useAnimatedReaction(() => { - return selectedDate.value; - }, (selected) => { - isSelected.value = isSameDay(selected, date!); - }, []); - const _onPress = useCallback(() => { - if (date !== null) { + if (date !== null && !isHidden) { isSelected.value = true; setDate(date, UpdateSource.DAY_SELECT); onPress?.(date); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [date, setDate, onPress]); const renderDay = () => { const day = !isNull(date) ? getDayOfDate(date) : ''; return ( - + {day} ); @@ -71,13 +82,13 @@ export default Day; const styles = StyleSheet.create({ dayContainer: { - width: 32, - height: 32 + width: DAY_SIZE, + height: DAY_SIZE }, selection: { position: 'absolute', - width: 24, - height: 24, - borderRadius: 12 + width: SELECTION_SIZE, + height: SELECTION_SIZE, + borderRadius: SELECTION_SIZE / 2 } }); diff --git a/src/incubator/Calendar/Header.tsx b/src/incubator/Calendar/Header.tsx index 748fc852fb..310ef456e6 100644 --- a/src/incubator/Calendar/Header.tsx +++ b/src/incubator/Calendar/Header.tsx @@ -4,28 +4,36 @@ import Reanimated, {useAnimatedProps} from 'react-native-reanimated'; import {Colors, Typography} from 'style'; import View from '../../components/view'; import Button from '../../components/button'; -import {getDateObject, addMonths, getMonthForIndex} from './helpers/DateUtils'; +import {getDateObject, getMonthForIndex, addMonths, getTimestamp} from './helpers/DateUtils'; import {HeaderProps, DayNamesFormat, UpdateSource} from './types'; import CalendarContext from './CalendarContext'; import WeekDaysNames from './WeekDaysNames'; -const AnimatedTextInput = Reanimated.createAnimatedComponent(TextInput); -const WEEK_NUMBER_WIDTH = 30; +const WEEK_NUMBER_WIDTH = 32; const ARROW_NEXT = require('./assets/arrowNext.png'); const ARROW_BACK = require('./assets/arrowBack.png'); +const AnimatedTextInput = Reanimated.createAnimatedComponent(TextInput); + const Header = (props: HeaderProps) => { const {month, year} = props; const {selectedDate, setDate, showWeeksNumbers, staticHeader, setHeaderHeight} = useContext(CalendarContext); + const getNewDate = useCallback((count: number) => { + const newDate = addMonths(selectedDate.value, count); + const dateObject = getDateObject(newDate); + return getTimestamp({year: dateObject.year, month: dateObject.month, day: 1}); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const onLeftArrowPress = useCallback(() => { - setDate(addMonths(selectedDate.value, -1), UpdateSource.MONTH_ARROW); - }, [selectedDate.value, setDate]); + setDate(getNewDate(-1), UpdateSource.MONTH_ARROW); + }, [setDate, getNewDate]); const onRightArrowPress = useCallback(() => { - setDate(addMonths(selectedDate.value, 1), UpdateSource.MONTH_ARROW); - }, [selectedDate.value, setDate]); + setDate(getNewDate(1), UpdateSource.MONTH_ARROW); + }, [setDate, getNewDate]); const animatedProps = useAnimatedProps(() => { const dateObject = getDateObject(selectedDate.value); @@ -38,7 +46,7 @@ const Header = (props: HeaderProps) => { const onLayout = useCallback((event: LayoutChangeEvent) => { setHeaderHeight?.(event.nativeEvent.layout.height); - }, []); + }, [setHeaderHeight]); const renderTitle = () => { // @ts-expect-error diff --git a/src/incubator/Calendar/Month.tsx b/src/incubator/Calendar/Month.tsx index 001a26450e..5e439a366c 100644 --- a/src/incubator/Calendar/Month.tsx +++ b/src/incubator/Calendar/Month.tsx @@ -5,18 +5,19 @@ import {MonthProps} from './types'; import Week from './Week'; import CalendarContext from './CalendarContext'; + function Month(props: MonthProps) { const {year, month} = props; const {firstDayOfWeek} = useContext(CalendarContext); const weekNumbers = useMemo(() => { return getWeekNumbersOfMonth(year, month, firstDayOfWeek); - }, [year, month]); + }, [year, month, firstDayOfWeek]); return ( {weekNumbers.map(weekNumber => { - return ; + return ; })} ); diff --git a/src/incubator/Calendar/Week.tsx b/src/incubator/Calendar/Week.tsx index f0f2cc2caf..26a9063818 100644 --- a/src/incubator/Calendar/Week.tsx +++ b/src/incubator/Calendar/Week.tsx @@ -1,9 +1,9 @@ import _ from 'lodash'; -import React, {useContext, useMemo} from 'react'; +import React, {useContext, useMemo, useCallback} from 'react'; import {StyleSheet} from 'react-native'; import View from '../../components/view'; import Text from '../../components/text'; -import {getDaysOfWeekNumber} from './helpers/DateUtils'; +import {getDaysOfWeekNumber, getDateObject} from './helpers/DateUtils'; import {WeekProps} from './types'; import CalendarContext from './CalendarContext'; import Day from './Day'; @@ -12,7 +12,7 @@ import Day from './Day'; const WEEK_NUMBER_WIDTH = 20; const Week = (props: WeekProps) => { - const {weekNumber, year} = props; + const {weekNumber, year, month} = props; const {firstDayOfWeek, showWeeksNumbers} = useContext(CalendarContext); @@ -26,11 +26,16 @@ const Week = (props: WeekProps) => { } }; + const isExtraDay = useCallback((day: number) => { + const dayMonth = getDateObject(day).month; + return dayMonth !== month; + }, [month]); + return ( {renderWeekNumbers()} {_.map(days, day => ( - + ))} ); diff --git a/src/incubator/Calendar/__tests__/DateUtils.spec.ts b/src/incubator/Calendar/__tests__/DateUtils.spec.ts index 2e3b556ead..aaf9f0144e 100644 --- a/src/incubator/Calendar/__tests__/DateUtils.spec.ts +++ b/src/incubator/Calendar/__tests__/DateUtils.spec.ts @@ -273,6 +273,24 @@ describe('Calendar/DateUtils', () => { }); }); + describe('addYears', () => { + it('should return the date timestamp for the next (1) year in the same month', () => { + const date = DateUtils.addYears(new Date(2022, 11, 26).getTime(), 1); + const dayObject = DateUtils.getDateObject(date); + expect(dayObject.day).toBe(26); + expect(dayObject.month).toBe(11); + expect(dayObject.year).toBe(2023); + }); + + it('should return the date timestamp for the previous (-1) year in the same month', () => { + const date = DateUtils.addYears(new Date(2022, 11, 26).getTime(), -1); + const dayObject = DateUtils.getDateObject(date); + expect(dayObject.day).toBe(26); + expect(dayObject.month).toBe(11); + expect(dayObject.year).toBe(2021); + }); + }); + describe('getWeekDayNames', () => { it('should return the week days names for first day = Sunday', () => { const weekDaysNames = DateUtils.getWeekDayNames(); diff --git a/src/incubator/Calendar/helpers/CalendarProcessor.ts b/src/incubator/Calendar/helpers/CalendarProcessor.ts index 2111a542ad..d6e8820cb3 100644 --- a/src/incubator/Calendar/helpers/CalendarProcessor.ts +++ b/src/incubator/Calendar/helpers/CalendarProcessor.ts @@ -1,9 +1,9 @@ -export function generateMonthItems(range: number) { - const today = new Date(); - const currentYear = today.getFullYear(); +export function generateMonthItems(date: number, pastRange: number, futureRange: number) { + const currentDate = new Date(date); + const currentYear = currentDate.getFullYear(); const monthItems = []; - for (let year = currentYear - range; year <= currentYear + range; year++) { + for (let year = currentYear - pastRange; year <= currentYear + futureRange; year++) { for (let month = 0; month < 12; month++) { monthItems.push({year, month}); } diff --git a/src/incubator/Calendar/helpers/DateUtils.ts b/src/incubator/Calendar/helpers/DateUtils.ts index 54ace9d738..a3b3ce0821 100644 --- a/src/incubator/Calendar/helpers/DateUtils.ts +++ b/src/incubator/Calendar/helpers/DateUtils.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; import getWeek from 'date-fns/getWeek'; -import {FirstDayOfWeek, DayNamesFormat, DateObjectWithOptionalDay} from '../types'; +import {FirstDayOfWeek, DayNamesFormat, DateObjectWithOptionalDay, DateObject} from '../types'; export const HOUR_IN_MS = 60 * 60 * 1000; const DAY_IN_MS = 24 * HOUR_IN_MS; @@ -20,7 +20,7 @@ export function getWeekNumbersOfMonth(year: number, month: number, firstDayOfWee return weekNumbers; } -function getNumberOfWeeksInMonth(year: number, month: number, firstDayOfWeek: FirstDayOfWeek) { +export function getNumberOfWeeksInMonth(year: number, month: number, firstDayOfWeek: FirstDayOfWeek) { const numberOfDaysInMonth = new Date(year, month + 1, 0).getDate(); const dayOfTheWeek = new Date(year, month, 1).getDay(); // Modify day in the week based on the first day of the week @@ -78,6 +78,11 @@ export function getDateObject(date: number) { }; } +export function getTimestamp(date: DateObject) { + 'worklet'; + return new Date(date.year, date.month, date.day).getTime(); +} + export function addMonths(date: number, count: number) { 'worklet'; if (count === 0) { @@ -88,6 +93,16 @@ export function addMonths(date: number, count: number) { return new Date(date).setMonth(month + count); } +export function addYears(date: number, count: number) { + 'worklet'; + if (count === 0) { + return date; + } + + const year = getDateObject(date).year; + return new Date(date).setFullYear(year + count); +} + export function getMonthForIndex(index: number) { 'worklet'; const months = [ diff --git a/src/incubator/Calendar/index.tsx b/src/incubator/Calendar/index.tsx index c39cf7ff37..36577997a6 100644 --- a/src/incubator/Calendar/index.tsx +++ b/src/incubator/Calendar/index.tsx @@ -1,33 +1,52 @@ -import findIndex from 'lodash/findIndex'; -import React, {PropsWithChildren, useCallback, useMemo, useRef, useEffect} from 'react'; +import React, {PropsWithChildren, useCallback, useMemo, useRef, useState} from 'react'; import {useSharedValue, useAnimatedReaction, runOnJS} from 'react-native-reanimated'; import {FlashListPackage} from 'optionalDeps'; import {Constants} from '../../commons/new'; import {generateMonthItems} from './helpers/CalendarProcessor'; import {addHeaders} from './helpers/DataProcessor'; -import {isSameMonth} from './helpers/DateUtils'; -import {CalendarContextProps, CalendarProps, FirstDayOfWeek, UpdateSource} from './types'; +import {isSameMonth, getTimestamp, addYears} from './helpers/DateUtils'; +import {CalendarContextProps, CalendarProps, FirstDayOfWeek, UpdateSource, DateObjectWithOptionalDay} from './types'; import CalendarContext from './CalendarContext'; import CalendarItem from './CalendarItem'; import Agenda from './Agenda'; import TodayButton from './TodayButton'; import Header from './Header'; +import {useDidUpdate} from 'hooks'; const FlashList = FlashListPackage?.FlashList; -// TODO: Move this logic elsewhere to pre-generate on install? -const MONTH_ITEMS = generateMonthItems(2); -const getIndex = (date: number) => { - return findIndex(MONTH_ITEMS, item => isSameMonth(item, date)); -}; +const VIEWABILITY_CONFIG = {itemVisiblePercentThreshold: 95, minimumViewTime: 200}; +const YEARS_RANGE = 1; +const PAGE_RELOAD_THRESHOLD = 3; +const NOW = Date.now(); // so the 'initialDate' effect won't get called since the now different on every rerender function Calendar(props: PropsWithChildren) { - const {data, children, initialDate = Date.now(), firstDayOfWeek = FirstDayOfWeek.MONDAY, staticHeader = false} = props; + const { + data, + children, + initialDate = NOW, + onChangeDate, + firstDayOfWeek = FirstDayOfWeek.MONDAY, + staticHeader = false, + showExtraDays = true + } = props; + + const initialItems = generateMonthItems(initialDate, YEARS_RANGE, YEARS_RANGE); + const [items, setItems] = useState(initialItems); + + const getItemIndex = useCallback((date: number) => { + 'worklet'; + for (let i = 0; i < items.length; i++) { + if (isSameMonth(items[i], date)) { + return i; + } + } + return -1; + }, [items]); const flashListRef = useRef(); - const calendarWidth = Constants.screenWidth; const current = useSharedValue(initialDate); - const initialMonthIndex = useRef(getIndex(current.value)); + const initialMonthIndex = useRef(getItemIndex(current.value)); const lastUpdateSource = useSharedValue(UpdateSource.INIT); const processedData = useMemo(() => addHeaders(data), [data]); const scrolledByUser = useSharedValue(false); @@ -36,15 +55,31 @@ function Calendar(props: PropsWithChildren) { const setDate = useCallback((date: number, updateSource: UpdateSource) => { current.value = date; lastUpdateSource.value = updateSource; + if (updateSource !== UpdateSource.PROP_UPDATE) { + onChangeDate?.(date); + } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useEffect(() => { + const scrollToIndex = useCallback((index: number) => { + scrolledByUser.value = false; + // @ts-expect-error + flashListRef.current?.scrollToIndex({index, animated: true}); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [getItemIndex]); + + useDidUpdate(() => { setDate(initialDate, UpdateSource.PROP_UPDATE); - }, [initialDate, setDate]); + }, [initialDate]); + + useDidUpdate(() => { + const index = getItemIndex(current.value); + scrollToIndex(index); + }, [items, getItemIndex]); const setHeaderHeight = useCallback((height: number) => { headerHeight.value = height; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const contextValue = useMemo(() => { @@ -54,39 +89,69 @@ function Calendar(props: PropsWithChildren) { selectedDate: current, setDate, showWeeksNumbers: true, + showExtraDays, updateSource: lastUpdateSource, staticHeader, setHeaderHeight, headerHeight }; - }, []); + }, [processedData]); - const scrollToIndex = useCallback((date: number) => { - scrolledByUser.value = false; - // @ts-expect-error - flashListRef.current?.scrollToIndex({index: getIndex(date), animated: false}); + /** Pages reload */ + + const mergeArrays = (prepend: boolean, array: DateObjectWithOptionalDay[], newArray: DateObjectWithOptionalDay[]) => { + const arr: DateObjectWithOptionalDay[] = array.slice(); + if (prepend) { + arr.unshift(...newArray); + } else { + arr.push(...newArray); + } + return arr; + }; + + const addPages = useCallback((index: number) => { + const prepend = index < PAGE_RELOAD_THRESHOLD; + const append = index > items.length - PAGE_RELOAD_THRESHOLD; + const pastRange = prepend ? YEARS_RANGE : 0; + const futureRange = append ? YEARS_RANGE : 0; + const newDate = addYears(current.value, prepend ? -1 : 1); + const newItems = generateMonthItems(newDate, pastRange, futureRange); + const newArray = mergeArrays(prepend, items, newItems); + setItems(newArray); // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [items]); + + const shouldAddPages = useCallback((index: number) => { + 'worklet'; + return index !== -1 && + (index < PAGE_RELOAD_THRESHOLD || index > items.length - PAGE_RELOAD_THRESHOLD); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [items]); useAnimatedReaction(() => { return current.value; }, (selected, previous) => { - if (lastUpdateSource.value !== UpdateSource.MONTH_SCROLL) { + const index = getItemIndex(selected); + + if (shouldAddPages(index)) { + runOnJS(addPages)(index); + } else if (lastUpdateSource.value !== UpdateSource.MONTH_SCROLL) { if (previous && !isSameMonth(selected, previous)) { - runOnJS(scrollToIndex)(selected); + runOnJS(scrollToIndex)(index); } } - }, []); + }, [getItemIndex]); const onViewableItemsChanged = useCallback(({viewableItems}: any) => { - if (scrolledByUser.value) { - const item = viewableItems?.[0]?.item; - if (item && !isSameMonth(item, current.value)) { - const newDate = new Date(item.year, item.month, 1); - setDate(newDate.getTime(), UpdateSource.MONTH_SCROLL); + const item = viewableItems?.[0]?.item; + if (item && scrolledByUser.value) { + if (!isSameMonth(item, current.value)) { + const newDate = getTimestamp({year: item.year, month: item.month, day: 1}); + setDate(newDate, UpdateSource.MONTH_SCROLL); } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const onMomentumScrollBegin = useCallback(() => { @@ -108,8 +173,8 @@ function Calendar(props: PropsWithChildren) { {staticHeader &&
} ) { showsHorizontalScrollIndicator={false} // TODO: Consider moving this shared logic with Agenda to a hook onViewableItemsChanged={onViewableItemsChanged} + viewabilityConfig={VIEWABILITY_CONFIG} onMomentumScrollBegin={onMomentumScrollBegin} onScrollBeginDrag={onScrollBeginDrag} /> diff --git a/src/incubator/Calendar/types.ts b/src/incubator/Calendar/types.ts index c1d8ec75f1..9afac5d788 100644 --- a/src/incubator/Calendar/types.ts +++ b/src/incubator/Calendar/types.ts @@ -55,7 +55,8 @@ export interface CalendarContextProps { setDate: (date: number, updateSource: UpdateSource) => void; data: InternalData; showWeeksNumbers: boolean; - updateSource?: SharedValue; + showExtraDays: boolean; + updateSource: SharedValue; staticHeader?: boolean; setHeaderHeight?: (height: number) => void; headerHeight: SharedValue; @@ -64,11 +65,13 @@ export interface CalendarContextProps { export interface DayProps { date: number | null; onPress?: (date: number) => void; + inactive?: boolean; // inactive look but still pressable } export interface WeekProps { weekNumber: number; year: number; + month: number; } export interface MonthProps { @@ -107,12 +110,16 @@ export enum DayNamesFormat { export interface CalendarProps { data: Data; initialDate?: number; + onChangeDate: (date: number) => void; firstDayOfWeek?: /* `${FirstDayOfWeek}` & */ FirstDayOfWeek; // NOTE: template literals usage depends on ts min version ^4.3.2 staticHeader?: boolean; + showExtraDays?: boolean; } -// export interface AgendaProps { -// // Type: list(events)/timeline -// // layout: -// // scrollTo(date) -// } +export interface AgendaProps { + showLoader?: boolean; + onEndReached?: (date: number) => void; + // Type: list(events)/timeline + // layout: + // scrollTo(date) +} From 46363383277f47fb1d8da686b951d274480bf7a3 Mon Sep 17 00:00:00 2001 From: Miki Leib <38354019+M-i-k-e-l@users.noreply.github.com> Date: Thu, 20 Apr 2023 11:51:22 +0300 Subject: [PATCH 10/35] SortableList - newHeight in tests (#2571) --- src/components/sortableList/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/sortableList/index.tsx b/src/components/sortableList/index.tsx index 0e6690d3df..3f6c4397df 100644 --- a/src/components/sortableList/index.tsx +++ b/src/components/sortableList/index.tsx @@ -47,7 +47,10 @@ const SortableList = (props: SortableListPr const onItemLayout = useCallback((event: LayoutChangeEvent) => { // Round height for Android const newHeight = Math.round(event.nativeEvent.layout.height); - itemHeight.value = newHeight; + // Check validity for tests + if (newHeight) { + itemHeight.value = newHeight; + } }, []); const context = useMemo(() => { From a0060e448dd6cd2711c1e518b19842479ac25d6c Mon Sep 17 00:00:00 2001 From: Inbal Tish Date: Sun, 23 Apr 2023 11:32:21 +0300 Subject: [PATCH 11/35] Picker - remove commented out code related to 'renderNativePicker' deprecated prop --- src/components/picker/NativePicker.js | 6 ++---- src/components/picker/types.tsx | 4 ---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/components/picker/NativePicker.js b/src/components/picker/NativePicker.js index fd3aa2b618..a2465eb360 100644 --- a/src/components/picker/NativePicker.js +++ b/src/components/picker/NativePicker.js @@ -65,10 +65,8 @@ class NativePicker extends Component { renderPicker = () => { const {selectedValue} = this.state; - const {children, /* renderNativePicker, */ pickerStyle, wheelPickerProps, testID} = this.props; - // if (_.isFunction(renderNativePicker)) { - // return renderNativePicker(this.props); - // } + const {children, pickerStyle, wheelPickerProps, testID} = this.props; + return ( & * Use wheel picker instead of a list picker */ useWheelPicker?: boolean; - // /** - // * Callback for rendering a custom native picker inside the dialog (relevant to native picker only) - // */ - // renderNativePicker?: () => React.ReactElement; /** * Pass props to the list component that wraps the picker options (allows to control FlatList behavior) */ From 1bf95487618d9d67f6d1c12e58ed8ee6bc7837fc Mon Sep 17 00:00:00 2001 From: Miki Leib <38354019+M-i-k-e-l@users.noreply.github.com> Date: Sun, 23 Apr 2023 19:10:32 +0300 Subject: [PATCH 12/35] Incubator.Dialog - fix RN Modal native calls on RN71 (#2573) --- src/incubator/Dialog/__tests__/index.spec.tsx | 3 ++- src/incubator/Dialog/index.tsx | 10 +++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/incubator/Dialog/__tests__/index.spec.tsx b/src/incubator/Dialog/__tests__/index.spec.tsx index 5482deea8f..c8191d9fd4 100644 --- a/src/incubator/Dialog/__tests__/index.spec.tsx +++ b/src/incubator/Dialog/__tests__/index.spec.tsx @@ -54,6 +54,7 @@ describe('Incubator.Dialog', () => { const closeButtonDriver = new ButtonDriver({component, testID: 'closeButton'}); await closeButtonDriver.press(); expect(await dialogDriver.exists()).toBeFalsy(); - expect(onDismiss).toHaveBeenCalledTimes(1); + // TODO: + // expect(onDismiss).toHaveBeenCalledTimes(1); }); }); diff --git a/src/incubator/Dialog/index.tsx b/src/incubator/Dialog/index.tsx index 3aa15b2667..6ded94f4e3 100644 --- a/src/incubator/Dialog/index.tsx +++ b/src/incubator/Dialog/index.tsx @@ -18,7 +18,7 @@ import { } from 'react-native-gesture-handler'; import {Spacings, Colors, BorderRadiuses} from '../../style'; import {useDidUpdate} from '../../hooks'; -import {asBaseComponent} from '../../commons/new'; +import {asBaseComponent, Constants} from '../../commons/new'; import View from '../../components/view'; import Modal from '../../components/modal'; import {extractAlignmentsValues} from '../../commons/modifiers'; @@ -113,7 +113,7 @@ const Dialog = (props: DialogProps, ref: ForwardedRef) if (wasMeasured) { if (modalVisibility) { open(); - } else { + } else if (Constants.isAndroid) { onDismiss?.(); } } @@ -217,10 +217,6 @@ const Dialog = (props: DialogProps, ref: ForwardedRef) ); - if (!modalVisibility) { - return null; - } - return ( ) visible={modalVisibility} onBackgroundPress={ignoreBackgroundPress ? undefined : close} onRequestClose={ignoreBackgroundPress ? undefined : close} - onDismiss={undefined} + onDismiss={onDismiss} > {renderOverlayView()} From 8055593647fede88f9a3977d7b7dcb2b00aaec8a Mon Sep 17 00:00:00 2001 From: Adi Mordo Date: Thu, 27 Apr 2023 07:48:45 +0300 Subject: [PATCH 13/35] CharCounter now working with emojis (16-bit code) (#2570) * CharCounter now working with emojis (16-bit code) * Fixed review notes --- src/incubator/TextField/CharCounter.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/incubator/TextField/CharCounter.tsx b/src/incubator/TextField/CharCounter.tsx index c226940f07..20d70fad3e 100644 --- a/src/incubator/TextField/CharCounter.tsx +++ b/src/incubator/TextField/CharCounter.tsx @@ -7,13 +7,14 @@ import {CharCounterProps} from './types'; const CharCounter = ({maxLength, charCounterStyle, testID}: CharCounterProps) => { const {value} = useContext(FieldContext); + const length = value?.length ?? 0; if (_.isUndefined(maxLength)) { return null; } return ( - {`${_.size(value)}/${maxLength}`} + {`${length}/${maxLength}`} ); }; From 07f6077284e68c5e91378fa6f09bb42729f1ca07 Mon Sep 17 00:00:00 2001 From: Inbal Tish Date: Thu, 27 Apr 2023 08:39:22 +0300 Subject: [PATCH 14/35] SortableList - adding important note --- src/components/sortableList/types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/sortableList/types.ts b/src/components/sortableList/types.ts index 170860ff88..d489276d5a 100644 --- a/src/components/sortableList/types.ts +++ b/src/components/sortableList/types.ts @@ -13,7 +13,8 @@ export interface SortableListProps extends Omit, 'extraData' | 'data'>, Pick, 'scale'> { /** - * The data of the list, do not update the data. + * The data of the list. + IMPORTANT: Do not update 'data' in 'onOrderChange' (i.e. for each order change); only update it when you change the items (i.g. adding and removing an item). */ data: Data; /** From da05ccbc8040fd9ac1163f625097524c07e4bbbb Mon Sep 17 00:00:00 2001 From: Adi Mordo Date: Mon, 1 May 2023 16:36:19 +0300 Subject: [PATCH 15/35] fix wheelpicker in android (#2558) * fix wheelpicker in android * Removed initialNumToRender, removed style * removed itemLength variable * opt-out * added flatListProps to wheelPicker.api * added note to the api.json file * refactor the nore * fixed note * minor fix --- src/components/WheelPicker/index.tsx | 8 ++++++++ src/components/WheelPicker/wheelPicker.api.json | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/components/WheelPicker/index.tsx b/src/components/WheelPicker/index.tsx index 7c63fe7819..014ea40036 100644 --- a/src/components/WheelPicker/index.tsx +++ b/src/components/WheelPicker/index.tsx @@ -146,6 +146,13 @@ const WheelPicker = ({ const prevIndex = useRef(currentIndex); const [flatListWidth, setFlatListWidth] = useState(0); const keyExtractor = useCallback((item: ItemProps, index: number) => `${item}.${index}`, []); + const androidFlatListProps = useMemo(() => { + if (Constants.isAndroid) { + return { + maxToRenderPerBatch: items.length + }; + } + }, [items]); useEffect(() => { // This effect making sure to reset index if initialValue has changed @@ -323,6 +330,7 @@ const WheelPicker = ({ maxToRenderPerBatch prop set to items.length to solve FlatList bug on Android, you can override it by passing your own flatListProps with maxToRenderPerBatch prop.
See the RN FlatList issue for more info: https://github.com/facebook/react-native/issues/15990", "example": "https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/WheelPickerScreen.tsx", "images": [], "props": [ @@ -33,7 +34,12 @@ "default": "center" }, {"name": "separatorsStyle", "type": "ViewStyle", "description": "Extra style for the separators"}, - {"name": "testID", "type": "string", "description": "test identifier"} + {"name": "testID", "type": "string", "description": "test identifier"}, + { + "name": "flatListProps", + "type": "FlatListProps", + "description": "Props to be sent to the FlatList." + } ], "snippet": [ " Date: Tue, 2 May 2023 09:39:19 +0300 Subject: [PATCH 16/35] Align LogService with console (#2577) Allow sending multiple params and allow (any) objects --- src/services/LogService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/LogService.ts b/src/services/LogService.ts index c4974edb69..ca64d87fc9 100644 --- a/src/services/LogService.ts +++ b/src/services/LogService.ts @@ -12,15 +12,15 @@ class LogService { this.biLogger?.log(event); }; - warn = (message: string) => { + warn = (message?: any, ...optionalParams: any[]) => { if (__DEV__) { - console.warn(message); + console.warn(message, ...optionalParams); } }; - error = (message: string) => { + error = (message?: any, ...optionalParams: any[]) => { if (__DEV__) { - console.error(message); + console.error(message, ...optionalParams); } }; From 89073864176b1dedc4c9d736622118eca40dc8ca Mon Sep 17 00:00:00 2001 From: Miki Leib <38354019+M-i-k-e-l@users.noreply.github.com> Date: Wed, 3 May 2023 10:24:12 +0300 Subject: [PATCH 17/35] Incubator.Dialog - fix test after RN71 bug fix (#2575) * Incubator.Dialog - fix test after RN71 bug fix * Forgot this --- .../GestureDetectorMock.tsx | 0 jest-setup.js => jestSetup/jest-setup.js | 14 ++++++++++++++ jestSetup/useDidUpdate.ts | 18 ++++++++++++++++++ package.json | 2 +- src/incubator/Dialog/__tests__/index.spec.tsx | 3 +-- 5 files changed, 34 insertions(+), 3 deletions(-) rename GestureDetectorMock.tsx => jestSetup/GestureDetectorMock.tsx (100%) rename jest-setup.js => jestSetup/jest-setup.js (90%) create mode 100644 jestSetup/useDidUpdate.ts diff --git a/GestureDetectorMock.tsx b/jestSetup/GestureDetectorMock.tsx similarity index 100% rename from GestureDetectorMock.tsx rename to jestSetup/GestureDetectorMock.tsx diff --git a/jest-setup.js b/jestSetup/jest-setup.js similarity index 90% rename from jest-setup.js rename to jestSetup/jest-setup.js index 9b230cc5f1..0da842324c 100644 --- a/jest-setup.js +++ b/jestSetup/jest-setup.js @@ -97,6 +97,20 @@ jest.mock('react-native-gesture-handler', jest.mock('react-native', () => { const reactNative = jest.requireActual('react-native'); reactNative.NativeModules.KeyboardTrackingViewTempManager = {}; + const OriginalModal = reactNative.Modal; + const React = jest.requireActual('react'); + const useDidUpdate = require('./useDidUpdate').default; + Object.defineProperty(reactNative, 'Modal', { + value: props => { + // eslint-disable-next-line react-hooks/rules-of-hooks + useDidUpdate(() => { + if (!props.visible) { + props.onDismiss?.(); + } + }, [props.visible]); + return ; + } + }); return reactNative; }); diff --git a/jestSetup/useDidUpdate.ts b/jestSetup/useDidUpdate.ts new file mode 100644 index 0000000000..654a79a9b2 --- /dev/null +++ b/jestSetup/useDidUpdate.ts @@ -0,0 +1,18 @@ +import {useEffect, useRef, DependencyList} from 'react'; + +/** + * This hook avoid calling useEffect on the initial value of his dependency array + */ +const useDidUpdate = (callback: () => void, dep: DependencyList) => { + const isMounted = useRef(false); + + useEffect(() => { + if (isMounted.current) { + callback(); + } else { + isMounted.current = true; + } + }, dep); +}; + +export default useDidUpdate; diff --git a/package.json b/package.json index 5c37a90b5b..771c480ed3 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "/uilib-docs/" ], "setupFiles": [ - "./jest-setup.js" + "./jestSetup/jest-setup.js" ], "testMatch": [ "**/*.spec.(js|ts|tsx)" diff --git a/src/incubator/Dialog/__tests__/index.spec.tsx b/src/incubator/Dialog/__tests__/index.spec.tsx index c8191d9fd4..5482deea8f 100644 --- a/src/incubator/Dialog/__tests__/index.spec.tsx +++ b/src/incubator/Dialog/__tests__/index.spec.tsx @@ -54,7 +54,6 @@ describe('Incubator.Dialog', () => { const closeButtonDriver = new ButtonDriver({component, testID: 'closeButton'}); await closeButtonDriver.press(); expect(await dialogDriver.exists()).toBeFalsy(); - // TODO: - // expect(onDismiss).toHaveBeenCalledTimes(1); + expect(onDismiss).toHaveBeenCalledTimes(1); }); }); From 159348917cfa30ca5b16195357a73b81684c3163 Mon Sep 17 00:00:00 2001 From: Inbal Tish <33805983+Inbal-Tish@users.noreply.github.com> Date: Mon, 8 May 2023 11:43:17 +0300 Subject: [PATCH 18/35] Replacing 'react-native-text-size' with wix fork (#2542) * Replacing 'react-native-text-size' with wix fork * fix version --- ios/rnuilib.xcodeproj/project.pbxproj | 164 +++++++++++++------------- package.json | 2 +- src/style/typography.ts | 2 +- 3 files changed, 84 insertions(+), 84 deletions(-) diff --git a/ios/rnuilib.xcodeproj/project.pbxproj b/ios/rnuilib.xcodeproj/project.pbxproj index db28547d76..e6e1ccc526 100644 --- a/ios/rnuilib.xcodeproj/project.pbxproj +++ b/ios/rnuilib.xcodeproj/project.pbxproj @@ -8,17 +8,17 @@ /* Begin PBXBuildFile section */ 00E356F31AD99517003FC87E /* rnuilibTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* rnuilibTests.m */; }; - 0A1C60508FA9D9CBB0208734 /* libPods-rnuilib-rnuilibTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A8E874090C50DE6EC77F7F72 /* libPods-rnuilib-rnuilibTests.a */; }; 10A864C1285B5CB00011FF03 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 10A864C0285B5CB00011FF03 /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 2DCD954D1E0B4F2C00145EB5 /* rnuilibTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* rnuilibTests.m */; }; + 56EBC4A1F72808C5B51F7BBF /* libPods-rnuilib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C5768CF5EB4CEC5AF24695D /* libPods-rnuilib.a */; }; 8E52CBDF2887DD21009D5EC5 /* DesignTokens.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8E52CBDE2887DD21009D5EC5 /* DesignTokens.xcassets */; }; 8E8B0D662744D9CD0026B520 /* void.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E8B0D652744D9CD0026B520 /* void.swift */; }; 8EA1FC8C2519E7F7008B4B36 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8EA1FC8B2519E7F7008B4B36 /* LaunchScreen.storyboard */; }; - AF7086C5E11D38CFBEB20C79 /* libPods-rnuilib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3637F09D204DA39F84CE0A /* libPods-rnuilib.a */; }; + C24F706C923507D9D0AFAF26 /* libPods-rnuilib-rnuilibTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB858F3A7230EAA142D23DA1 /* libPods-rnuilib-rnuilibTests.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -49,19 +49,19 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = rnuilib/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = rnuilib/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = rnuilib/main.m; sourceTree = ""; }; - 1A3637F09D204DA39F84CE0A /* libPods-rnuilib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-rnuilib.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 2D02E47B1E0B4A5D006451C7 /* rnuilib-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "rnuilib-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 2D02E4901E0B4A5D006451C7 /* rnuilib-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "rnuilib-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 3D3E2E98DFD30898C81A5293 /* Pods-rnuilib-rnuilibTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnuilib-rnuilibTests.release.xcconfig"; path = "Target Support Files/Pods-rnuilib-rnuilibTests/Pods-rnuilib-rnuilibTests.release.xcconfig"; sourceTree = ""; }; - 4F15E4C4D4931613EAD48585 /* Pods-rnuilib.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnuilib.release.xcconfig"; path = "Target Support Files/Pods-rnuilib/Pods-rnuilib.release.xcconfig"; sourceTree = ""; }; - 666D1B6A60C23741B20A5051 /* Pods-rnuilib-rnuilibTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnuilib-rnuilibTests.debug.xcconfig"; path = "Target Support Files/Pods-rnuilib-rnuilibTests/Pods-rnuilib-rnuilibTests.debug.xcconfig"; sourceTree = ""; }; - 71C796B95A6CB852F0146DEF /* Pods-rnuilib.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnuilib.debug.xcconfig"; path = "Target Support Files/Pods-rnuilib/Pods-rnuilib.debug.xcconfig"; sourceTree = ""; }; + 56523CB09C6A79FEBA0C210A /* Pods-rnuilib-rnuilibTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnuilib-rnuilibTests.release.xcconfig"; path = "Target Support Files/Pods-rnuilib-rnuilibTests/Pods-rnuilib-rnuilibTests.release.xcconfig"; sourceTree = ""; }; + 65B766BC0A8D1F830F3D2004 /* Pods-rnuilib.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnuilib.debug.xcconfig"; path = "Target Support Files/Pods-rnuilib/Pods-rnuilib.debug.xcconfig"; sourceTree = ""; }; + 66AD894C4A02A884861A27E2 /* Pods-rnuilib.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnuilib.release.xcconfig"; path = "Target Support Files/Pods-rnuilib/Pods-rnuilib.release.xcconfig"; sourceTree = ""; }; + 6C5768CF5EB4CEC5AF24695D /* libPods-rnuilib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-rnuilib.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 86A4AD8C011363501515A0EC /* Pods-rnuilib-rnuilibTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnuilib-rnuilibTests.debug.xcconfig"; path = "Target Support Files/Pods-rnuilib-rnuilibTests/Pods-rnuilib-rnuilibTests.debug.xcconfig"; sourceTree = ""; }; 8E52CBDE2887DD21009D5EC5 /* DesignTokens.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DesignTokens.xcassets; sourceTree = ""; }; 8E8B0D652744D9CD0026B520 /* void.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = void.swift; sourceTree = ""; }; 8EA1FC8B2519E7F7008B4B36 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = rnuilib/LaunchScreen.storyboard; sourceTree = ""; }; - A8E874090C50DE6EC77F7F72 /* libPods-rnuilib-rnuilibTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-rnuilib-rnuilibTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; + FB858F3A7230EAA142D23DA1 /* libPods-rnuilib-rnuilibTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-rnuilib-rnuilibTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -69,7 +69,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0A1C60508FA9D9CBB0208734 /* libPods-rnuilib-rnuilibTests.a in Frameworks */, + C24F706C923507D9D0AFAF26 /* libPods-rnuilib-rnuilibTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -77,7 +77,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - AF7086C5E11D38CFBEB20C79 /* libPods-rnuilib.a in Frameworks */, + 56EBC4A1F72808C5B51F7BBF /* libPods-rnuilib.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -136,8 +136,8 @@ children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED2971642150620600B7C4FE /* JavaScriptCore.framework */, - 1A3637F09D204DA39F84CE0A /* libPods-rnuilib.a */, - A8E874090C50DE6EC77F7F72 /* libPods-rnuilib-rnuilibTests.a */, + 6C5768CF5EB4CEC5AF24695D /* libPods-rnuilib.a */, + FB858F3A7230EAA142D23DA1 /* libPods-rnuilib-rnuilibTests.a */, ); name = Frameworks; sourceTree = ""; @@ -178,10 +178,10 @@ D99C980AB24A05601E0007F9 /* Pods */ = { isa = PBXGroup; children = ( - 71C796B95A6CB852F0146DEF /* Pods-rnuilib.debug.xcconfig */, - 4F15E4C4D4931613EAD48585 /* Pods-rnuilib.release.xcconfig */, - 666D1B6A60C23741B20A5051 /* Pods-rnuilib-rnuilibTests.debug.xcconfig */, - 3D3E2E98DFD30898C81A5293 /* Pods-rnuilib-rnuilibTests.release.xcconfig */, + 65B766BC0A8D1F830F3D2004 /* Pods-rnuilib.debug.xcconfig */, + 66AD894C4A02A884861A27E2 /* Pods-rnuilib.release.xcconfig */, + 86A4AD8C011363501515A0EC /* Pods-rnuilib-rnuilibTests.debug.xcconfig */, + 56523CB09C6A79FEBA0C210A /* Pods-rnuilib-rnuilibTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -193,12 +193,12 @@ isa = PBXNativeTarget; buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "rnuilibTests" */; buildPhases = ( - 03ACEC70B300DF14A2726B96 /* [CP] Check Pods Manifest.lock */, + 72FA426D5675971732169A33 /* [CP] Check Pods Manifest.lock */, 00E356EA1AD99517003FC87E /* Sources */, 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, - F68A06EBFB89A15C94C6B5FF /* [CP] Copy Pods Resources */, - 2403068A2103B3AD40F9CB77 /* [CP] Embed Pods Frameworks */, + 4A2DFE2E4F276607CC9422BA /* [CP] Embed Pods Frameworks */, + 05F5C3E9528158446F3788A5 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -214,14 +214,14 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "rnuilib" */; buildPhases = ( - D7E6439E4D8846E5E7EBBA81 /* [CP] Check Pods Manifest.lock */, + 5C139D0477DFF8903C0C457A /* [CP] Check Pods Manifest.lock */, FD10A7F022414F080027D42C /* Start Packager */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 6A01744B3971F15EA74E9EC1 /* [CP] Copy Pods Resources */, - B501C960470EF44E6FDFF61C /* [CP] Embed Pods Frameworks */, + A077BED00DEE8BB3D2E7978D /* [CP] Embed Pods Frameworks */, + 74E5355D055C34254F806DF4 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -368,29 +368,39 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; - 03ACEC70B300DF14A2726B96 /* [CP] Check Pods Manifest.lock */ = { + 05F5C3E9528158446F3788A5 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-rnuilib-rnuilibTests/Pods-rnuilib-rnuilibTests-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", ); + name = "[CP] Copy Pods Resources"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-rnuilib-rnuilibTests-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-rnuilib-rnuilibTests/Pods-rnuilib-rnuilibTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 2403068A2103B3AD40F9CB77 /* [CP] Embed Pods Frameworks */ = { + 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Bundle React Native Code And Images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; + }; + 4A2DFE2E4F276607CC9422BA /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -414,21 +424,51 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-rnuilib-rnuilibTests/Pods-rnuilib-rnuilibTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { + 5C139D0477DFF8903C0C457A /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "Bundle React Native Code And Images"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-rnuilib-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 72FA426D5675971732169A33 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-rnuilib-rnuilibTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - 6A01744B3971F15EA74E9EC1 /* [CP] Copy Pods Resources */ = { + 74E5355D055C34254F806DF4 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -446,7 +486,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-rnuilib/Pods-rnuilib-resources.sh\"\n"; showEnvVarsInLog = 0; }; - B501C960470EF44E6FDFF61C /* [CP] Embed Pods Frameworks */ = { + A077BED00DEE8BB3D2E7978D /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -470,46 +510,6 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-rnuilib/Pods-rnuilib-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - D7E6439E4D8846E5E7EBBA81 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-rnuilib-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - F68A06EBFB89A15C94C6B5FF /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-rnuilib-rnuilibTests/Pods-rnuilib-rnuilibTests-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-rnuilib-rnuilibTests/Pods-rnuilib-rnuilibTests-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; FD10A7F022414F080027D42C /* Start Packager */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -603,7 +603,7 @@ /* Begin XCBuildConfiguration section */ 00E356F61AD99517003FC87E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 666D1B6A60C23741B20A5051 /* Pods-rnuilib-rnuilibTests.debug.xcconfig */; + baseConfigurationReference = 86A4AD8C011363501515A0EC /* Pods-rnuilib-rnuilibTests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -627,7 +627,7 @@ }; 00E356F71AD99517003FC87E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3D3E2E98DFD30898C81A5293 /* Pods-rnuilib-rnuilibTests.release.xcconfig */; + baseConfigurationReference = 56523CB09C6A79FEBA0C210A /* Pods-rnuilib-rnuilibTests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; @@ -649,7 +649,7 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 71C796B95A6CB852F0146DEF /* Pods-rnuilib.debug.xcconfig */; + baseConfigurationReference = 65B766BC0A8D1F830F3D2004 /* Pods-rnuilib.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -678,7 +678,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4F15E4C4D4931613EAD48585 /* Pods-rnuilib.release.xcconfig */; + baseConfigurationReference = 66AD894C4A02A884861A27E2 /* Pods-rnuilib.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/package.json b/package.json index 771c480ed3..a93e6d75a6 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "prop-types": "^15.5.10", "react-freeze": "^1.0.0", "react-native-redash": "^12.0.3", - "react-native-text-size": "4.0.0-rc.1", + "wix-react-native-text-size": "~1.0.0", "semver": "^5.5.0", "tinycolor2": "^1.4.2", "url-parse": "^1.2.0" diff --git a/src/style/typography.ts b/src/style/typography.ts index c4e6ccb7ff..2cb72afcbb 100644 --- a/src/style/typography.ts +++ b/src/style/typography.ts @@ -45,7 +45,7 @@ export class Typography { async measureTextSize(text: string, typography: MeasureTextTypography = TypographyPresets.text70!, containerWidth = Constants.screenWidth) { - const rnTextSize = require('react-native-text-size').default; + const rnTextSize = require('wix-react-native-text-size').default; if (text) { const size = await rnTextSize.measure({ text, // text to measure, can include symbols From 913855e9eb9999b419c8654216139aef1c294c88 Mon Sep 17 00:00:00 2001 From: Ethan Sharabi <1780255+ethanshar@users.noreply.github.com> Date: Tue, 9 May 2023 15:32:54 +0300 Subject: [PATCH 19/35] Enable TS check on demo files and fix all errors (#2585) * Enable TS check on demo files and fix all errors * Code review fixes * Add space - test commit * Remove space --- demo/src/screens/PlaygroundScreen.tsx | 2 +- .../componentScreens/ActionBarScreen.tsx | 10 +- .../componentScreens/AvatarsScreen.tsx | 12 +-- .../componentScreens/BasicListScreen.tsx | 1 - .../componentScreens/ButtonsScreen.tsx | 24 +++-- .../componentScreens/CarouselScreen.tsx | 2 +- .../componentScreens/ChipsInputScreen.tsx | 2 +- .../componentScreens/ColorPickerScreen.tsx | 2 +- .../screens/componentScreens/DrawerScreen.tsx | 95 ++++++++----------- .../componentScreens/GridViewScreen.tsx | 6 +- .../screens/componentScreens/HintsScreen.tsx | 4 +- .../screens/componentScreens/ModalScreen.tsx | 2 +- .../componentScreens/OverlaysScreen.tsx | 4 +- .../componentScreens/PanDismissibleScreen.tsx | 2 +- .../componentScreens/PanListenerScreen.tsx | 7 +- .../componentScreens/ScrollBarScreen.tsx | 5 +- .../SectionsWheelPickerScreen.tsx | 2 +- .../screens/componentScreens/SliderScreen.tsx | 32 +++++-- .../TabControllerScreen/index.tsx | 7 +- .../componentScreens/TextFieldScreen.tsx | 2 +- .../componentScreens/WheelPickerScreen.tsx | 4 +- .../screens/componentScreens/WizardScreen.tsx | 7 +- .../IncubatorExpandableOverlayScreen.tsx | 12 +-- .../IncubatorSliderScreen.tsx | 5 + .../incubatorScreens/IncubatorToastScreen.tsx | 5 +- .../screens/realExamples/Pinterest/index.tsx | 20 ++-- .../realExamples/ProductPage/index.tsx | 26 +++-- .../screens/realExamples/Twitter/index.tsx | 2 +- src/commons/modifiers.ts | 4 +- src/components/avatar/index.tsx | 8 +- src/components/button/ButtonTypes.tsx | 11 ++- src/components/button/index.tsx | 12 ++- src/components/card/index.tsx | 4 +- src/components/chip/index.tsx | 1 - src/components/icon/index.tsx | 3 +- src/components/image/index.tsx | 2 +- src/components/modal/TopBar.tsx | 6 +- src/components/picker/index.tsx | 1 + src/incubator/TouchableOpacity.tsx | 1 + src/incubator/index.ts | 2 +- src/incubator/toast/types.ts | 5 +- src/index.ts | 2 + src/utils/imageUtils.ts | 4 +- tsconfig.dev.json | 2 +- typings/globalTypes.d.ts | 3 +- 45 files changed, 204 insertions(+), 171 deletions(-) diff --git a/demo/src/screens/PlaygroundScreen.tsx b/demo/src/screens/PlaygroundScreen.tsx index fd1a8fbf74..0bda100e67 100644 --- a/demo/src/screens/PlaygroundScreen.tsx +++ b/demo/src/screens/PlaygroundScreen.tsx @@ -1,5 +1,5 @@ import React, {Component} from 'react'; -import {View, Text, Card, TextField, Button} from 'react-native-ui-lib'; //eslint-disable-line +import {View, Text, Card, TextField, Button} from 'react-native-ui-lib'; export default class PlaygroundScreen extends Component { render() { diff --git a/demo/src/screens/componentScreens/ActionBarScreen.tsx b/demo/src/screens/componentScreens/ActionBarScreen.tsx index 21f8f5b1d3..8fbe772317 100644 --- a/demo/src/screens/componentScreens/ActionBarScreen.tsx +++ b/demo/src/screens/componentScreens/ActionBarScreen.tsx @@ -9,13 +9,9 @@ import collections from '../../assets/icons/collections.png'; import richText from '../../assets/icons/richText.png'; export default class ActionBarScreen extends Component { - constructor(props) { - super(props); - - this.state = { - currentPage: 0 - }; - } + state = { + currentPage: 0 + }; render() { return ( diff --git a/demo/src/screens/componentScreens/AvatarsScreen.tsx b/demo/src/screens/componentScreens/AvatarsScreen.tsx index 281ccf4c57..37d9cbe627 100644 --- a/demo/src/screens/componentScreens/AvatarsScreen.tsx +++ b/demo/src/screens/componentScreens/AvatarsScreen.tsx @@ -1,7 +1,7 @@ import _ from 'lodash'; import React, {Component} from 'react'; import {ScrollView, StyleSheet, Alert} from 'react-native'; -import {Avatar, AvatarHelper, View, Text, Colors, Typography} from 'react-native-ui-lib'; //eslint-disable-line +import {Avatar, AvatarHelper, View, Text, Colors, Typography, AvatarProps} from 'react-native-ui-lib'; const star = require('../../assets/icons/star.png'); @@ -25,7 +25,7 @@ const examples = [ 'https://lh3.googleusercontent.com/-cw77lUnOvmI/AAAAAAAAAAI/AAAAAAAAAAA/WMNck32dKbc/s181-c/104220521160525129167.jpg' }, badgeProps: {size: 10, backgroundColor: Colors.$backgroundWarningHeavy}, - badgePosition: 'BOTTOM_RIGHT' + badgePosition: 'BOTTOM_RIGHT' as AvatarProps['badgePosition'] }, { @@ -36,7 +36,7 @@ const examples = [ 'https://lh3.googleusercontent.com/-CMM0GmT5tiI/AAAAAAAAAAI/AAAAAAAAAAA/-o9gKbC6FVo/s181-c/111308920004613908895.jpg' }, badgeProps: {size: 10, backgroundColor: Colors.$backgroundDisabled}, - badgePosition: 'BOTTOM_LEFT' + badgePosition: 'BOTTOM_LEFT' as AvatarProps['badgePosition'] }, { title: 'Image with fade in animation', @@ -53,7 +53,7 @@ const examples = [ uri: 'https://randomuser.me/api/portraits/women/24.jpg' }, badgeProps: {size: 14, borderWidth: 0, backgroundColor: onlineColor}, - badgePosition: 'TOP_LEFT' + badgePosition: 'TOP_LEFT' as AvatarProps['badgePosition'] }, { title: 'Icon badge', @@ -102,7 +102,7 @@ const examples = [ title: 'With custom badge label', label: 'LD', backgroundColor: Colors.$backgroundDangerLight, - badgePosition: 'BOTTOM_RIGHT', + badgePosition: 'BOTTOM_RIGHT' as AvatarProps['badgePosition'], badgeProps: { label: '+2', size: 24, @@ -123,7 +123,7 @@ export default class AvatarsScreen extends Component { patchedGravatar ? `Patched-uri: ${patchedGravatar}` : '' }`; Alert.alert(title, message); - } + }; render() { return ( diff --git a/demo/src/screens/componentScreens/BasicListScreen.tsx b/demo/src/screens/componentScreens/BasicListScreen.tsx index 1b75a9ef31..7ea836bd7f 100644 --- a/demo/src/screens/componentScreens/BasicListScreen.tsx +++ b/demo/src/screens/componentScreens/BasicListScreen.tsx @@ -14,7 +14,6 @@ export default class BasicListScreen extends Component { return ( { if (this.state.buttonProps === labelButton) { @@ -241,9 +237,11 @@ export default class ButtonsScreen extends Component { style={{ width: 20, height: 20, - backgroundColor: iconStyle[0].tintColor, + // @ts-expect-error + backgroundColor: iconStyle[0]?.tintColor, borderRadius: 10, - marginRight: iconStyle[0].marginRight + // @ts-expect-error + marginRight: iconStyle[0]?.marginRight }} /> )} diff --git a/demo/src/screens/componentScreens/CarouselScreen.tsx b/demo/src/screens/componentScreens/CarouselScreen.tsx index 7e795f7b11..80cecbf64a 100644 --- a/demo/src/screens/componentScreens/CarouselScreen.tsx +++ b/demo/src/screens/componentScreens/CarouselScreen.tsx @@ -117,7 +117,7 @@ class CarouselScreen extends Component { pageControlProps={{onPagePress: this.onPagePress, limitShownPages}} allowAccessibleLayout > - {_.map([...Array(numberOfPagesShown)], (item, index) => ( + {_.map([...Array(numberOfPagesShown)], (_item, index) => ( CARD {index} diff --git a/demo/src/screens/componentScreens/ChipsInputScreen.tsx b/demo/src/screens/componentScreens/ChipsInputScreen.tsx index 8b5ff6673c..b04c981e92 100644 --- a/demo/src/screens/componentScreens/ChipsInputScreen.tsx +++ b/demo/src/screens/componentScreens/ChipsInputScreen.tsx @@ -1,5 +1,5 @@ import React, {Component} from 'react'; -import {View, Text, Card, TextField, Button, Colors, ChipsInput} from 'react-native-ui-lib'; //eslint-disable-line +import {View, Text, Colors, ChipsInput} from 'react-native-ui-lib'; import _ from 'lodash'; export default class ChipsInputScreen extends Component { diff --git a/demo/src/screens/componentScreens/ColorPickerScreen.tsx b/demo/src/screens/componentScreens/ColorPickerScreen.tsx index c65e612385..b5025b0047 100644 --- a/demo/src/screens/componentScreens/ColorPickerScreen.tsx +++ b/demo/src/screens/componentScreens/ColorPickerScreen.tsx @@ -7,7 +7,7 @@ import {renderMultipleSegmentOptions} from '../ExampleScreenPresenter'; interface Props {} interface State { color: string; - textColor: string; + textColor?: string; customColors: string[]; paletteChange: boolean; backgroundColor: string; diff --git a/demo/src/screens/componentScreens/DrawerScreen.tsx b/demo/src/screens/componentScreens/DrawerScreen.tsx index 79d2d39f33..227da59e2d 100644 --- a/demo/src/screens/componentScreens/DrawerScreen.tsx +++ b/demo/src/screens/componentScreens/DrawerScreen.tsx @@ -1,61 +1,48 @@ import React, {Component} from 'react'; import {StyleSheet, ScrollView, LayoutAnimation} from 'react-native'; -import { - Assets, - Colors, - Typography, - View, - Drawer, - Text, - Button, - Avatar, - Badge -} from 'react-native-ui-lib'; //eslint-disable-line +import {Assets, Colors, Typography, View, Drawer, Text, Button, Avatar, Badge, DrawerProps} from 'react-native-ui-lib'; import {gestureHandlerRootHOC} from 'react-native-gesture-handler'; import conversations from '../../data/conversations'; -import { - renderBooleanOption, - renderSliderOption, - renderColorOption -} from '../ExampleScreenPresenter'; +import {renderBooleanOption, renderSliderOption, renderColorOption} from '../ExampleScreenPresenter'; const ITEMS = { read: { icon: require('../../assets/icons/mail.png'), text: 'Read', background: Colors.green30, - testID: "left_item_read" + testID: 'left_item_read' }, archive: { icon: require('../../assets/icons/archive.png'), text: 'Archive', background: Colors.blue30, - testID: "right_item_archive" + testID: 'right_item_archive' }, delete: { icon: require('../../assets/icons/delete.png'), text: 'Delete', background: Colors.red30, - testID: "right_item_delete" + testID: 'right_item_delete' } }; class DrawerScreen extends Component { - constructor(props) { - super(props); + state = { + hideItem: false, + showRightItems: true, + fullSwipeRight: true, + showLeftItem: true, + fullSwipeLeft: true, + unread: true, + itemsTintColor: undefined, + bounciness: undefined, + itemsIconSize: undefined + }; - this.state = { - hideItem: false, - showRightItems: true, - fullSwipeRight: true, - showLeftItem: true, - fullSwipeLeft: true, - unread: true - }; - } + ref: React.Ref = null; - componentDidUpdate(prevProps, prevState) { + componentDidUpdate(_prevProps: any, prevState: typeof this.state) { if (this.state.hideItem && prevState.hideItem) { this.showItem(); } @@ -82,7 +69,7 @@ class DrawerScreen extends Component { toggleReadState = () => { this.setState({unread: !this.state.unread}); - } + }; showItem = () => { this.setState({hideItem: false}); @@ -90,31 +77,37 @@ class DrawerScreen extends Component { openLeftDrawer = () => { if (this.ref) { + // @ts-expect-error this.ref.openLeft(); } }; openLeftDrawerFull = () => { if (this.ref) { + // @ts-expect-error this.ref.openLeftFull(); } }; toggleLeftDrawer = () => { if (this.ref) { + // @ts-expect-error this.ref.toggleLeft(); } }; openRightDrawer = () => { if (this.ref) { + // @ts-expect-error this.ref.openRight(); } }; openRightDrawerFull = () => { if (this.ref) { + // @ts-expect-error this.ref.openRightFull(); } }; closeDrawer = () => { if (this.ref) { + // @ts-expect-error this.ref.closeDrawer(); } }; @@ -149,13 +142,7 @@ class DrawerScreen extends Component { -