mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Implement compatibility methods in ReactNativeElement and use it behind a flag
Summary: This implements all compatibility methods from `ReactFabricHostComponent` into `ReactNativeElement` and uses the feature flag `enableAccessToHostTreeInFabric` to choose between them at runtime. This flag is disabled everywhere so this won't have an effect for now. Both these classes implement the NativeMethods interface, so they're interchangeable as refs in existing product code. Changelog: [internal] bypass-github-export-checks Reviewed By: yungsters Differential Revision: D44304435 fbshipit-source-id: 43b5525b943e385287d7b00107147884b859c1fd
This commit is contained in:
committed by
Facebook GitHub Bot
parent
fbb3a9eda8
commit
130a951e2e
+86
-12
@@ -5,22 +5,55 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow strict
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
// flowlint unsafe-getters-setters:off
|
||||
|
||||
import type {
|
||||
HostComponent,
|
||||
INativeMethods,
|
||||
InternalInstanceHandle,
|
||||
MeasureInWindowOnSuccessCallback,
|
||||
MeasureLayoutOnSuccessCallback,
|
||||
MeasureOnSuccessCallback,
|
||||
ViewConfig,
|
||||
} from '../../Renderer/shims/ReactNativeTypes';
|
||||
import type {ElementRef} from 'react';
|
||||
|
||||
import TextInputState from '../../Components/TextInput/TextInputState';
|
||||
import {getFabricUIManager} from '../../ReactNative/FabricUIManager';
|
||||
import {create as createAttributePayload} from '../../ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload';
|
||||
import warnForStyleProps from '../../ReactNative/ReactFabricPublicInstance/warnForStyleProps';
|
||||
import ReadOnlyElement from './ReadOnlyElement';
|
||||
import ReadOnlyNode from './ReadOnlyNode';
|
||||
import {getShadowNode} from './ReadOnlyNode';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
export default class ReactNativeElement
|
||||
extends ReadOnlyElement
|
||||
implements INativeMethods
|
||||
{
|
||||
// These need to be accessible from `ReactFabricPublicInstanceUtils`.
|
||||
__nativeTag: number;
|
||||
__internalInstanceHandle: InternalInstanceHandle;
|
||||
|
||||
_viewConfig: ViewConfig;
|
||||
|
||||
constructor(
|
||||
tag: number,
|
||||
viewConfig: ViewConfig,
|
||||
internalInstanceHandle: InternalInstanceHandle,
|
||||
) {
|
||||
super(internalInstanceHandle);
|
||||
|
||||
this.__nativeTag = tag;
|
||||
this.__internalInstanceHandle = internalInstanceHandle;
|
||||
this._viewConfig = viewConfig;
|
||||
}
|
||||
|
||||
export default class ReactNativeElement extends ReadOnlyElement {
|
||||
get offsetHeight(): number {
|
||||
throw new TypeError('Unimplemented');
|
||||
}
|
||||
@@ -46,30 +79,71 @@ export default class ReactNativeElement extends ReadOnlyElement {
|
||||
*/
|
||||
|
||||
blur(): void {
|
||||
throw new TypeError('Unimplemented');
|
||||
// $FlowFixMe[incompatible-exact] Migrate all usages of `NativeMethods` to an interface to fix this.
|
||||
TextInputState.blurTextInput(this);
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
throw new TypeError('Unimplemented');
|
||||
focus() {
|
||||
// $FlowFixMe[incompatible-exact] Migrate all usages of `NativeMethods` to an interface to fix this.
|
||||
TextInputState.focusTextInput(this);
|
||||
}
|
||||
|
||||
measure(callback: MeasureOnSuccessCallback): void {
|
||||
throw new TypeError('Unimplemented');
|
||||
measure(callback: MeasureOnSuccessCallback) {
|
||||
const node = getShadowNode(this);
|
||||
if (node != null) {
|
||||
nullthrows(getFabricUIManager()).measure(node, callback);
|
||||
}
|
||||
}
|
||||
|
||||
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void {
|
||||
throw new TypeError('Unimplemented');
|
||||
measureInWindow(callback: MeasureInWindowOnSuccessCallback) {
|
||||
const node = getShadowNode(this);
|
||||
if (node != null) {
|
||||
nullthrows(getFabricUIManager()).measureInWindow(node, callback);
|
||||
}
|
||||
}
|
||||
|
||||
measureLayout(
|
||||
relativeToNativeNode: number | ElementRef<HostComponent<mixed>>,
|
||||
onSuccess: MeasureLayoutOnSuccessCallback,
|
||||
onFail?: () => void /* currently unused */,
|
||||
): void {
|
||||
throw new TypeError('Unimplemented');
|
||||
) {
|
||||
if (!(relativeToNativeNode instanceof ReadOnlyNode)) {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'Warning: ref.measureLayout must be called with a ref to a native component.',
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const toStateNode = getShadowNode(this);
|
||||
const fromStateNode = getShadowNode(relativeToNativeNode);
|
||||
|
||||
if (toStateNode != null && fromStateNode != null) {
|
||||
nullthrows(getFabricUIManager()).measureLayout(
|
||||
toStateNode,
|
||||
fromStateNode,
|
||||
onFail != null ? onFail : noop,
|
||||
onSuccess != null ? onSuccess : noop,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setNativeProps(nativeProps: {...}): void {
|
||||
throw new TypeError('Unimplemented');
|
||||
if (__DEV__) {
|
||||
warnForStyleProps(nativeProps, this._viewConfig.validAttributes);
|
||||
}
|
||||
|
||||
const updatePayload = createAttributePayload(
|
||||
nativeProps,
|
||||
this._viewConfig.validAttributes,
|
||||
);
|
||||
|
||||
const node = getShadowNode(this);
|
||||
|
||||
if (node != null && updatePayload != null) {
|
||||
nullthrows(getFabricUIManager()).setNativeProps(node, updatePayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow strict
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
// flowlint unsafe-getters-setters:off
|
||||
|
||||
+30
-1
@@ -5,15 +5,25 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow strict
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
// flowlint unsafe-getters-setters:off
|
||||
|
||||
import type {
|
||||
InternalInstanceHandle,
|
||||
Node as ShadowNode,
|
||||
} from '../../Renderer/shims/ReactNativeTypes';
|
||||
import type NodeList from '../OldStyleCollections/NodeList';
|
||||
import type ReadOnlyElement from './ReadOnlyElement';
|
||||
|
||||
import ReactFabric from '../../Renderer/shims/ReactFabric';
|
||||
|
||||
export default class ReadOnlyNode {
|
||||
constructor(internalInstanceHandle: InternalInstanceHandle) {
|
||||
setInstanceHandle(this, internalInstanceHandle);
|
||||
}
|
||||
|
||||
get childNodes(): NodeList<ReadOnlyNode> {
|
||||
throw new TypeError('Unimplemented');
|
||||
}
|
||||
@@ -165,3 +175,22 @@ export default class ReadOnlyNode {
|
||||
*/
|
||||
static DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: number = 32;
|
||||
}
|
||||
|
||||
const INSTANCE_HANDLE_KEY = Symbol('internalInstanceHandle');
|
||||
|
||||
function getInstanceHandle(node: ReadOnlyNode): InternalInstanceHandle {
|
||||
// $FlowExpectedError[prop-missing]
|
||||
return node[INSTANCE_HANDLE_KEY];
|
||||
}
|
||||
|
||||
function setInstanceHandle(
|
||||
node: ReadOnlyNode,
|
||||
instanceHandle: InternalInstanceHandle,
|
||||
): void {
|
||||
// $FlowExpectedError[prop-missing]
|
||||
node[INSTANCE_HANDLE_KEY] = instanceHandle;
|
||||
}
|
||||
|
||||
export function getShadowNode(node: ReadOnlyNode): ?ShadowNode {
|
||||
return ReactFabric.getNodeFromInternalInstanceHandle(getInstanceHandle(node));
|
||||
}
|
||||
|
||||
Vendored
+1
-22
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
|
||||
import type {
|
||||
AttributeConfiguration,
|
||||
HostComponent,
|
||||
INativeMethods,
|
||||
InternalInstanceHandle,
|
||||
@@ -24,6 +23,7 @@ import TextInputState from '../../Components/TextInput/TextInputState';
|
||||
import {getNodeFromInternalInstanceHandle} from '../../Renderer/shims/ReactFabric';
|
||||
import {getFabricUIManager} from '../FabricUIManager';
|
||||
import {create} from './ReactNativeAttributePayload';
|
||||
import warnForStyleProps from './warnForStyleProps';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
const {
|
||||
@@ -149,24 +149,3 @@ export default class ReactFabricHostComponent implements INativeMethods {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function warnForStyleProps(
|
||||
props: {...},
|
||||
validAttributes: AttributeConfiguration,
|
||||
): void {
|
||||
if (__DEV__) {
|
||||
for (const key in validAttributes.style) {
|
||||
if (!(validAttributes[key] || props[key] === undefined)) {
|
||||
console.error(
|
||||
'You are setting the style `{ %s' +
|
||||
': ... }` as a prop. You ' +
|
||||
'should nest it in a style object. ' +
|
||||
'E.g. `{ style: { %s' +
|
||||
': ... } }`',
|
||||
key,
|
||||
key,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+24
-10
@@ -8,15 +8,22 @@
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
import type ReactNativeElement from '../../DOM/Nodes/ReactNativeElement';
|
||||
import typeof ReactFabricType from '../../Renderer/shims/ReactFabric';
|
||||
import type {
|
||||
InternalInstanceHandle,
|
||||
Node,
|
||||
ViewConfig,
|
||||
} from '../../Renderer/shims/ReactNativeTypes';
|
||||
import type ReactFabricHostComponentType from './ReactFabricHostComponent';
|
||||
import type ReactFabricHostComponent from './ReactFabricHostComponent';
|
||||
|
||||
import ReactNativeFeatureFlags from '../ReactNativeFeatureFlags';
|
||||
|
||||
// Lazy loaded to avoid evaluating the module when using the legacy renderer.
|
||||
let ReactFabricHostComponent: Class<ReactFabricHostComponentType>;
|
||||
let PublicInstanceClass:
|
||||
| Class<ReactFabricHostComponent>
|
||||
| Class<ReactNativeElement>;
|
||||
|
||||
// Lazy loaded to avoid evaluating the module when using the legacy renderer.
|
||||
let ReactFabric: ReactFabricType;
|
||||
|
||||
@@ -24,11 +31,19 @@ export function createPublicInstance(
|
||||
tag: number,
|
||||
viewConfig: ViewConfig,
|
||||
internalInstanceHandle: InternalInstanceHandle,
|
||||
): ReactFabricHostComponentType {
|
||||
if (ReactFabricHostComponent == null) {
|
||||
ReactFabricHostComponent = require('./ReactFabricHostComponent').default;
|
||||
): ReactFabricHostComponent | ReactNativeElement {
|
||||
if (PublicInstanceClass == null) {
|
||||
// We don't use inline requires in react-native, so this forces lazy loading
|
||||
// the right module to avoid eagerly loading both.
|
||||
if (ReactNativeFeatureFlags.enableAccessToHostTreeInFabric()) {
|
||||
PublicInstanceClass =
|
||||
require('../../DOM/Nodes/ReactNativeElement').default;
|
||||
} else {
|
||||
PublicInstanceClass = require('./ReactFabricHostComponent').default;
|
||||
}
|
||||
}
|
||||
return new ReactFabricHostComponent(tag, viewConfig, internalInstanceHandle);
|
||||
|
||||
return new PublicInstanceClass(tag, viewConfig, internalInstanceHandle);
|
||||
}
|
||||
|
||||
export function createPublicTextInstance(internalInstanceHandle: mixed): {} {
|
||||
@@ -39,14 +54,14 @@ export function createPublicTextInstance(internalInstanceHandle: mixed): {} {
|
||||
}
|
||||
|
||||
export function getNativeTagFromPublicInstance(
|
||||
publicInstance: ReactFabricHostComponentType,
|
||||
publicInstance: ReactFabricHostComponent | ReactNativeElement,
|
||||
): number {
|
||||
return publicInstance.__nativeTag;
|
||||
}
|
||||
|
||||
export function getNodeFromPublicInstance(
|
||||
publicInstance: ReactFabricHostComponentType,
|
||||
): mixed {
|
||||
publicInstance: ReactFabricHostComponent | ReactNativeElement,
|
||||
): ?Node {
|
||||
// Avoid loading ReactFabric if using an instance from the legacy renderer.
|
||||
if (publicInstance.__internalInstanceHandle == null) {
|
||||
return null;
|
||||
@@ -55,7 +70,6 @@ export function getNodeFromPublicInstance(
|
||||
if (ReactFabric == null) {
|
||||
ReactFabric = require('../../Renderer/shims/ReactFabric');
|
||||
}
|
||||
|
||||
return ReactFabric.getNodeFromInternalInstanceHandle(
|
||||
publicInstance.__internalInstanceHandle,
|
||||
);
|
||||
|
||||
+147
-137
@@ -15,13 +15,13 @@ import * as React from 'react';
|
||||
import {act} from 'react-test-renderer';
|
||||
|
||||
const TextInputState = require('../../../Components/TextInput/TextInputState');
|
||||
const FabricUIManager = require('../../../ReactNative/FabricUIManager');
|
||||
const ReactFabric = require('../../../Renderer/shims/ReactFabric');
|
||||
const ReactNativeViewConfigRegistry = require('../../../Renderer/shims/ReactNativeViewConfigRegistry');
|
||||
const FabricUIManager = require('../../FabricUIManager');
|
||||
const nullthrows = require('nullthrows');
|
||||
|
||||
jest.mock('../../../ReactNative/FabricUIManager', () =>
|
||||
require('../../../ReactNative/__mocks__/FabricUIManager'),
|
||||
jest.mock('../../FabricUIManager', () =>
|
||||
require('../../__mocks__/FabricUIManager'),
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -107,161 +107,171 @@ async function mockRenderKeys(
|
||||
return result;
|
||||
}
|
||||
|
||||
describe('ReactFabricPublicInstance', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
// Installs the global `nativeFabricUIManager` pointing to the mock.
|
||||
require('../../../ReactNative/__mocks__/FabricUIManager');
|
||||
jest.spyOn(TextInputState, 'blurTextInput');
|
||||
jest.spyOn(TextInputState, 'focusTextInput');
|
||||
});
|
||||
[
|
||||
{enableAccessToHostTreeInFabric: false},
|
||||
{enableAccessToHostTreeInFabric: true},
|
||||
].forEach(flags => {
|
||||
describe(`ReactFabricPublicInstance (ReactNativeFeatureFlags.enableAccessToHostTreeInFabric = ${String(
|
||||
flags.enableAccessToHostTreeInFabric,
|
||||
)})'`, () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
// Installs the global `nativeFabricUIManager` pointing to the mock.
|
||||
require('../../../ReactNative/__mocks__/FabricUIManager');
|
||||
jest.spyOn(TextInputState, 'blurTextInput');
|
||||
jest.spyOn(TextInputState, 'focusTextInput');
|
||||
|
||||
describe('blur', () => {
|
||||
test('blur() invokes TextInputState', async () => {
|
||||
const result = await mockRenderKeys([['foo']]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
|
||||
fooRef.blur();
|
||||
|
||||
expect(mockOf(TextInputState.blurTextInput).mock.calls).toEqual([
|
||||
[fooRef],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('focus', () => {
|
||||
test('focus() invokes TextInputState', async () => {
|
||||
const result = await mockRenderKeys([['foo']]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
|
||||
fooRef.focus();
|
||||
|
||||
expect(mockOf(TextInputState.focusTextInput).mock.calls).toEqual([
|
||||
[fooRef],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('measure', () => {
|
||||
test('component.measure(...) invokes callback', async () => {
|
||||
const result = await mockRenderKeys([['foo']]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
|
||||
const callback = jest.fn();
|
||||
fooRef.measure(callback);
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measure,
|
||||
).toHaveBeenCalledTimes(1);
|
||||
expect(callback.mock.calls).toEqual([[10, 10, 100, 100, 0, 0]]);
|
||||
require('../../../ReactNative/ReactNativeFeatureFlags').enableAccessToHostTreeInFabric =
|
||||
() => flags.enableAccessToHostTreeInFabric;
|
||||
});
|
||||
|
||||
test('unmounted.measure(...) does nothing', async () => {
|
||||
const result = await mockRenderKeys([['foo'], null]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
const callback = jest.fn();
|
||||
fooRef.measure(callback);
|
||||
describe('blur', () => {
|
||||
test('blur() invokes TextInputState', async () => {
|
||||
const result = await mockRenderKeys([['foo']]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measure,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
fooRef.blur();
|
||||
|
||||
describe('measureInWindow', () => {
|
||||
test('component.measureInWindow(...) invokes callback', async () => {
|
||||
const result = await mockRenderKeys([['foo']]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
|
||||
const callback = jest.fn();
|
||||
fooRef.measureInWindow(callback);
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measureInWindow,
|
||||
).toHaveBeenCalledTimes(1);
|
||||
expect(callback.mock.calls).toEqual([[10, 10, 100, 100]]);
|
||||
expect(mockOf(TextInputState.blurTextInput).mock.calls).toEqual([
|
||||
[fooRef],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('unmounted.measureInWindow(...) does nothing', async () => {
|
||||
const result = await mockRenderKeys([['foo'], null]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
describe('focus', () => {
|
||||
test('focus() invokes TextInputState', async () => {
|
||||
const result = await mockRenderKeys([['foo']]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
|
||||
const callback = jest.fn();
|
||||
fooRef.measureInWindow(callback);
|
||||
fooRef.focus();
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measureInWindow,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('measureLayout', () => {
|
||||
test('component.measureLayout(component, ...) invokes callback', async () => {
|
||||
const result = await mockRenderKeys([['foo', 'bar']]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
const barRef = nullthrows(result?.[0]?.[1]);
|
||||
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
fooRef.measureLayout(barRef, successCallback, failureCallback);
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measureLayout,
|
||||
).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback.mock.calls).toEqual([[1, 1, 100, 100]]);
|
||||
expect(mockOf(TextInputState.focusTextInput).mock.calls).toEqual([
|
||||
[fooRef],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('unmounted.measureLayout(component, ...) does nothing', async () => {
|
||||
const result = await mockRenderKeys([
|
||||
['foo', 'bar'],
|
||||
['foo', null],
|
||||
]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
const barRef = nullthrows(result?.[0]?.[1]);
|
||||
describe('measure', () => {
|
||||
test('component.measure(...) invokes callback', async () => {
|
||||
const result = await mockRenderKeys([['foo']]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
fooRef.measureLayout(barRef, successCallback, failureCallback);
|
||||
const callback = jest.fn();
|
||||
fooRef.measure(callback);
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measureLayout,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(successCallback).not.toHaveBeenCalled();
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measure,
|
||||
).toHaveBeenCalledTimes(1);
|
||||
expect(callback.mock.calls).toEqual([[10, 10, 100, 100, 0, 0]]);
|
||||
});
|
||||
|
||||
test('unmounted.measure(...) does nothing', async () => {
|
||||
const result = await mockRenderKeys([['foo'], null]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
const callback = jest.fn();
|
||||
fooRef.measure(callback);
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measure,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('component.measureLayout(unmounted, ...) does nothing', async () => {
|
||||
const result = await mockRenderKeys([
|
||||
['foo', 'bar'],
|
||||
[null, 'bar'],
|
||||
]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
const barRef = nullthrows(result?.[0]?.[1]);
|
||||
describe('measureInWindow', () => {
|
||||
test('component.measureInWindow(...) invokes callback', async () => {
|
||||
const result = await mockRenderKeys([['foo']]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
fooRef.measureLayout(barRef, successCallback, failureCallback);
|
||||
const callback = jest.fn();
|
||||
fooRef.measureInWindow(callback);
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measureLayout,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(successCallback).not.toHaveBeenCalled();
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measureInWindow,
|
||||
).toHaveBeenCalledTimes(1);
|
||||
expect(callback.mock.calls).toEqual([[10, 10, 100, 100]]);
|
||||
});
|
||||
|
||||
test('unmounted.measureInWindow(...) does nothing', async () => {
|
||||
const result = await mockRenderKeys([['foo'], null]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
|
||||
const callback = jest.fn();
|
||||
fooRef.measureInWindow(callback);
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measureInWindow,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('unmounted.measureLayout(unmounted, ...) does nothing', async () => {
|
||||
const result = await mockRenderKeys([['foo', 'bar'], null]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
const barRef = nullthrows(result?.[0]?.[1]);
|
||||
describe('measureLayout', () => {
|
||||
test('component.measureLayout(component, ...) invokes callback', async () => {
|
||||
const result = await mockRenderKeys([['foo', 'bar']]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
const barRef = nullthrows(result?.[0]?.[1]);
|
||||
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
fooRef.measureLayout(barRef, successCallback, failureCallback);
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
fooRef.measureLayout(barRef, successCallback, failureCallback);
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measureLayout,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(successCallback).not.toHaveBeenCalled();
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measureLayout,
|
||||
).toHaveBeenCalledTimes(1);
|
||||
expect(successCallback.mock.calls).toEqual([[1, 1, 100, 100]]);
|
||||
});
|
||||
|
||||
test('unmounted.measureLayout(component, ...) does nothing', async () => {
|
||||
const result = await mockRenderKeys([
|
||||
['foo', 'bar'],
|
||||
['foo', null],
|
||||
]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
const barRef = nullthrows(result?.[0]?.[1]);
|
||||
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
fooRef.measureLayout(barRef, successCallback, failureCallback);
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measureLayout,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(successCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('component.measureLayout(unmounted, ...) does nothing', async () => {
|
||||
const result = await mockRenderKeys([
|
||||
['foo', 'bar'],
|
||||
[null, 'bar'],
|
||||
]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
const barRef = nullthrows(result?.[0]?.[1]);
|
||||
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
fooRef.measureLayout(barRef, successCallback, failureCallback);
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measureLayout,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(successCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('unmounted.measureLayout(unmounted, ...) does nothing', async () => {
|
||||
const result = await mockRenderKeys([['foo', 'bar'], null]);
|
||||
const fooRef = nullthrows(result?.[0]?.[0]);
|
||||
const barRef = nullthrows(result?.[0]?.[1]);
|
||||
|
||||
const successCallback = jest.fn();
|
||||
const failureCallback = jest.fn();
|
||||
fooRef.measureLayout(barRef, successCallback, failureCallback);
|
||||
|
||||
expect(
|
||||
nullthrows(FabricUIManager.getFabricUIManager()).measureLayout,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(successCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and 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
|
||||
*/
|
||||
|
||||
import type {AttributeConfiguration} from '../../Renderer/shims/ReactNativeTypes';
|
||||
|
||||
export default function warnForStyleProps(
|
||||
props: {...},
|
||||
validAttributes: AttributeConfiguration,
|
||||
): void {
|
||||
if (__DEV__) {
|
||||
for (const key in validAttributes.style) {
|
||||
if (!(validAttributes[key] || props[key] === undefined)) {
|
||||
console.error(
|
||||
'You are setting the style `{ %s' +
|
||||
': ... }` as a prop. You ' +
|
||||
'should nest it in a style object. ' +
|
||||
'E.g. `{ style: { %s' +
|
||||
': ... } }`',
|
||||
key,
|
||||
key,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user