Skip to content

Commit

Permalink
introduce Fantom.dispatchNativeEvent (#48793)
Browse files Browse the repository at this point in the history
Summary:
changelog: [internal]

Pull Request resolved: #48793

Adds new method `dispatchNativeEvent` to Fantom give option to dispatch fake native events.

The API is expanded in subsequent diffs. The version introduced in this diff supports only dispatching event without payload and without any options.

Reviewed By: rubennorte

Differential Revision: D68331986

fbshipit-source-id: 075360e1d6874794ba6df966087fbe6a0a820cbc
  • Loading branch information
sammy-SC authored and facebook-github-bot committed Jan 22, 2025
1 parent a24f9ef commit ff0bcb2
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 3 deletions.
45 changes: 43 additions & 2 deletions packages/react-native-fantom/src/__tests__/Fantom-itest.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,15 @@ import 'react-native/Libraries/Core/InitializeCore';

import type {Root} from '..';

import {createRoot, runTask} from '..';
import {
createRoot,
dispatchNativeEvent,
runOnUIThread,
runTask,
runWorkLoop,
} from '..';
import * as React from 'react';
import {Text, View} from 'react-native';
import {Text, TextInput, View} from 'react-native';
import ensureInstance from 'react-native/src/private/utilities/ensureInstance';
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';

Expand Down Expand Up @@ -369,4 +375,39 @@ describe('Fantom', () => {
});
});
});

describe('runOnUIThread + dispatchNativeEvent', () => {
it('sends focus event', () => {
const root = createRoot();
let maybeNode;

let focusEvent = jest.fn();

runTask(() => {
root.render(
<TextInput
onFocus={focusEvent}
ref={node => {
maybeNode = node;
}}
/>,
);
});

const element = ensureInstance(maybeNode, ReactNativeElement);

expect(focusEvent).toHaveBeenCalledTimes(0);

runOnUIThread(() => {
dispatchNativeEvent(element, 'focus');
});

// The tasks have not run.
expect(focusEvent).toHaveBeenCalledTimes(0);

runWorkLoop();

expect(focusEvent).toHaveBeenCalledTimes(1);
});
});
});
17 changes: 16 additions & 1 deletion packages/react-native-fantom/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import type {
} from './getFantomRenderedOutput';
import type {MixedElement} from 'react';

import ReactNativeElement from '../../react-native/src/private/webapis/dom/nodes/ReadOnlyNode';
import {getShadowNode} from '../../react-native/src/private/webapis/dom/nodes/ReadOnlyNode';
import * as Benchmark from './Benchmark';
import getFantomRenderedOutput from './getFantomRenderedOutput';
import ReactFabric from 'react-native/Libraries/Renderer/shims/ReactFabric';
Expand Down Expand Up @@ -100,7 +102,7 @@ export function scheduleTask(task: () => void | Promise<void>) {
let flushingQueue = false;

/*
* Runs a task on on the event loop. To be used together with root.render.
* Runs a task on the event loop. To be used together with root.render.
*
* React must run inside of event loop to ensure scheduling environment is closer to production.
*/
Expand All @@ -115,6 +117,14 @@ export function runTask(task: () => void | Promise<void>) {
runWorkLoop();
}

/*
* Simmulates running a task on the UI thread and forces side effect to drain the event queue, dispatching events to JavaScript.
*/
export function runOnUIThread(task: () => void) {
task();
NativeFantom.flushEventQueue();
}

/**
* Runs the event loop until all tasks are executed.
*/
Expand All @@ -139,6 +149,11 @@ export function createRoot(rootConfig?: RootConfig): Root {
return new Root(rootConfig);
}

export function dispatchNativeEvent(node: ReactNativeElement, type: string) {
const shadowNode = getShadowNode(node);
NativeFantom.dispatchNativeEvent(shadowNode, type);
}

export const unstable_benchmark = Benchmark;

type FantomConstants = $ReadOnly<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10916,8 +10916,10 @@ interface Spec extends TurboModule {
devicePixelRatio: number
) => void;
stopSurface: (surfaceId: number) => void;
dispatchNativeEvent: (shadowNode: mixed, type: string) => void;
getMountingManagerLogs: (surfaceId: number) => Array<string>;
flushMessageQueue: () => void;
flushEventQueue: () => void;
getRenderedOutput: (surfaceId: number, config: RenderFormatOptions) => string;
reportTestSuiteResultsJSON: (results: string) => void;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <react/renderer/dom/DOM.h>
#include <react/renderer/uimanager/PointerEventsProcessor.h>
#include <react/renderer/uimanager/UIManagerBinding.h>
#include <react/renderer/uimanager/primitives.h>

#ifdef RN_DISABLE_OSS_PLUGIN_HEADER
#include "Plugins.h"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ interface Spec extends TurboModule {
devicePixelRatio: number,
) => void;
stopSurface: (surfaceId: number) => void;
dispatchNativeEvent: (
shadowNode: mixed /* ShadowNode */,
type: string,
) => void;
getMountingManagerLogs: (surfaceId: number) => Array<string>;
flushMessageQueue: () => void;
flushEventQueue: () => void;
getRenderedOutput: (surfaceId: number, config: RenderFormatOptions) => string;
reportTestSuiteResultsJSON: (results: string) => void;
}
Expand Down

0 comments on commit ff0bcb2

Please sign in to comment.