From bde1d63c853630609b22c87121c125775dd1f5cb Mon Sep 17 00:00:00 2001 From: Kacie Bawiec Date: Tue, 3 Dec 2019 10:15:10 -0800 Subject: [PATCH] Add getNativeScrollRef to FlatList Summary: Add a method to get the underlying host component of `FlatList`. Fix flow types in `FlatList` and `VirtualizedList`. Add test cases to test the behavior of the new function in all cases. Changelog: [General] [Added] - Add getNativeScrollRef method to FlatList component Reviewed By: TheSavior Differential Revision: D18302202 fbshipit-source-id: 7005a2bc1dab207434be3f1f4d8fde0b11b3bb4d --- Libraries/Lists/FlatList.js | 23 +++++++- Libraries/Lists/VirtualizedList.js | 4 +- Libraries/Lists/__tests__/FlatList-test.js | 59 +++++++++++++++++++ .../Lists/__tests__/VirtualizedList-test.js | 53 +++++++++++++++++ 4 files changed, 137 insertions(+), 2 deletions(-) diff --git a/Libraries/Lists/FlatList.js b/Libraries/Lists/FlatList.js index 3ede56e8266cd5..c8f262c900b99a 100644 --- a/Libraries/Lists/FlatList.js +++ b/Libraries/Lists/FlatList.js @@ -19,7 +19,10 @@ const StyleSheet = require('../StyleSheet/StyleSheet'); const invariant = require('invariant'); -import type {ScrollResponderType} from '../Components/ScrollView/ScrollView'; +import ScrollView, { + type ScrollResponderType, +} from '../Components/ScrollView/ScrollView'; +import type {ScrollViewNativeComponentType} from '../Components/ScrollView/ScrollViewNativeComponentType.js'; import type {ViewStyleProp} from '../StyleSheet/StyleSheet'; import type { ViewToken, @@ -367,6 +370,24 @@ class FlatList extends React.PureComponent, void> { } } + /** + * Provides a reference to the underlying host component + */ + getNativeScrollRef(): + | ?React.ElementRef + | ?React.ElementRef { + if (this._listRef) { + const scrollRef = this._listRef.getScrollRef(); + if (scrollRef != null) { + if (scrollRef instanceof ScrollView) { + return scrollRef.getNativeScrollRef(); + } else { + return scrollRef; + } + } + } + } + getScrollableNode(): any { if (this._listRef) { return this._listRef.getScrollableNode(); diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index 927849b102db8e..5ef712ca32f87a 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -520,7 +520,9 @@ class VirtualizedList extends React.PureComponent { } } - getScrollRef(): ?React.ElementRef { + getScrollRef(): + | ?React.ElementRef + | ?React.ElementRef { if (this._scrollRef && this._scrollRef.getScrollRef) { return this._scrollRef.getScrollRef(); } else { diff --git a/Libraries/Lists/__tests__/FlatList-test.js b/Libraries/Lists/__tests__/FlatList-test.js index 79b5ecd2c2821e..eb7e648d258e9a 100644 --- a/Libraries/Lists/__tests__/FlatList-test.js +++ b/Libraries/Lists/__tests__/FlatList-test.js @@ -93,4 +93,63 @@ describe('FlatList', () => { ); expect(component).toMatchSnapshot(); }); + it('getNativeScrollRef for case where it returns a native view', () => { + jest.resetModules(); + jest.unmock('../../Components/ScrollView/ScrollView'); + + const listRef = React.createRef(null); + + ReactTestRenderer.create( + ( + { + return ; + }} + ref={listRef} + /> + )} + />, + ); + + const scrollRef = listRef.current.getNativeScrollRef(); + + // This is checking if the ref acts like a host component. If we had an + // `isHostComponent(ref)` method, that would be preferred. + expect(scrollRef.measure).toBeInstanceOf(jest.fn().constructor); + expect(scrollRef.measureLayout).toBeInstanceOf(jest.fn().constructor); + expect(scrollRef.measureInWindow).toBeInstanceOf(jest.fn().constructor); + }); + + it('getNativeScrollRef for case where it returns a native scroll view', () => { + jest.resetModules(); + jest.unmock('../../Components/ScrollView/ScrollView'); + + function ListItemComponent({item}) { + return ; + } + const listRef = React.createRef(null); + + ReactTestRenderer.create( + , + ); + + const scrollRef = listRef.current.getNativeScrollRef(); + + // This is checking if the ref acts like a host component. If we had an + // `isHostComponent(ref)` method, that would be preferred. + expect(scrollRef.measure).toBeInstanceOf(jest.fn().constructor); + expect(scrollRef.measureLayout).toBeInstanceOf(jest.fn().constructor); + expect(scrollRef.measureInWindow).toBeInstanceOf(jest.fn().constructor); + }); }); diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index fca5e4caba55bf..5cee6bca2cbcdc 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -281,4 +281,57 @@ describe('VirtualizedList', () => { }), ); }); + + it('getScrollRef for case where it returns a ScrollView', () => { + const listRef = React.createRef(null); + + ReactTestRenderer.create( + } + getItem={(data, index) => data[index]} + getItemCount={data => data.length} + ref={listRef} + />, + ); + + const scrollRef = listRef.current.getScrollRef(); + + // This is checking if the ref acts like a ScrollView. If we had an + // `isScrollView(ref)` method, that would be preferred. + expect(scrollRef.scrollTo).toBeInstanceOf(Function); + }); + + it('getScrollRef for case where it returns a View', () => { + const listRef = React.createRef(null); + + ReactTestRenderer.create( + ( + { + return ; + }} + getItem={(data, index) => data[index]} + getItemCount={data => data.length} + ref={listRef} + /> + )} + getItem={(data, index) => data[index]} + getItemCount={data => data.length} + />, + ); + const scrollRef = listRef.current.getScrollRef(); + + // This is checking if the ref acts like a host component. If we had an + // `isHostComponent(ref)` method, that would be preferred. + expect(scrollRef.measure).toBeInstanceOf(jest.fn().constructor); + expect(scrollRef.measureLayout).toBeInstanceOf(jest.fn().constructor); + expect(scrollRef.measureInWindow).toBeInstanceOf(jest.fn().constructor); + }); });