From 68495b1fe2b163087a545d94db66d7b6f76bbad1 Mon Sep 17 00:00:00 2001 From: Dmitry Vaytul Date: Fri, 26 Nov 2021 11:22:12 +0300 Subject: [PATCH 01/11] feat: add forwardRef to textInput --- src/components/ui/input/input.component.tsx | 7 +++--- src/components/ui/input/input.spec.tsx | 24 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/components/ui/input/input.component.tsx b/src/components/ui/input/input.component.tsx index cc73dfa4a..739364ccb 100644 --- a/src/components/ui/input/input.component.tsx +++ b/src/components/ui/input/input.component.tsx @@ -55,6 +55,7 @@ export interface InputProps extends TextInputProps, InputStyledProps { accessoryLeft?: RenderProp>; accessoryRight?: RenderProp>; textStyle?: StyleProp; + textInputRef?: React.RefObject; } export type InputElement = React.ReactElement; @@ -140,7 +141,7 @@ export type InputElement = React.ReactElement; @styled('Input') export class Input extends React.Component implements WebEventResponderCallbacks { - private textInputRef = React.createRef(); + private textInputRef = this.props.textInputRef || React.createRef(); private webEventResponder: WebEventResponderInstance = WebEventResponder.create(this); public focus = (): void => { @@ -288,8 +289,8 @@ export class Input extends React.Component implements WebEventRespon component={accessoryRight} /> - diff --git a/src/components/ui/input/input.spec.tsx b/src/components/ui/input/input.spec.tsx index 0014d5d35..b5e1b6f16 100644 --- a/src/components/ui/input/input.spec.tsx +++ b/src/components/ui/input/input.spec.tsx @@ -71,6 +71,30 @@ describe('@input: component checks', () => { expect(componentRef.current.clear).toBeTruthy(); }); + it('should forward ref to kitten input', () => { + const inputRef: React.RefObject = React.createRef(); + render( + , + ); + + // should not be RN textInput + expect(inputRef.current.setNativeProps).toBeFalsy(); + // should have inner prop that is RN textInput + expect(inputRef.current.textInputRef.current.setNativeProps).toBeTruthy(); + }); + + it('should forward ref to RN textInput', () => { + const textInputRef: React.RefObject = React.createRef(); + render( + , + ); + + // should be RN textInput + expect(textInputRef.current.setNativeProps).toBeTruthy(); + // should not have inner prop + expect(textInputRef.current.textInputRef).toBeUndefined(); + }); + it('should set TextInput editable to false by passing disabled prop', () => { const component = render( , From 2c1ebd7f8ed577ea4e5d831a50cb409e36cdd037 Mon Sep 17 00:00:00 2001 From: Dmitry Vaytul Date: Fri, 26 Nov 2021 11:41:20 +0300 Subject: [PATCH 02/11] feat: add description of TextInput ref --- src/components/ui/input/input.component.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/ui/input/input.component.tsx b/src/components/ui/input/input.component.tsx index 739364ccb..a45378868 100644 --- a/src/components/ui/input/input.component.tsx +++ b/src/components/ui/input/input.component.tsx @@ -105,6 +105,8 @@ export type InputElement = React.ReactElement; * * @property {StyleProp} textStyle - Customizes the style of the text field. * + * @property {React.RefObject} textInputRef - Ref to the underlying TextInput. + * * @property {TextInputProps} ...TextInputProps - Any props applied to TextInput component. * * @overview-example InputSimpleUsage From 6c7289151b4c94ac61085b5d5588eeb1ba29a6ca Mon Sep 17 00:00:00 2001 From: Dmitry Vaytul Date: Fri, 26 Nov 2021 13:14:44 +0300 Subject: [PATCH 03/11] feat add forwardRef to List --- src/components/ui/list/list.component.tsx | 8 ++++-- src/components/ui/list/list.spec.tsx | 31 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/components/ui/list/list.component.tsx b/src/components/ui/list/list.component.tsx index 15f9938b4..2a66bda92 100644 --- a/src/components/ui/list/list.component.tsx +++ b/src/components/ui/list/list.component.tsx @@ -19,7 +19,9 @@ type ListStyledProps = Overwrite; }>; -export type ListProps = FlatListProps & ListStyledProps; +export type ListProps = FlatListProps & ListStyledProps & { + flatListRef?: React.RefObject; +}; export type ListElement = React.ReactElement>; export interface BaseScrollParams { @@ -49,6 +51,8 @@ export interface ScrollToOffsetParams extends BaseScrollParams { * @property {(ListRenderItemInfo) => ReactElement} renderItem - Takes an * item from *data* and renders it into the list. * + * @property {React.RefObject} flatListRef - Ref to the underlying FlatList. + * * @property {FlatListProps} ...FlatListProps - Any props applied to FlatList component. * * @overview-example ListSimpleUsage @@ -67,7 +71,7 @@ export interface ScrollToOffsetParams extends BaseScrollParams { @styled('List') export class List extends React.Component> { - private listRef = React.createRef(); + private listRef = this.props.flatListRef || React.createRef(); public scrollToEnd = (params?: BaseScrollParams): void => { this.listRef.current?.scrollToEnd(params); diff --git a/src/components/ui/list/list.spec.tsx b/src/components/ui/list/list.spec.tsx index 69d753bd4..c47428b43 100644 --- a/src/components/ui/list/list.spec.tsx +++ b/src/components/ui/list/list.spec.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { + FlatList, Image, ImageProps, Text, @@ -236,4 +237,34 @@ describe('@list: component checks', () => { componentRef.current.scrollToOffset({ offset: 0 }); }); + it('should forward ref to kitten List', () => { + const componentRef = React.createRef(); + render( + , + ); + + // should not be RN FlatList + expect(componentRef.current.setNativeProps).toBeFalsy(); + // should have inner prop that is RN FlatList + expect(componentRef.current.listRef.current.setNativeProps).toBeTruthy(); + }); + + it('should forward ref to RN FlatList', () => { + const flatListRef = React.createRef(); + render( + , + ); + + // should be RN FlatList + expect(flatListRef.current.setNativeProps).toBeTruthy(); + // should not have inner prop + expect(flatListRef.current.listRef).toBeUndefined(); + }); + }); From a10c69a1dd50ddb79b1d065a71d89b2d65387dac Mon Sep 17 00:00:00 2001 From: Dmitry Vaytul Date: Mon, 29 Nov 2021 14:17:58 +0300 Subject: [PATCH 04/11] feat: add ref to underlying TextInput --- .../ui/autocomplete/autocomplete.component.tsx | 4 ++++ .../ui/autocomplete/autocomplete.spec.tsx | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/components/ui/autocomplete/autocomplete.component.tsx b/src/components/ui/autocomplete/autocomplete.component.tsx index 2a96441fb..02585f63a 100644 --- a/src/components/ui/autocomplete/autocomplete.component.tsx +++ b/src/components/ui/autocomplete/autocomplete.component.tsx @@ -9,6 +9,7 @@ import { ListRenderItemInfo, NativeSyntheticEvent, StyleSheet, + TextInput, TextInputFocusEventData, TextInputSubmitEditingEventData, View, @@ -33,6 +34,7 @@ export interface AutocompleteProps extends InputProps { children?: ChildrenWithProps; onSelect?: (index: number) => void; placement?: string; + textInputRef?: React.RefObject; } export type AutocompleteElement = React.ReactElement; @@ -94,6 +96,8 @@ interface State { * * @property {() => void} onBlur - Called when options list becomes invisible. * + * @property {React.RefObject} textInputRef - Ref to the underlying TextInput. + * * @property {InputProps} ...InputProps - Any props applied to Input component. * * @overview-example AutocompleteSimpleUsage diff --git a/src/components/ui/autocomplete/autocomplete.spec.tsx b/src/components/ui/autocomplete/autocomplete.spec.tsx index 494d831af..9f67d49f6 100644 --- a/src/components/ui/autocomplete/autocomplete.spec.tsx +++ b/src/components/ui/autocomplete/autocomplete.spec.tsx @@ -373,4 +373,21 @@ describe('@autocomplete: component checks', () => { componentRef.current.hide(); }); + it('should forward ref to RN TextInput', () => { + const componentRef: React.RefObject = React.createRef(); + const textInputRef: React.RefObject = React.createRef(); + render( + , + ); + + // should not be Autocomplete, not RN TextInput + expect(componentRef.current.setNativeProps).toBeFalsy(); + // should have inner prop that is kitten Input, not RN TextInput + expect(componentRef.current.inputRef.current.setNativeProps).toBeFalsy(); + // should be RN TextInput + expect(textInputRef.current.setNativeProps).toBeTruthy(); + }); + }); From 139563fdf228caa46a22b176b1b8987b07a916f9 Mon Sep 17 00:00:00 2001 From: Dmitry Vaytul Date: Mon, 29 Nov 2021 14:50:49 +0300 Subject: [PATCH 05/11] feat: add ref in Menu to underlying FlatList --- src/components/ui/menu/menu.component.tsx | 7 +++++-- src/components/ui/menu/menu.spec.tsx | 13 +++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/components/ui/menu/menu.component.tsx b/src/components/ui/menu/menu.component.tsx index 39441e230..cb9d4be07 100644 --- a/src/components/ui/menu/menu.component.tsx +++ b/src/components/ui/menu/menu.component.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { ListRenderItemInfo } from 'react-native'; +import { FlatList, ListRenderItemInfo } from 'react-native'; import { ChildrenWithProps, IndexPath, @@ -38,6 +38,7 @@ export interface MenuProps extends MenuListProps { children?: ChildrenWithProps; selectedIndex?: IndexPath; onSelect?: (index: IndexPath) => void; + flatListRef?: React.RefObject; } export type MenuElement = React.ReactElement; @@ -64,6 +65,8 @@ export type MenuElement = React.ReactElement; * Called with `row: number, section: number` for items rendered within group, * where row - index of item in group, section - index of group in list. * + * @property {React.RefObject} flatListRef - Ref to the underlying FlatList. + * * @property {ListProps} ...ListProps - Any props applied to List component, * excluding `renderItem` and `data`. * @@ -167,4 +170,4 @@ export class Menu extends React.Component { /> ); } -} \ No newline at end of file +} diff --git a/src/components/ui/menu/menu.spec.tsx b/src/components/ui/menu/menu.spec.tsx index 047d12d64..0acd9db06 100644 --- a/src/components/ui/menu/menu.spec.tsx +++ b/src/components/ui/menu/menu.spec.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { + FlatList, Image, ImageProps, Text, @@ -30,6 +31,7 @@ import { } from './menuItem.component'; import { IndexPath } from '../../devsupport'; import { MenuGroup } from './menuGroup.component'; +import { List } from '../list/list.component'; jest.useFakeTimers(); @@ -182,6 +184,17 @@ describe('@menu: component checks', () => { expect(component.queryByText('Option 2')).toBeTruthy(); }); + it('should forward ref to underlying RN FlatList', () => { + const flatListRef = React.createRef(); + + render( + , + ); + + // should be RN FlatList + expect(flatListRef.current.setNativeProps).toBeTruthy(); + }); + it('should call onSelect with non-grouped index', () => { const onSelect = jest.fn((index: IndexPath) => { expect(index.row).toEqual(1); From 3676fbf7be97f605e55de257c3f75215625b570e Mon Sep 17 00:00:00 2001 From: Dmitry Vaytul Date: Mon, 29 Nov 2021 15:44:49 +0300 Subject: [PATCH 06/11] feat: add ref in OverflowMenu to underlying Flatlist --- .../ui/overflowMenu/overflowMenu.spec.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/components/ui/overflowMenu/overflowMenu.spec.tsx b/src/components/ui/overflowMenu/overflowMenu.spec.tsx index 215162115..5c55dd946 100644 --- a/src/components/ui/overflowMenu/overflowMenu.spec.tsx +++ b/src/components/ui/overflowMenu/overflowMenu.spec.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { Button, + FlatList, StyleSheet, TouchableOpacity, } from 'react-native'; @@ -163,4 +164,21 @@ describe('@overflow-menu: component checks', () => { expect(options.length).toEqual(0); }); + it('should forward ref to underlying RN FlatList', async () => { + const componentRef = React.createRef(); + const flatListRef = React.createRef(); + + render( + , + ); + + componentRef.current.show(); + await waitForElement(() => null); + + expect(componentRef.current.setNativeProps).toBeFalsy(); + expect(flatListRef.current.setNativeProps).toBeTruthy(); + }); + }); From 90f20486b08eeced473bdb6484d55dcf0143a5d6 Mon Sep 17 00:00:00 2001 From: Dmitry Vaytul Date: Mon, 29 Nov 2021 16:14:17 +0300 Subject: [PATCH 07/11] feat: add ref in Drawer to underlying Flatlist --- src/components/ui/drawer/drawer.component.tsx | 2 +- src/components/ui/drawer/drawer.spec.tsx | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/ui/drawer/drawer.component.tsx b/src/components/ui/drawer/drawer.component.tsx index 945e3d068..7d00897f2 100644 --- a/src/components/ui/drawer/drawer.component.tsx +++ b/src/components/ui/drawer/drawer.component.tsx @@ -34,7 +34,7 @@ export type DrawerElement = React.ReactElement; * * @property {ReactElement | ReactElement[]} children - * items to be rendered within drawer. - * + * * @property {ReactElement | (ViewProps) => ReactElement} header - Function component * to render above the content. * diff --git a/src/components/ui/drawer/drawer.spec.tsx b/src/components/ui/drawer/drawer.spec.tsx index aba9ac507..d88565e2f 100644 --- a/src/components/ui/drawer/drawer.spec.tsx +++ b/src/components/ui/drawer/drawer.spec.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { + FlatList, Image, ImageProps, Text, @@ -189,4 +190,14 @@ describe('@drawer: component checks', () => { expect(component.queryByText('I love Babel')).toBeTruthy(); }); + it('should forward ref to underlying RN FlatList', () => { + const flatListRef = React.createRef(); + + render( + , + ); + + expect(flatListRef.current.setNativeProps).toBeTruthy(); + }); + }); From 2d0dba38d9cc3b823ddec9064ddb466d95fab891 Mon Sep 17 00:00:00 2001 From: Dmitry Vaytul Date: Mon, 29 Nov 2021 16:55:14 +0300 Subject: [PATCH 08/11] feat: add ref in Select to underlying FlatList --- src/components/ui/select/select.component.tsx | 7 ++++++- src/components/ui/select/select.spec.tsx | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/components/ui/select/select.component.tsx b/src/components/ui/select/select.component.tsx index 50fdea89d..8bb445f15 100644 --- a/src/components/ui/select/select.component.tsx +++ b/src/components/ui/select/select.component.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { Animated, + FlatList, GestureResponderEvent, ImageProps, ListRenderItemInfo, @@ -69,6 +70,7 @@ export interface SelectProps extends TouchableWebProps, SelectStyledProps { accessoryRight?: RenderProp>; status?: EvaStatus; size?: EvaInputSize; + flatListRef?: React.RefObject; } export type SelectElement = React.ReactElement; @@ -150,6 +152,8 @@ const CHEVRON_ANIM_DURATION: number = 200; * Can be `small`, `medium` or `large`. * Defaults to *medium*. * + * @property {React.RefObject} flatListRef - Ref to the underlying FlatList. + * * @property {() => void} onFocus - Called when options list becomes visible. * * @property {() => void} onBlur - Called when options list becomes invisible. @@ -483,7 +487,7 @@ export class Select extends React.Component { }; public render(): React.ReactElement { - const { eva, style, label, caption, children, ...touchableProps } = this.props; + const { eva, style, label, caption, children, flatListRef, ...touchableProps } = this.props; const evaStyle = this.getComponentStyle(eva.style); return ( @@ -501,6 +505,7 @@ export class Select extends React.Component { onBackdropPress={this.onBackdropPress}> { componentRef.current.hide(); }); + it('should be able to call hide with ref', async () => { + const componentRef: React.RefObject = React.createRef(); - render( - , - ); - - // should not be RN textInput - expect(inputRef.current.setNativeProps).toBeFalsy(); - // should have inner prop that is RN textInput - expect(inputRef.current.textInputRef.current.setNativeProps).toBeTruthy(); - }); - - it('should forward ref to RN textInput', () => { + it('should forward ref to underlying RN TextInput', () => { + const componentRef: React.RefObject = React.createRef(); const textInputRef: React.RefObject = React.createRef(); + render( - , + , ); - // should be RN textInput + expect(componentRef.current.constructor.name).toEqual('Input'); + // @ts-ignore + expect(componentRef.current.textInputRef.current.constructor.displayName).toEqual('TextInput'); expect(textInputRef.current.setNativeProps).toBeTruthy(); - // should not have inner prop - expect(textInputRef.current.textInputRef).toBeUndefined(); + // @ts-ignore + expect(textInputRef.current.constructor.displayName).toEqual('TextInput'); }); it('should set TextInput editable to false by passing disabled prop', () => { diff --git a/src/components/ui/list/list.spec.tsx b/src/components/ui/list/list.spec.tsx index c47428b43..bf8c6f2ea 100644 --- a/src/components/ui/list/list.spec.tsx +++ b/src/components/ui/list/list.spec.tsx @@ -237,34 +237,23 @@ describe('@list: component checks', () => { componentRef.current.scrollToOffset({ offset: 0 }); }); - it('should forward ref to kitten List', () => { + it('should forward ref to underlying RN FlatList', () => { const componentRef = React.createRef(); - render( - , - ); - - // should not be RN FlatList - expect(componentRef.current.setNativeProps).toBeFalsy(); - // should have inner prop that is RN FlatList - expect(componentRef.current.listRef.current.setNativeProps).toBeTruthy(); - }); - - it('should forward ref to RN FlatList', () => { const flatListRef = React.createRef(); + render( , ); - // should be RN FlatList + expect(componentRef.current.constructor.name).toEqual('List'); + // @ts-ignore + expect(componentRef.current.listRef.current.constructor.name).toEqual('FlatList'); expect(flatListRef.current.setNativeProps).toBeTruthy(); - // should not have inner prop - expect(flatListRef.current.listRef).toBeUndefined(); + expect(flatListRef.current.constructor.name).toEqual('FlatList'); }); }); diff --git a/src/components/ui/menu/menu.spec.tsx b/src/components/ui/menu/menu.spec.tsx index 0acd9db06..8d4cdd813 100644 --- a/src/components/ui/menu/menu.spec.tsx +++ b/src/components/ui/menu/menu.spec.tsx @@ -32,6 +32,7 @@ import { import { IndexPath } from '../../devsupport'; import { MenuGroup } from './menuGroup.component'; import { List } from '../list/list.component'; +import { OverflowMenu } from '../overflowMenu/overflowMenu.component'; jest.useFakeTimers(); @@ -191,8 +192,8 @@ describe('@menu: component checks', () => { , ); - // should be RN FlatList expect(flatListRef.current.setNativeProps).toBeTruthy(); + expect(flatListRef.current.constructor.name).toEqual('FlatList'); }); it('should call onSelect with non-grouped index', () => { @@ -246,7 +247,7 @@ describe('@menu: component checks', () => { - + , ); fireEvent.press(component.queryByText('Group 1')); @@ -266,7 +267,7 @@ describe('@menu: component checks', () => { - + , ); fireEvent.press(component.queryByText('Group 2')); diff --git a/src/components/ui/overflowMenu/overflowMenu.spec.tsx b/src/components/ui/overflowMenu/overflowMenu.spec.tsx index 5c55dd946..2ae49d11f 100644 --- a/src/components/ui/overflowMenu/overflowMenu.spec.tsx +++ b/src/components/ui/overflowMenu/overflowMenu.spec.tsx @@ -177,8 +177,9 @@ describe('@overflow-menu: component checks', () => { componentRef.current.show(); await waitForElement(() => null); - expect(componentRef.current.setNativeProps).toBeFalsy(); + expect(componentRef.current.constructor.name).toEqual('OverflowMenu'); expect(flatListRef.current.setNativeProps).toBeTruthy(); + expect(flatListRef.current.constructor.name).toEqual('FlatList'); }); }); diff --git a/src/components/ui/select/select.spec.tsx b/src/components/ui/select/select.spec.tsx index 4526ccab4..f2169a94d 100644 --- a/src/components/ui/select/select.spec.tsx +++ b/src/components/ui/select/select.spec.tsx @@ -532,7 +532,7 @@ describe('@select: component checks', () => { componentRef.current.hide(); }); - it('should be able to call hide with ref', async () => { + it('should forward ref to underlying RN FlatList', async () => { const componentRef: React.RefObject