Make ScrollView use ForwardRef

Summary:
Have ScrollView use forwardRef so that the host component methods like `measure` and `measureLayout` are available without having to call `getNativeScrollRef`. Instead, you can use `<ScrollView ref={myRef} />` and directly call all methods of ScrollView and host components on `myRef`.

Previous usage:
```
const myRef = React.createRef<React.ElementRef<typeof ScrollView>>();
<ScrollView ref={myRef} />

const innerViewRef = myRef.current.getNativeScrollRef();

innerViewRef.measure();
```
New usage:
```
const myRef = React.createRef<React.ElementRef<typeof View>>();
<ScrollView ref={myRef} />

// now, myRef.current can be used directly as the ref
myRef.current.measure();
myRef.current.measureLayout();

// Additionally, myRef still has access to ScrollView methods
myRef.current.scrollTo(...);
```

Changes:

* Added deprecation warnings to ScrollView methods `getNativeScrollRef`, `getScrollableNode`, and `getScrollResponder`
* Added the forwardRef call to create `ForwardedScrollView` - this takes in `ref` and passes it into the class ScrollView as `scrollViewRef`.
* Forwarded the ref to the native scroll view using `setAndForwardRef`.
* Added statics onto `ForwardedScrollView` so that `ScrollView.Context` can still be accessed.
* Added type `ScrollViewImperativeMethods`, which lists the public methods of ScrollView.
* Converted all public methods of ScrollView to arrow functions. This is because they need to be bound to the forwarded ref.
* Bound all public methods of ScrollView to the forwarded ref in the `setAndForwardRef` call.
* Flow typed the final output (ForwardedScrollView) as an abstract component that takes in the props of the `ScrollView` class, and has all methods of both the inner host component (`measure`, `measureLayout`, etc) and the public methods (`scrollTo`, etc).

Changes to mockScrollView:
* Changed mockScrollView to be able to mock the function component instead of a class component
* Updated necessary tests

Changelog:
[General] [Changed] - Make ScrollView use forwardRef

Reviewed By: TheSavior

Differential Revision: D19304480

fbshipit-source-id: 6c359897526d9d5ac6bc6ab6d5f9d82bfc0d8af4
This commit is contained in:
Kacie Bawiec 2020-03-26 16:47:39 -07:00 коммит произвёл Facebook GitHub Bot
Родитель 93ee5b2cc8
Коммит d2f314af75
10 изменённых файлов: 211 добавлений и 102 удалений

Просмотреть файл

@ -62,20 +62,29 @@ if (Platform.OS === 'android') {
RCTScrollContentView = ScrollContentViewNativeComponent; RCTScrollContentView = ScrollContentViewNativeComponent;
} }
export type ScrollResponderType = { // Public methods for ScrollView
// We'd like to do ...ScrollView here, however Flow doesn't seem export type ScrollViewImperativeMethods = $ReadOnly<{|
// to see the imperative methods of ScrollView that way. Workaround the getScrollResponder: $PropertyType<ScrollView, 'getScrollResponder'>,
// issue by specifying them manually.
getScrollableNode: $PropertyType<ScrollView, 'getScrollableNode'>, getScrollableNode: $PropertyType<ScrollView, 'getScrollableNode'>,
getInnerViewNode: $PropertyType<ScrollView, 'getInnerViewNode'>, getInnerViewNode: $PropertyType<ScrollView, 'getInnerViewNode'>,
getInnerViewRef: $PropertyType<ScrollView, 'getInnerViewRef'>, getInnerViewRef: $PropertyType<ScrollView, 'getInnerViewRef'>,
getNativeScrollRef: $PropertyType<ScrollView, 'getNativeScrollRef'>, getNativeScrollRef: $PropertyType<ScrollView, 'getNativeScrollRef'>,
setNativeProps: $PropertyType<ScrollView, 'setNativeProps'>,
scrollTo: $PropertyType<ScrollView, 'scrollTo'>, scrollTo: $PropertyType<ScrollView, 'scrollTo'>,
scrollToEnd: $PropertyType<ScrollView, 'scrollToEnd'>,
flashScrollIndicators: $PropertyType<ScrollView, 'flashScrollIndicators'>, flashScrollIndicators: $PropertyType<ScrollView, 'flashScrollIndicators'>,
...typeof ScrollResponder.Mixin,
... // ScrollResponder.Mixin public methods
}; scrollResponderZoomTo: $PropertyType<
typeof ScrollResponder.Mixin,
'scrollResponderZoomTo',
>,
scrollResponderScrollNativeHandleToKeyboard: $PropertyType<
typeof ScrollResponder.Mixin,
'scrollResponderScrollNativeHandleToKeyboard',
>,
|}>;
export type ScrollResponderType = ScrollViewImperativeMethods;
type IOSProps = $ReadOnly<{| type IOSProps = $ReadOnly<{|
/** /**
@ -581,6 +590,14 @@ export type Props = $ReadOnly<{|
* instead of calling `getInnerViewRef`. * instead of calling `getInnerViewRef`.
*/ */
innerViewRef?: React.Ref<typeof View>, innerViewRef?: React.Ref<typeof View>,
/**
* A ref to the Native ScrollView component. This ref can be used to call
* all of ScrollView's public methods, in addition to native methods like
* measure, measureLayout, etc.
*/
scrollViewRef?: React.Ref<
typeof ScrollViewNativeComponent & ScrollViewImperativeMethods,
>,
|}>; |}>;
type State = {| type State = {|
@ -603,11 +620,14 @@ function createScrollResponder(
} }
type ContextType = {|horizontal: boolean|} | null; type ContextType = {|horizontal: boolean|} | null;
const Context = React.createContext<ContextType>(null); const Context: React.Context<ContextType> = React.createContext(null);
const standardHorizontalContext: ContextType = Object.freeze({ const standardHorizontalContext: ContextType = Object.freeze({
horizontal: true, horizontal: true,
}); });
const standardVerticalContext: ContextType = Object.freeze({horizontal: false}); const standardVerticalContext: ContextType = Object.freeze({horizontal: false});
type ScrollViewComponentStatics = $ReadOnly<{|
Context: typeof Context,
|}>;
/** /**
* Component that wraps platform ScrollView while providing * Component that wraps platform ScrollView while providing
@ -750,9 +770,37 @@ class ScrollView extends React.Component<Props, State> {
} }
} }
setNativeProps(props: {[key: string]: mixed, ...}) { _setNativeRef = setAndForwardRef({
this._scrollViewRef && this._scrollViewRef.setNativeProps(props); getForwardedRef: () => this.props.scrollViewRef,
} setLocalRef: ref => {
this._scrollViewRef = ref;
/*
This is a hack. Ideally we would forwardRef to the underlying
host component. However, since ScrollView has it's own methods that can be
called as well, if we used the standard forwardRef then these
methods wouldn't be accessible and thus be a breaking change.
Therefore we edit ref to include ScrollView's public methods so that
they are callable from the ref.
*/
if (ref) {
ref.getScrollResponder = this.getScrollResponder;
ref.getScrollableNode = this.getScrollableNode;
ref.getInnerViewNode = this.getInnerViewNode;
ref.getInnerViewRef = this.getInnerViewRef;
ref.getNativeScrollRef = this.getNativeScrollRef;
ref.scrollTo = this.scrollTo;
ref.scrollToEnd = this.scrollToEnd;
ref.flashScrollIndicators = this.flashScrollIndicators;
// $FlowFixMe - This method was manually bound from ScrollResponder.mixin
ref.scrollResponderZoomTo = this.scrollResponderZoomTo;
// $FlowFixMe - This method was manually bound from ScrollResponder.mixin
ref.scrollResponderScrollNativeHandleToKeyboard = this.scrollResponderScrollNativeHandleToKeyboard;
}
},
});
/** /**
* Returns a reference to the underlying scroll responder, which supports * Returns a reference to the underlying scroll responder, which supports
@ -760,14 +808,26 @@ class ScrollView extends React.Component<Props, State> {
* implement this method so that they can be composed while providing access * implement this method so that they can be composed while providing access
* to the underlying scroll responder's methods. * to the underlying scroll responder's methods.
*/ */
getScrollResponder(): ScrollResponderType { getScrollResponder: () => ScrollResponderType = () => {
if (__DEV__) {
console.warn(
'`getScrollResponder()` is deprecated. This will be removed in a future release. ' +
'Use <ScrollView ref={myRef} /> instead.',
);
}
// $FlowFixMe - overriding type to include ScrollResponder.Mixin // $FlowFixMe - overriding type to include ScrollResponder.Mixin
return ((this: any): ScrollResponderType); return ((this: any): ScrollResponderType);
} };
getScrollableNode(): ?number { getScrollableNode: () => ?number = () => {
if (__DEV__) {
console.warn(
'`getScrollableNode()` is deprecated. This will be removed in a future release. ' +
'Use <ScrollView ref={myRef} /> instead.',
);
}
return ReactNative.findNodeHandle(this._scrollViewRef); return ReactNative.findNodeHandle(this._scrollViewRef);
} };
getInnerViewNode(): ?number { getInnerViewNode(): ?number {
console.warn( console.warn(
@ -785,9 +845,15 @@ class ScrollView extends React.Component<Props, State> {
return this._innerViewRef; return this._innerViewRef;
} }
getNativeScrollRef(): ?React.ElementRef<HostComponent<mixed>> { getNativeScrollRef: () => ?React.ElementRef<HostComponent<mixed>> = () => {
if (__DEV__) {
console.warn(
'`getNativeScrollRef()` is deprecated. This will be removed in a future release. ' +
'Use <ScrollView ref={myRef} /> instead.',
);
}
return this._scrollViewRef; return this._scrollViewRef;
} };
/** /**
* Scrolls to a given x, y offset, either immediately or with a smooth animation. * Scrolls to a given x, y offset, either immediately or with a smooth animation.
@ -800,7 +866,7 @@ class ScrollView extends React.Component<Props, State> {
* the function also accepts separate arguments as an alternative to the options object. * the function also accepts separate arguments as an alternative to the options object.
* This is deprecated due to ambiguity (y before x), and SHOULD NOT BE USED. * This is deprecated due to ambiguity (y before x), and SHOULD NOT BE USED.
*/ */
scrollTo( scrollTo: (
options?: options?:
| { | {
x?: number, x?: number,
@ -811,7 +877,18 @@ class ScrollView extends React.Component<Props, State> {
| number, | number,
deprecatedX?: number, deprecatedX?: number,
deprecatedAnimated?: boolean, deprecatedAnimated?: boolean,
) { ) => void = (
options?:
| {
x?: number,
y?: number,
animated?: boolean,
...
}
| number,
deprecatedX?: number,
deprecatedAnimated?: boolean,
) => {
let x, y, animated; let x, y, animated;
if (typeof options === 'number') { if (typeof options === 'number') {
console.warn( console.warn(
@ -831,7 +908,7 @@ class ScrollView extends React.Component<Props, State> {
y: y || 0, y: y || 0,
animated: animated !== false, animated: animated !== false,
}); });
} };
/** /**
* If this is a vertical ScrollView scrolls to the bottom. * If this is a vertical ScrollView scrolls to the bottom.
@ -841,22 +918,24 @@ class ScrollView extends React.Component<Props, State> {
* `scrollToEnd({animated: false})` for immediate scrolling. * `scrollToEnd({animated: false})` for immediate scrolling.
* If no options are passed, `animated` defaults to true. * If no options are passed, `animated` defaults to true.
*/ */
scrollToEnd(options?: ?{animated?: boolean, ...}) { scrollToEnd: (options?: ?{animated?: boolean, ...}) => void = (
options?: ?{animated?: boolean, ...},
) => {
// Default to true // Default to true
const animated = (options && options.animated) !== false; const animated = (options && options.animated) !== false;
this._scrollResponder.scrollResponderScrollToEnd({ this._scrollResponder.scrollResponderScrollToEnd({
animated: animated, animated: animated,
}); });
} };
/** /**
* Displays the scroll indicators momentarily. * Displays the scroll indicators momentarily.
* *
* @platform ios * @platform ios
*/ */
flashScrollIndicators() { flashScrollIndicators: () => void = () => {
this._scrollResponder.scrollResponderFlashScrollIndicators(); this._scrollResponder.scrollResponderFlashScrollIndicators();
} };
_getKeyForIndex(index, childArray) { _getKeyForIndex(index, childArray) {
const child = childArray[index]; const child = childArray[index];
@ -959,9 +1038,6 @@ class ScrollView extends React.Component<Props, State> {
}; };
_scrollViewRef: ?React.ElementRef<HostComponent<mixed>> = null; _scrollViewRef: ?React.ElementRef<HostComponent<mixed>> = null;
_setScrollViewRef = (ref: ?React.ElementRef<HostComponent<mixed>>) => {
this._scrollViewRef = ref;
};
_innerViewRef: ?React.ElementRef<typeof View> = null; _innerViewRef: ?React.ElementRef<typeof View> = null;
_setInnerViewRef = setAndForwardRef({ _setInnerViewRef = setAndForwardRef({
@ -1182,7 +1258,7 @@ class ScrollView extends React.Component<Props, State> {
/* $FlowFixMe(>=0.117.0 site=react_native_fb) This comment suppresses /* $FlowFixMe(>=0.117.0 site=react_native_fb) This comment suppresses
* an error found when Flow v0.117 was deployed. To see the error, * an error found when Flow v0.117 was deployed. To see the error,
* delete this comment and run Flow. */ * delete this comment and run Flow. */
<ScrollViewClass {...props} ref={this._setScrollViewRef}> <ScrollViewClass {...props} ref={this._setNativeRef}>
{Platform.isTV ? null : refreshControl} {Platform.isTV ? null : refreshControl}
{contentContainer} {contentContainer}
</ScrollViewClass> </ScrollViewClass>
@ -1200,14 +1276,14 @@ class ScrollView extends React.Component<Props, State> {
<ScrollViewClass <ScrollViewClass
{...props} {...props}
style={[baseStyle, inner]} style={[baseStyle, inner]}
ref={this._setScrollViewRef}> ref={this._setNativeRef}>
{contentContainer} {contentContainer}
</ScrollViewClass>, </ScrollViewClass>,
); );
} }
} }
return ( return (
<ScrollViewClass {...props} ref={this._setScrollViewRef}> <ScrollViewClass {...props} ref={this._setNativeRef}>
{contentContainer} {contentContainer}
</ScrollViewClass> </ScrollViewClass>
); );
@ -1232,4 +1308,22 @@ const styles = StyleSheet.create({
}, },
}); });
module.exports = ScrollView; function Wrapper(props, ref) {
return <ScrollView {...props} scrollViewRef={ref} />;
}
Wrapper.displayName = 'ScrollView';
const ForwardedScrollView = React.forwardRef(Wrapper);
// $FlowFixMe Add static context to ForwardedScrollView
ForwardedScrollView.Context = Context;
ForwardedScrollView.displayName = 'ScrollView';
module.exports = ((ForwardedScrollView: $FlowFixMe): React.AbstractComponent<
React.ElementConfig<typeof ScrollView>,
$ReadOnly<{|
...$Exact<React.ElementRef<HostComponent<mixed>>>,
...ScrollViewImperativeMethods,
|}>,
> &
ScrollViewComponentStatics);

Просмотреть файл

@ -1,39 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
*/
/* eslint-env jest */
'use strict';
const React = require('react');
const View = require('../../View/View');
const requireNativeComponent = require('../../../ReactNative/requireNativeComponent');
import type {HostComponent} from '../../../Renderer/shims/ReactNativeTypes';
const RCTScrollView: HostComponent<mixed> = requireNativeComponent<mixed>(
'RCTScrollView',
);
const ScrollViewComponent: $FlowFixMe = jest.genMockFromModule('../ScrollView');
class ScrollViewMock extends ScrollViewComponent {
render(): React.Element<typeof RCTScrollView> {
return (
<RCTScrollView {...this.props}>
{this.props.refreshControl}
<View>{this.props.children}</View>
</RCTScrollView>
);
}
}
module.exports = ScrollViewMock;

Просмотреть файл

@ -6,7 +6,7 @@
* *
* @format * @format
* @emails oncall+react_native * @emails oncall+react_native
* @flow strict-local * @flow-strict
*/ */
'use strict'; 'use strict';
@ -14,6 +14,7 @@
const React = require('react'); const React = require('react');
const ScrollView = require('../ScrollView'); const ScrollView = require('../ScrollView');
const ReactNativeTestTools = require('../../../Utilities/ReactNativeTestTools'); const ReactNativeTestTools = require('../../../Utilities/ReactNativeTestTools');
const ReactTestRenderer = require('react-test-renderer');
const View = require('../../View/View'); const View = require('../../View/View');
const Text = require('../../../Text/Text'); const Text = require('../../../Text/Text');
@ -33,4 +34,18 @@ describe('<ScrollView />', () => {
}, },
); );
}); });
it('should mock native methods and instance methods when mocked', () => {
jest.resetModules();
jest.mock('../ScrollView');
const ref = React.createRef();
ReactTestRenderer.create(<ScrollView ref={ref} />);
expect(ref.current != null && ref.current.measure).toBeInstanceOf(
jest.fn().constructor,
);
expect(ref.current != null && ref.current.scrollTo).toBeInstanceOf(
jest.fn().constructor,
);
});
}); });

Просмотреть файл

@ -36,6 +36,7 @@ exports[`<ScrollView /> should render as expected: should deep render when not m
onTouchStart={[Function]} onTouchStart={[Function]}
pagingEnabled={false} pagingEnabled={false}
scrollBarThumbImage={null} scrollBarThumbImage={null}
scrollViewRef={null}
sendMomentumEvents={false} sendMomentumEvents={false}
snapToEnd={true} snapToEnd={true}
snapToStart={true} snapToStart={true}
@ -70,21 +71,21 @@ exports[`<ScrollView /> should render as expected: should deep render when not m
`; `;
exports[`<ScrollView /> should render as expected: should shallow render as <ScrollView /> when mocked 1`] = ` exports[`<ScrollView /> should render as expected: should shallow render as <ScrollView /> when mocked 1`] = `
<ScrollView> <ForwardRef(ScrollView)>
<View> <View>
<Text> <Text>
Hello World! Hello World!
</Text> </Text>
</View> </View>
</ScrollView> </ForwardRef(ScrollView)>
`; `;
exports[`<ScrollView /> should render as expected: should shallow render as <ScrollView /> when not mocked 1`] = ` exports[`<ScrollView /> should render as expected: should shallow render as <ScrollView /> when not mocked 1`] = `
<ScrollView> <ForwardRef(ScrollView)>
<View> <View>
<Text> <Text>
Hello World! Hello World!
</Text> </Text>
</View> </View>
</ScrollView> </ForwardRef(ScrollView)>
`; `;

Просмотреть файл

@ -19,10 +19,8 @@ const StyleSheet = require('../StyleSheet/StyleSheet');
const invariant = require('invariant'); const invariant = require('invariant');
import ScrollView, { import {type ScrollResponderType} from '../Components/ScrollView/ScrollView';
type ScrollResponderType, import type {ScrollViewNativeComponentType} from '../Components/ScrollView/ScrollViewNativeComponentType.js';
} from '../Components/ScrollView/ScrollView';
import type {ScrollViewNativeComponentType} from '../Components/ScrollView/ScrollViewNativeComponentType';
import type {ViewStyleProp} from '../StyleSheet/StyleSheet'; import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
import type { import type {
ViewToken, ViewToken,
@ -377,14 +375,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
| ?React.ElementRef<typeof View> | ?React.ElementRef<typeof View>
| ?React.ElementRef<ScrollViewNativeComponentType> { | ?React.ElementRef<ScrollViewNativeComponentType> {
if (this._listRef) { if (this._listRef) {
const scrollRef = this._listRef.getScrollRef(); return this._listRef.getScrollRef();
if (scrollRef != null) {
if (scrollRef instanceof ScrollView) {
return scrollRef.getNativeScrollRef();
} else {
return scrollRef;
}
}
} }
} }

Просмотреть файл

@ -1397,10 +1397,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
// We are assuming that getOutermostParentListRef().getScrollRef() // We are assuming that getOutermostParentListRef().getScrollRef()
// is a non-null reference to a ScrollView // is a non-null reference to a ScrollView
this._scrollRef.measureLayout( this._scrollRef.measureLayout(
this.context.virtualizedList this.context.virtualizedList.getOutermostParentListRef().getScrollRef(),
.getOutermostParentListRef()
.getScrollRef()
.getNativeScrollRef(),
(x, y, width, height) => { (x, y, width, height) => {
this._offsetFromParentVirtualizedList = this._selectOffset({x, y}); this._offsetFromParentVirtualizedList = this._selectOffset({x, y});
this._scrollMetrics.contentLength = this._selectLength({ this._scrollMetrics.contentLength = this._selectLength({

Просмотреть файл

@ -300,7 +300,7 @@ describe('VirtualizedList', () => {
// This is checking if the ref acts like a ScrollView. If we had an // This is checking if the ref acts like a ScrollView. If we had an
// `isScrollView(ref)` method, that would be preferred. // `isScrollView(ref)` method, that would be preferred.
expect(scrollRef.scrollTo).toBeInstanceOf(Function); expect(scrollRef.scrollTo).toBeInstanceOf(jest.fn().constructor);
}); });
it('getScrollRef for case where it returns a View', () => { it('getScrollRef for case where it returns a View', () => {

Просмотреть файл

@ -25,7 +25,7 @@ exports[`LogBoxInspectorCodeFrame should render a code frame 1`] = `
} }
} }
> >
<ScrollViewMock <ScrollView
horizontal={true} horizontal={true}
> >
<Ansi <Ansi
@ -44,7 +44,7 @@ exports[`LogBoxInspectorCodeFrame should render a code frame 1`] = `
| ^ | ^
200 |" 200 |"
/> />
</ScrollViewMock> </ScrollView>
</View> </View>
<LogBoxButton <LogBoxButton
backgroundColor={ backgroundColor={
@ -107,7 +107,7 @@ exports[`LogBoxInspectorCodeFrame should render a code frame without a location
} }
} }
> >
<ScrollViewMock <ScrollView
horizontal={true} horizontal={true}
> >
<Ansi <Ansi
@ -126,7 +126,7 @@ exports[`LogBoxInspectorCodeFrame should render a code frame without a location
| ^ | ^
200 |" 200 |"
/> />
</ScrollViewMock> </ScrollView>
</View> </View>
<LogBoxButton <LogBoxButton
backgroundColor={ backgroundColor={

35
jest/mockScrollView.js Normal file
Просмотреть файл

@ -0,0 +1,35 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
*/
/* eslint-env jest */
'use strict';
const React = require('react');
const View = require('../Libraries/Components/View/View');
const requireNativeComponent = require('../Libraries/ReactNative/requireNativeComponent');
const RCTScrollView: $FlowFixMe = requireNativeComponent('RCTScrollView');
function mockScrollView(BaseComponent: $FlowFixMe) {
class ScrollViewMock extends BaseComponent {
render(): React.Element<typeof RCTScrollView> {
return (
<RCTScrollView {...this.props}>
{this.props.refreshControl}
<View>{this.props.children}</View>
</RCTScrollView>
);
}
}
return ScrollViewMock;
}
module.exports = (mockScrollView: $FlowFixMe);

Просмотреть файл

@ -126,11 +126,26 @@ jest
'../Libraries/Components/RefreshControl/__mocks__/RefreshControlMock', '../Libraries/Components/RefreshControl/__mocks__/RefreshControlMock',
), ),
) )
.mock('../Libraries/Components/ScrollView/ScrollView', () => .mock('../Libraries/Components/ScrollView/ScrollView', () => {
jest.requireActual( const baseComponent = mockComponent(
'../Libraries/Components/ScrollView/__mocks__/ScrollViewMock', '../Libraries/Components/ScrollView/ScrollView',
), {
) ...MockNativeMethods,
getScrollResponder: jest.fn(),
getScrollableNode: jest.fn(),
getInnerViewNode: jest.fn(),
getInnerViewRef: jest.fn(),
getNativeScrollRef: jest.fn(),
scrollTo: jest.fn(),
scrollToEnd: jest.fn(),
flashScrollIndicators: jest.fn(),
scrollResponderZoomTo: jest.fn(),
scrollResponderScrollNativeHandleToKeyboard: jest.fn(),
},
);
const mockScrollView = jest.requireActual('./mockScrollView');
return mockScrollView(baseComponent);
})
.mock('../Libraries/Components/ActivityIndicator/ActivityIndicator', () => .mock('../Libraries/Components/ActivityIndicator/ActivityIndicator', () =>
mockComponent( mockComponent(
'../Libraries/Components/ActivityIndicator/ActivityIndicator', '../Libraries/Components/ActivityIndicator/ActivityIndicator',