mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
introduce convenience method Fantom.scrollTo (#50011)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/50011 changelog: [internal] Majority of time, you want to simply scroll. This diff introduces a convenience method Fantom.scrollTo. Previously, you would have to write: ``` Fantom.runOnUIThread(() => { Fantom.enqueueScrollEvent(element, { x: 0, y: 60, }); }); Fantom.runWorkLoop(); ``` Now, you can just do: ``` Fantom.scrollTo(element, { x: 0, y: 60, }); ``` Reviewed By: rubennorte Differential Revision: D71113680 fbshipit-source-id: aed49a2f12be8ab30be549235cc7a2b3e35faadb
This commit is contained in:
committed by
Facebook GitHub Bot
parent
b275a318a6
commit
65ffc588fb
@@ -638,6 +638,106 @@ describe('Fantom', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('scrollTo', () => {
|
||||
it('throws error if called on node that is not scroll view', () => {
|
||||
const root = Fantom.createRoot();
|
||||
let maybeNode;
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<View
|
||||
ref={node => {
|
||||
maybeNode = node;
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
expect(() => {
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 1,
|
||||
});
|
||||
});
|
||||
}).toThrow(
|
||||
'Exception in HostFunction: enqueueScrollEvent() can only be called on <ScrollView />',
|
||||
);
|
||||
});
|
||||
|
||||
it('delivers onScroll event and affects position of elements on screen', () => {
|
||||
const root = Fantom.createRoot();
|
||||
let maybeScrollViewNode;
|
||||
let maybeNode;
|
||||
const onScroll = jest.fn();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView
|
||||
onScroll={event => {
|
||||
onScroll(event.nativeEvent);
|
||||
}}
|
||||
ref={node => {
|
||||
maybeScrollViewNode = node;
|
||||
}}>
|
||||
<View
|
||||
style={{width: 1, height: 2, top: 3}}
|
||||
ref={node => {
|
||||
maybeNode = node;
|
||||
}}
|
||||
/>
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
const scrollViewElement = ensureInstance(
|
||||
maybeScrollViewNode,
|
||||
ReactNativeElement,
|
||||
);
|
||||
|
||||
Fantom.scrollTo(scrollViewElement, {
|
||||
x: 0,
|
||||
y: 1,
|
||||
});
|
||||
|
||||
expect(onScroll).toHaveBeenCalledTimes(1);
|
||||
|
||||
const viewElement = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
let rect;
|
||||
|
||||
viewElement.measure((x, y, width, height, pageX, pageY) => {
|
||||
rect = {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
pageX,
|
||||
pageY,
|
||||
};
|
||||
});
|
||||
|
||||
expect(rect).toEqual({
|
||||
x: 0,
|
||||
y: 3,
|
||||
width: 1,
|
||||
height: 2,
|
||||
pageY: 2,
|
||||
pageX: 0,
|
||||
});
|
||||
|
||||
const boundingClientRect = viewElement.getBoundingClientRect();
|
||||
expect(boundingClientRect.x).toBe(0);
|
||||
expect(boundingClientRect.y).toBe(2);
|
||||
expect(boundingClientRect.width).toBe(1);
|
||||
expect(boundingClientRect.height).toBe(2);
|
||||
|
||||
root.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('flushAllNativeEvents', () => {
|
||||
it('calls events in the event queue', () => {
|
||||
const root = Fantom.createRoot();
|
||||
|
||||
+44
-1
@@ -229,14 +229,56 @@ function dispatchNativeEvent(
|
||||
runWorkLoop();
|
||||
}
|
||||
|
||||
export type ScrollEventOptions = {
|
||||
x: number,
|
||||
y: number,
|
||||
zoomScale?: number,
|
||||
};
|
||||
|
||||
function enqueueScrollEvent(
|
||||
node: ReactNativeElement,
|
||||
options: {x: number, y: number, zoomScale?: number},
|
||||
options: ScrollEventOptions,
|
||||
) {
|
||||
const shadowNode = getNativeNodeReference(node);
|
||||
NativeFantom.enqueueScrollEvent(shadowNode, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the specified ScrollView node to the given coordinates on the UI thread.
|
||||
* The call is immediately observable unlike `Fantom.enqueueScrollEvent` where the
|
||||
* event is queued and not processed.
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* const root = Fantom.createRoot();
|
||||
* let maybeScrollViewNode;
|
||||
*
|
||||
* Fantom.runTask(() => {
|
||||
* root.render(
|
||||
* <ScrollView
|
||||
* ref={node => {
|
||||
* maybeScrollViewNode = node;
|
||||
* }} />
|
||||
* <ScrollViewContent />
|
||||
* </ScrollView>,
|
||||
* );
|
||||
* });
|
||||
*
|
||||
* const element = ensureInstance(maybeScrollViewNode, ReactNativeElement);
|
||||
*
|
||||
* Fantom.scrollTo(element, {x: 0, y: 20});
|
||||
*
|
||||
* // Assert that changes from Fantom.scrollTo are in effect.
|
||||
* ```
|
||||
*/
|
||||
function scrollTo(node: ReactNativeElement, options: ScrollEventOptions) {
|
||||
runOnUIThread(() => {
|
||||
enqueueScrollEvent(node, options);
|
||||
});
|
||||
|
||||
runWorkLoop();
|
||||
}
|
||||
|
||||
function enqueueModalSizeUpdate(
|
||||
node: ReactNativeElement,
|
||||
size: {width: number, height: number},
|
||||
@@ -347,5 +389,6 @@ export default {
|
||||
enqueueModalSizeUpdate,
|
||||
unstable_benchmark: Benchmark,
|
||||
enqueueScrollEvent,
|
||||
scrollTo,
|
||||
saveJSMemoryHeapSnapshot,
|
||||
};
|
||||
|
||||
Vendored
+58
-115
@@ -49,13 +49,10 @@ test('basic culling', () => {
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 60,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 60,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "child"}',
|
||||
@@ -65,13 +62,10 @@ test('basic culling', () => {
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -132,26 +126,21 @@ test('recursive culling', () => {
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
// === Scroll down to the edge of child AA ===
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 30,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 30,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
]);
|
||||
|
||||
// === Scroll down past child AA ===
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 36,
|
||||
});
|
||||
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 36,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -160,13 +149,10 @@ test('recursive culling', () => {
|
||||
]);
|
||||
|
||||
// === Scroll down past child AB ===
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 51,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 51,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -175,13 +161,10 @@ test('recursive culling', () => {
|
||||
]);
|
||||
|
||||
// === Scroll down past element A ===
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 56,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 56,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -190,13 +173,10 @@ test('recursive culling', () => {
|
||||
]);
|
||||
|
||||
// Scroll element B into viewport. Just child BA should be created.
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 155,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 155,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -207,13 +187,10 @@ test('recursive culling', () => {
|
||||
]);
|
||||
|
||||
// Scroll child BA into viewport.
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 165,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 165,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -222,13 +199,10 @@ test('recursive culling', () => {
|
||||
]);
|
||||
|
||||
// Scroll back to start
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -247,13 +221,10 @@ test('recursive culling', () => {
|
||||
]);
|
||||
|
||||
// Scroll past element A
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 85,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 85,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -303,13 +274,10 @@ test('recursive culling when initial offset is negative', () => {
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -373,13 +341,10 @@ test('deep nesting', () => {
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 40,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 40,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -391,13 +356,10 @@ test('deep nesting', () => {
|
||||
'Insert {type: "View", parentNativeID: (N/A), index: 1, nativeID: "element B"}',
|
||||
]);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 150,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 150,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -542,13 +504,10 @@ test('initial render', () => {
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 100,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 100,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -621,13 +580,10 @@ test('basic culling smaller ScrollView', () => {
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 11,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 11,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "element 1"}',
|
||||
@@ -691,13 +647,10 @@ test('culling with transform move', () => {
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 1,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 1,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -743,13 +696,10 @@ test('culling with recursive transform move', () => {
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 1,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 1,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
@@ -794,13 +744,10 @@ test('culling with transform scale', () => {
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 121,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 121,
|
||||
});
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "child"}',
|
||||
@@ -874,15 +821,11 @@ test('view flattening with culling', () => {
|
||||
|
||||
const element = ensureInstance(maybeNode, ReactNativeElement);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(element, {
|
||||
x: 0,
|
||||
y: 60,
|
||||
});
|
||||
Fantom.scrollTo(element, {
|
||||
x: 0,
|
||||
y: 60,
|
||||
});
|
||||
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(root.takeMountingManagerLogs()).toEqual([
|
||||
'Update {type: "ScrollView", nativeID: (N/A)}',
|
||||
'Create {type: "View", nativeID: "child"}',
|
||||
|
||||
Reference in New Issue
Block a user