Compare commits

...

2 Commits

Author SHA1 Message Date
Eli White
0e78755102 Prettier 2020-01-17 16:21:28 -08:00
Eli White
598739c2f4 [Native] Migrate focus/blur to call TextInputState with the host component 2020-01-17 16:04:28 -08:00
7 changed files with 272 additions and 17 deletions

View File

@@ -17,10 +17,7 @@ import type {
import invariant from 'shared/invariant';
// Modules provided by RN:
import {
TextInputState,
UIManager,
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
import {create} from './ReactNativeAttributePayload';
import {
@@ -279,14 +276,28 @@ export default function(
* will depend on the platform and type of view.
*/
focus: function() {
TextInputState.focusTextInput(findNodeHandle(this));
if (__DEV__) {
console.error(
'focus and blur are no longer supported on NativeMethodsMixin or ReactNative.NativeComponent. ' +
'Call focus and blur on a ref to a native component.',
);
}
return;
},
/**
* Removes focus from an input or view. This is the opposite of `focus()`.
*/
blur: function() {
TextInputState.blurTextInput(findNodeHandle(this));
if (__DEV__) {
console.error(
'focus and blur are no longer supported on NativeMethodsMixin or ReactNative.NativeComponent. ' +
'Call focus and blur on a ref to a native component.',
);
}
return;
},
};

View File

@@ -104,11 +104,11 @@ class ReactFabricHostComponent {
}
blur() {
TextInputState.blurTextInput(this._nativeTag);
TextInputState.blurTextInput(this);
}
focus() {
TextInputState.focusTextInput(this._nativeTag);
TextInputState.focusTextInput(this);
}
measure(callback: MeasureOnSuccessCallback) {

View File

@@ -18,10 +18,7 @@ import type {
import React from 'react';
// Modules provided by RN:
import {
TextInputState,
UIManager,
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
import {create} from './ReactNativeAttributePayload';
import {mountSafeCallback_NOT_REALLY_SAFE} from './NativeMethodsMixinUtils';
@@ -56,14 +53,28 @@ export default function(
* Removes focus. This is the opposite of `focus()`.
*/
blur(): void {
TextInputState.blurTextInput(findNodeHandle(this));
if (__DEV__) {
console.error(
'focus and blur are no longer supported on NativeMethodsMixin or ReactNative.NativeComponent. ' +
'Call focus and blur on a ref to a native component.',
);
}
return;
}
/**
* Requests focus. The exact behavior depends on the platform and view.
*/
focus(): void {
TextInputState.focusTextInput(findNodeHandle(this));
if (__DEV__) {
console.error(
'focus and blur are no longer supported on NativeMethodsMixin or ReactNative.NativeComponent. ' +
'Call focus and blur on a ref to a native component.',
);
}
return;
}
/**

View File

@@ -47,11 +47,11 @@ class ReactNativeFiberHostComponent {
}
blur() {
TextInputState.blurTextInput(this._nativeTag);
TextInputState.blurTextInput(this);
}
focus() {
TextInputState.focusTextInput(this._nativeTag);
TextInputState.focusTextInput(this);
}
measure(callback: MeasureOnSuccessCallback) {

View File

@@ -10,6 +10,9 @@
// Mock of the Native Hooks
// TODO: Should this move into the components themselves? E.g. focusable
const TextInputState = {};
const TextInputState = {
blurTextInput: jest.fn(),
focusTextInput: jest.fn(),
};
module.exports = TextInputState;

View File

@@ -18,6 +18,7 @@ let createReactNativeComponentClass;
let UIManager;
let StrictMode;
let NativeMethodsMixin;
let TextInputState;
const SET_NATIVE_PROPS_NOT_SUPPORTED_MESSAGE =
'Warning: setNativeProps is not currently supported in Fabric';
@@ -52,6 +53,8 @@ describe('ReactFabric', () => {
NativeMethodsMixin =
ReactFabric.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
.NativeMethodsMixin;
TextInputState = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
.TextInputState;
});
it('should be able to create and render a native component', () => {
@@ -1132,4 +1135,116 @@ describe('ReactFabric', () => {
]);
expect(match).toBe(child._nativeTag);
});
it('blur on host component calls TextInputState', () => {
const View = createReactNativeComponentClass('RCTView', () => ({
validAttributes: {foo: true},
uiViewClassName: 'RCTView',
}));
let viewRef = React.createRef();
ReactFabric.render(<View ref={viewRef} />, 11);
expect(TextInputState.blurTextInput).not.toBeCalled();
viewRef.current.blur();
expect(TextInputState.blurTextInput).toHaveBeenCalledTimes(1);
expect(TextInputState.blurTextInput).toHaveBeenCalledWith(viewRef.current);
});
it('focus on host component calls TextInputState', () => {
const View = createReactNativeComponentClass('RCTView', () => ({
validAttributes: {foo: true},
uiViewClassName: 'RCTView',
}));
let viewRef = React.createRef();
ReactFabric.render(<View ref={viewRef} />, 11);
expect(TextInputState.focusTextInput).not.toBeCalled();
viewRef.current.focus();
expect(TextInputState.focusTextInput).toHaveBeenCalledTimes(1);
expect(TextInputState.focusTextInput).toHaveBeenCalledWith(viewRef.current);
});
it('blur on JS components is deprecated', () => {
const View = createReactNativeComponentClass('RCTView', () => ({
validAttributes: {foo: true},
uiViewClassName: 'RCTView',
}));
class Subclass extends ReactFabric.NativeComponent {
render() {
return <View />;
}
}
const CreateClass = createReactClass({
mixins: [NativeMethodsMixin],
render: () => {
return <View />;
},
});
[Subclass, CreateClass].forEach(Component => {
TextInputState.blurTextInput.mockReset();
let viewRef = React.createRef();
ReactFabric.render(<Component ref={viewRef} />, 11);
expect(() => viewRef.current.blur()).toErrorDev(
[
'focus and blur are no longer supported on NativeMethodsMixin or ReactNative.NativeComponent. ' +
'Call focus and blur on a ref to a native component',
],
{
withoutStack: true,
},
);
expect(TextInputState.blurTextInput).not.toBeCalled();
});
});
it('focus on JS components is deprecated', () => {
const View = createReactNativeComponentClass('RCTView', () => ({
validAttributes: {foo: true},
uiViewClassName: 'RCTView',
}));
class Subclass extends ReactFabric.NativeComponent {
render() {
return <View />;
}
}
const CreateClass = createReactClass({
mixins: [NativeMethodsMixin],
render: () => {
return <View />;
},
});
[Subclass, CreateClass].forEach(Component => {
TextInputState.focusTextInput.mockReset();
let viewRef = React.createRef();
ReactFabric.render(<Component ref={viewRef} />, 11);
expect(() => viewRef.current.focus()).toErrorDev(
[
'focus and blur are no longer supported on NativeMethodsMixin or ReactNative.NativeComponent. ' +
'Call focus and blur on a ref to a native component',
],
{
withoutStack: true,
},
);
expect(TextInputState.focusTextInput).not.toBeCalled();
});
});
});

View File

@@ -17,6 +17,7 @@ let createReactClass;
let createReactNativeComponentClass;
let UIManager;
let NativeMethodsMixin;
let TextInputState;
const DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT =
"Warning: dispatchCommand was called with a ref that isn't a " +
@@ -41,6 +42,8 @@ describe('ReactNative', () => {
NativeMethodsMixin =
ReactNative.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
.NativeMethodsMixin;
TextInputState = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
.TextInputState;
});
it('should be able to create and render a native component', () => {
@@ -688,4 +691,116 @@ describe('ReactNative', () => {
]);
expect(match).toBe(child._nativeTag);
});
it('blur on host component calls TextInputState', () => {
const View = createReactNativeComponentClass('RCTView', () => ({
validAttributes: {foo: true},
uiViewClassName: 'RCTView',
}));
let viewRef = React.createRef();
ReactNative.render(<View ref={viewRef} />, 11);
expect(TextInputState.blurTextInput).not.toBeCalled();
viewRef.current.blur();
expect(TextInputState.blurTextInput).toHaveBeenCalledTimes(1);
expect(TextInputState.blurTextInput).toHaveBeenCalledWith(viewRef.current);
});
it('focus on host component calls TextInputState', () => {
const View = createReactNativeComponentClass('RCTView', () => ({
validAttributes: {foo: true},
uiViewClassName: 'RCTView',
}));
let viewRef = React.createRef();
ReactNative.render(<View ref={viewRef} />, 11);
expect(TextInputState.focusTextInput).not.toBeCalled();
viewRef.current.focus();
expect(TextInputState.focusTextInput).toHaveBeenCalledTimes(1);
expect(TextInputState.focusTextInput).toHaveBeenCalledWith(viewRef.current);
});
it('blur on JS components is deprecated', () => {
const View = createReactNativeComponentClass('RCTView', () => ({
validAttributes: {foo: true},
uiViewClassName: 'RCTView',
}));
class Subclass extends ReactNative.NativeComponent {
render() {
return <View />;
}
}
const CreateClass = createReactClass({
mixins: [NativeMethodsMixin],
render: () => {
return <View />;
},
});
[Subclass, CreateClass].forEach(Component => {
TextInputState.blurTextInput.mockReset();
let viewRef = React.createRef();
ReactNative.render(<Component ref={viewRef} />, 11);
expect(() => viewRef.current.blur()).toErrorDev(
[
'focus and blur are no longer supported on NativeMethodsMixin or ReactNative.NativeComponent. ' +
'Call focus and blur on a ref to a native component',
],
{
withoutStack: true,
},
);
expect(TextInputState.blurTextInput).not.toBeCalled();
});
});
it('focus on JS components is deprecated', () => {
const View = createReactNativeComponentClass('RCTView', () => ({
validAttributes: {foo: true},
uiViewClassName: 'RCTView',
}));
class Subclass extends ReactNative.NativeComponent {
render() {
return <View />;
}
}
const CreateClass = createReactClass({
mixins: [NativeMethodsMixin],
render: () => {
return <View />;
},
});
[Subclass, CreateClass].forEach(Component => {
TextInputState.focusTextInput.mockReset();
let viewRef = React.createRef();
ReactNative.render(<Component ref={viewRef} />, 11);
expect(() => viewRef.current.focus()).toErrorDev(
[
'focus and blur are no longer supported on NativeMethodsMixin or ReactNative.NativeComponent. ' +
'Call focus and blur on a ref to a native component',
],
{
withoutStack: true,
},
);
expect(TextInputState.focusTextInput).not.toBeCalled();
});
});
});