mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Allow passing refs where nodes are expected (#54221)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/54221 Changelog: [internal] Small change in the Fantom API to accept refs and reduce boilerplate. See next diff for examples Reviewed By: javache Differential Revision: D85143177 fbshipit-source-id: fd65abcc1b107df8726fee172b284f5dc96e88a6
This commit is contained in:
committed by
meta-codesync[bot]
parent
bd004e0c12
commit
e58bbbcd8e
@@ -584,6 +584,31 @@ describe('Fantom', () => {
|
||||
expect(focusEvent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('sends event without payload (with ref)', () => {
|
||||
const root = Fantom.createRoot();
|
||||
|
||||
let focusEvent = jest.fn();
|
||||
|
||||
const ref = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(<TextInput onFocus={focusEvent} ref={ref} />);
|
||||
});
|
||||
|
||||
expect(focusEvent).toHaveBeenCalledTimes(0);
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueNativeEvent(ref, 'focus');
|
||||
});
|
||||
|
||||
// The tasks have not run.
|
||||
expect(focusEvent).toHaveBeenCalledTimes(0);
|
||||
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(focusEvent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('sends event with payload', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const ref = createRef<HostInstance>();
|
||||
@@ -692,6 +717,23 @@ describe('Fantom', () => {
|
||||
|
||||
expect(focusEvent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('flushes the event and runs the work loop (with ref)', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const ref = createRef<HostInstance>();
|
||||
|
||||
let focusEvent = jest.fn();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(<TextInput onFocus={focusEvent} ref={ref} />);
|
||||
});
|
||||
|
||||
expect(focusEvent).toHaveBeenCalledTimes(0);
|
||||
|
||||
Fantom.dispatchNativeEvent(ref, 'focus');
|
||||
|
||||
expect(focusEvent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('enqueueScrollEvent', () => {
|
||||
@@ -783,6 +825,36 @@ describe('Fantom', () => {
|
||||
|
||||
root.destroy();
|
||||
});
|
||||
|
||||
it('delivers onScroll event (with ref)', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const viewRef = createRef<HostInstance>();
|
||||
const scrollViewRef = createRef<HostInstance>();
|
||||
const onScroll = jest.fn();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView
|
||||
onScroll={event => {
|
||||
onScroll(event.nativeEvent);
|
||||
}}
|
||||
ref={scrollViewRef}>
|
||||
<View style={{width: 1, height: 2, top: 3}} ref={viewRef} />
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueScrollEvent(scrollViewRef, {
|
||||
x: 0,
|
||||
y: 1,
|
||||
});
|
||||
});
|
||||
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
expect(onScroll).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('scrollTo', () => {
|
||||
@@ -872,6 +944,39 @@ describe('Fantom', () => {
|
||||
|
||||
root.destroy();
|
||||
});
|
||||
|
||||
it('delivers onScroll event and affects position of elements on screen (with ref)', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const scrollViewRef = createRef<HostInstance>();
|
||||
const viewRef = createRef<HostInstance>();
|
||||
const onScroll = jest.fn();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<ScrollView
|
||||
onScroll={event => {
|
||||
onScroll(event.nativeEvent);
|
||||
}}
|
||||
ref={scrollViewRef}>
|
||||
<View style={{width: 1, height: 2, top: 3}} ref={viewRef} />
|
||||
</ScrollView>,
|
||||
);
|
||||
});
|
||||
|
||||
const scrollViewElement = ensureInstance(
|
||||
scrollViewRef.current,
|
||||
ReactNativeElement,
|
||||
);
|
||||
|
||||
expect(scrollViewElement.scrollTop).toBe(0);
|
||||
|
||||
Fantom.scrollTo(scrollViewRef, {
|
||||
x: 0,
|
||||
y: 1,
|
||||
});
|
||||
|
||||
expect(scrollViewElement.scrollTop).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('flushAllNativeEvents', () => {
|
||||
@@ -956,5 +1061,37 @@ describe('Fantom', () => {
|
||||
expect(boundingClientRect.height).toBe(25);
|
||||
expect(boundingClientRect.width).toBe(50);
|
||||
});
|
||||
|
||||
it('change size of <Modal /> (with ref)', () => {
|
||||
const root = Fantom.createRoot();
|
||||
const modalNodeRef = createRef<HostInstance>();
|
||||
const viewNodeRef = createRef<HostInstance>();
|
||||
|
||||
Fantom.runTask(() => {
|
||||
root.render(
|
||||
<Modal ref={modalNodeRef}>
|
||||
<View style={{width: '50%', height: '25%'}} ref={viewNodeRef} />
|
||||
</Modal>,
|
||||
);
|
||||
});
|
||||
|
||||
Fantom.runOnUIThread(() => {
|
||||
Fantom.enqueueModalSizeUpdate(modalNodeRef, {
|
||||
width: 100,
|
||||
height: 100,
|
||||
});
|
||||
});
|
||||
|
||||
Fantom.runWorkLoop();
|
||||
|
||||
const viewElement = ensureInstance(
|
||||
viewNodeRef.current,
|
||||
ReactNativeElement,
|
||||
);
|
||||
|
||||
const boundingClientRect = viewElement.getBoundingClientRect();
|
||||
expect(boundingClientRect.height).toBe(25);
|
||||
expect(boundingClientRect.width).toBe(50);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+57
-34
@@ -15,7 +15,6 @@ import type {
|
||||
import type {MixedElement} from 'react';
|
||||
import type {RootTag} from 'react-native';
|
||||
import type ReactNativeDocument from 'react-native/src/private/webapis/dom/nodes/ReactNativeDocument';
|
||||
import type ReadOnlyNode from 'react-native/src/private/webapis/dom/nodes/ReadOnlyNode';
|
||||
|
||||
import * as Benchmark from './Benchmark';
|
||||
import {getConstants} from './Constants';
|
||||
@@ -25,11 +24,14 @@ import NativeFantom, {
|
||||
NativeEventCategory,
|
||||
} from 'react-native/src/private/testing/fantom/specs/NativeFantom';
|
||||
import {getNativeNodeReference} from 'react-native/src/private/webapis/dom/nodes/internals/NodeInternals';
|
||||
import ReadOnlyNode from 'react-native/src/private/webapis/dom/nodes/ReadOnlyNode';
|
||||
|
||||
const nativeRuntimeScheduler = global.nativeRuntimeScheduler;
|
||||
const {unstable_scheduleCallback, unstable_ImmediatePriority} =
|
||||
nativeRuntimeScheduler;
|
||||
|
||||
type NodeOrRef = ReadOnlyNode | $ReadOnly<{current: ?ReadOnlyNode}>;
|
||||
|
||||
export type RootConfig = {
|
||||
viewportWidth?: number,
|
||||
viewportHeight?: number,
|
||||
@@ -218,17 +220,19 @@ export function unstable_produceFramesForDuration(milliseconds: number) {
|
||||
* Note: This API is marked as unstable and may change in future versions.
|
||||
*/
|
||||
export function unstable_getDirectManipulationProps(
|
||||
node: ReadOnlyNode,
|
||||
nodeOrRef: NodeOrRef,
|
||||
): $ReadOnly<{
|
||||
[string]: mixed,
|
||||
}> {
|
||||
const node = getNode(nodeOrRef);
|
||||
const shadowNode = getNativeNodeReference(node);
|
||||
return NativeFantom.getDirectManipulationProps(shadowNode);
|
||||
}
|
||||
|
||||
export function unstable_getFabricUpdateProps(node: ReadOnlyNode): $ReadOnly<{
|
||||
export function unstable_getFabricUpdateProps(nodeOrRef: NodeOrRef): $ReadOnly<{
|
||||
[string]: mixed,
|
||||
}> {
|
||||
const node = getNode(nodeOrRef);
|
||||
const shadowNode = getNativeNodeReference(node);
|
||||
return NativeFantom.getFabricUpdateProps(shadowNode);
|
||||
}
|
||||
@@ -394,11 +398,12 @@ export function createRoot(rootConfig?: RootConfig): Root {
|
||||
* ```
|
||||
*/
|
||||
export function enqueueNativeEvent(
|
||||
node: ReadOnlyNode,
|
||||
nodeOrRef: NodeOrRef,
|
||||
type: string,
|
||||
payload?: $ReadOnly<{[key: string]: mixed}>,
|
||||
options?: $ReadOnly<{category?: NativeEventCategory, isUnique?: boolean}>,
|
||||
) {
|
||||
const node = getNode(nodeOrRef);
|
||||
const shadowNode = getNativeNodeReference(node);
|
||||
NativeFantom.enqueueNativeEvent(
|
||||
shadowNode,
|
||||
@@ -425,11 +430,13 @@ export function enqueueNativeEvent(
|
||||
* ```
|
||||
*/
|
||||
export function dispatchNativeEvent(
|
||||
node: ReadOnlyNode,
|
||||
nodeOrRef: NodeOrRef,
|
||||
type: string,
|
||||
payload?: $ReadOnly<{[key: string]: mixed}>,
|
||||
options?: $ReadOnly<{category?: NativeEventCategory, isUnique?: boolean}>,
|
||||
) {
|
||||
const node = getNode(nodeOrRef);
|
||||
|
||||
runOnUIThread(() => {
|
||||
enqueueNativeEvent(node, type, payload, options);
|
||||
});
|
||||
@@ -487,9 +494,10 @@ export type ScrollEventOptions = {
|
||||
* ```
|
||||
*/
|
||||
export function enqueueScrollEvent(
|
||||
node: ReadOnlyNode,
|
||||
nodeOrRef: NodeOrRef,
|
||||
options: ScrollEventOptions,
|
||||
) {
|
||||
const node = getNode(nodeOrRef);
|
||||
const shadowNode = getNativeNodeReference(node);
|
||||
NativeFantom.enqueueScrollEvent(shadowNode, options);
|
||||
}
|
||||
@@ -525,7 +533,9 @@ export function enqueueScrollEvent(
|
||||
* // Assert that changes from Fantom.scrollTo are in effect.
|
||||
* ```
|
||||
*/
|
||||
export function scrollTo(node: ReadOnlyNode, options: ScrollEventOptions) {
|
||||
export function scrollTo(nodeOrRef: NodeOrRef, options: ScrollEventOptions) {
|
||||
const node = getNode(nodeOrRef);
|
||||
|
||||
runOnUIThread(() => {
|
||||
enqueueScrollEvent(node, options);
|
||||
});
|
||||
@@ -558,9 +568,10 @@ export function scrollTo(node: ReadOnlyNode, options: ScrollEventOptions) {
|
||||
* ```
|
||||
*/
|
||||
export function enqueueModalSizeUpdate(
|
||||
node: ReadOnlyNode,
|
||||
nodeOrRef: NodeOrRef,
|
||||
size: $ReadOnly<{width: number, height: number}>,
|
||||
) {
|
||||
const node = getNode(nodeOrRef);
|
||||
const shadowNode = getNativeNodeReference(node);
|
||||
NativeFantom.enqueueModalSizeUpdate(shadowNode, size.width, size.height);
|
||||
}
|
||||
@@ -572,28 +583,6 @@ export type {
|
||||
TestOptions as BenchmarkTestOptions,
|
||||
} from './Benchmark';
|
||||
|
||||
/**
|
||||
* Quick and dirty polyfills required by tinybench.
|
||||
*/
|
||||
|
||||
if (typeof global.Event === 'undefined') {
|
||||
global.Event =
|
||||
require('react-native/src/private/webapis/dom/events/Event').default;
|
||||
} else {
|
||||
console.warn(
|
||||
'The global Event class is already defined. If this API is already defined by React Native, you might want to remove this logic.',
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof global.EventTarget === 'undefined') {
|
||||
global.EventTarget =
|
||||
require('react-native/src/private/webapis/dom/events/EventTarget').default;
|
||||
} else {
|
||||
console.warn(
|
||||
'The global Event class is already defined. If this API is already defined by React Native, you might want to remove this logic.',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function that returns the current reference count for the supplied
|
||||
* element's shadow node. If the reference count is zero, that means the shadow
|
||||
@@ -602,9 +591,10 @@ if (typeof global.EventTarget === 'undefined') {
|
||||
* @param node The node for which to create a reference counting function.
|
||||
*/
|
||||
export function createShadowNodeReferenceCounter(
|
||||
node: ReadOnlyNode,
|
||||
nodeOrRef: NodeOrRef,
|
||||
): () => number {
|
||||
let shadowNode = getNativeNodeReference(node);
|
||||
const node = getNode(nodeOrRef);
|
||||
const shadowNode = getNativeNodeReference(node);
|
||||
return NativeFantom.createShadowNodeReferenceCounter(shadowNode);
|
||||
}
|
||||
|
||||
@@ -615,9 +605,10 @@ export function createShadowNodeReferenceCounter(
|
||||
* @param node The node for which to create a revision getter.
|
||||
*/
|
||||
export function createShadowNodeRevisionGetter(
|
||||
node: ReadOnlyNode,
|
||||
nodeOrRef: NodeOrRef,
|
||||
): () => ?number {
|
||||
let shadowNode = getNativeNodeReference(node);
|
||||
const node = getNode(nodeOrRef);
|
||||
const shadowNode = getNativeNodeReference(node);
|
||||
return NativeFantom.createShadowNodeRevisionGetter(shadowNode);
|
||||
}
|
||||
|
||||
@@ -681,4 +672,36 @@ function runLogBoxCheck() {
|
||||
}
|
||||
}
|
||||
|
||||
function getNode(nodeOrRef: NodeOrRef): ReadOnlyNode {
|
||||
if (nodeOrRef instanceof ReadOnlyNode) {
|
||||
return nodeOrRef;
|
||||
} else if (nodeOrRef.current != null) {
|
||||
return nodeOrRef.current;
|
||||
} else {
|
||||
throw new TypeError('Could not get node from ref');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick and dirty polyfills required by tinybench.
|
||||
*/
|
||||
|
||||
if (typeof global.Event === 'undefined') {
|
||||
global.Event =
|
||||
require('react-native/src/private/webapis/dom/events/Event').default;
|
||||
} else {
|
||||
console.warn(
|
||||
'The global Event class is already defined. If this API is already defined by React Native, you might want to remove this logic.',
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof global.EventTarget === 'undefined') {
|
||||
global.EventTarget =
|
||||
require('react-native/src/private/webapis/dom/events/EventTarget').default;
|
||||
} else {
|
||||
console.warn(
|
||||
'The global Event class is already defined. If this API is already defined by React Native, you might want to remove this logic.',
|
||||
);
|
||||
}
|
||||
|
||||
global.__FANTOM_PACKAGE_LOADED__ = true;
|
||||
|
||||
Reference in New Issue
Block a user