Fabric iOS: allow using fallback component for unsupported ones

Summary:
This allows an unsupported component to be rendered as a "unimplemented view" for better visualization of which component is missing. It is off by default, but configurable in the component factory.

For now, the layout simply follows regular <View />, which means the width/height etc is based on the react component styling. The side effect is that components with 0 height/width won't show up at all.

Reviewed By: mdvacca

Differential Revision: D14869656

fbshipit-source-id: f31e012fb7dc1c64fcc431ea5aa45079a23a618e
This commit is contained in:
Kevin Gozali 2019-04-10 11:00:50 -07:00 коммит произвёл Facebook Github Bot
Родитель 63343dad2e
Коммит 417adf526f
8 изменённых файлов: 182 добавлений и 4 удалений

Просмотреть файл

@ -0,0 +1,29 @@
/**
* Copyright (c) Facebook, Inc. and its 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
*/
'use strict';
const requireNativeComponent = require('requireNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {ViewStyleProp} from 'StyleSheet';
import type {NativeComponent} from 'ReactNative';
type NativeProps = $ReadOnly<{|
...ViewProps,
name?: ?string,
style?: ?ViewStyleProp,
|}>;
type UnimplementedViewNativeType = Class<NativeComponent<NativeProps>>;
module.exports = ((requireNativeComponent(
'UnimplementedNativeView',
): any): UnimplementedViewNativeType);

Просмотреть файл

@ -0,0 +1,43 @@
/**
* Copyright (c) Facebook, Inc. and its 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
*/
'use strict';
import type {SchemaType} from '../../../packages/react-native-codegen/src/CodegenSchema.js';
const UnimplementedNativeViewSchema: SchemaType = {
modules: {
UnimplementedNativeViewSchema: {
components: {
UnimplementedNativeView: {
extendsProps: [
{
type: 'ReactNativeBuiltInType',
knownTypeName: 'ReactNativeCoreViewProps',
},
],
events: [],
props: [
{
name: 'name',
optional: true,
typeAnnotation: {
type: 'StringTypeAnnotation',
default: '',
},
},
],
},
},
},
},
};
module.exports = UnimplementedNativeViewSchema;

Просмотреть файл

@ -0,0 +1,18 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTViewComponentView.h>
NS_ASSUME_NONNULL_BEGIN
@interface RCTUnimplementedNativeComponentView : RCTViewComponentView
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -0,0 +1,60 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTUnimplementedNativeComponentView.h"
#import <react/components/rncore/EventEmitters.h>
#import <react/components/rncore/Props.h>
#import <react/components/rncore/ShadowNodes.h>
using namespace facebook::react;
@implementation RCTUnimplementedNativeComponentView {
UILabel *_label;
}
#pragma mark - RCTComponentViewProtocol
+ (ComponentHandle)componentHandle
{
return UnimplementedNativeViewShadowNode::Handle();
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared<const UnimplementedNativeViewProps>();
_props = defaultProps;
CGRect bounds = self.bounds;
_label = [[UILabel alloc] initWithFrame:bounds];
_label.backgroundColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.3];
_label.layoutMargins = UIEdgeInsetsMake(12, 12, 12, 12);
_label.lineBreakMode = NSLineBreakByWordWrapping;
_label.numberOfLines = 0;
_label.textAlignment = NSTextAlignmentCenter;
_label.textColor = [UIColor whiteColor];
self.contentView = _label;
}
return self;
}
- (void)updateProps:(SharedProps)props oldProps:(SharedProps)oldProps
{
const auto &oldViewProps = *std::static_pointer_cast<const UnimplementedNativeViewProps>(oldProps ?: _props);
const auto &newViewProps = *std::static_pointer_cast<const UnimplementedNativeViewProps>(props);
[super updateProps:props oldProps:oldProps];
if (oldViewProps.name != newViewProps.name) {
_label.text = [NSString stringWithFormat:@"'%s' is not Fabric compatible yet.", newViewProps.name.c_str()];
}
}
@end

Просмотреть файл

@ -17,6 +17,7 @@
#import "RCTScrollViewComponentView.h"
#import "RCTSliderComponentView.h"
#import "RCTSwitchComponentView.h"
#import "RCTUnimplementedNativeComponentView.h"
#import "RCTViewComponentView.h"
using namespace facebook::react;
@ -39,6 +40,7 @@ using namespace facebook::react;
[componentViewFactory registerComponentViewClass:[RCTActivityIndicatorViewComponentView class]];
[componentViewFactory registerComponentViewClass:[RCTSliderComponentView class]];
[componentViewFactory registerComponentViewClass:[RCTSwitchComponentView class]];
[componentViewFactory registerComponentViewClass:[RCTUnimplementedNativeComponentView class]];
return componentViewFactory;
}

Просмотреть файл

@ -92,9 +92,12 @@ const ComponentDescriptor &ComponentDescriptorRegistry::at(
auto it = _registryByName.find(unifiedComponentName);
if (it == _registryByName.end()) {
throw std::invalid_argument(
("Unable to find componentDescriptor for " + unifiedComponentName)
.c_str());
if (_fallbackComponentDescriptor == nullptr) {
throw std::invalid_argument(
("Unable to find componentDescriptor for " + unifiedComponentName)
.c_str());
}
return *_fallbackComponentDescriptor.get();
}
return *it->second;
}
@ -123,5 +126,16 @@ SharedShadowNode ComponentDescriptorRegistry::createNode(
return shadowNode;
}
void ComponentDescriptorRegistry::setFallbackComponentDescriptor(
SharedComponentDescriptor descriptor) {
_fallbackComponentDescriptor = descriptor;
registerComponentDescriptor(descriptor);
}
const SharedComponentDescriptor
ComponentDescriptorRegistry::getFallbackComponentDescriptor() const {
return _fallbackComponentDescriptor;
}
} // namespace react
} // namespace facebook

Просмотреть файл

@ -39,10 +39,13 @@ class ComponentDescriptorRegistry {
Tag rootTag,
const folly::dynamic &props,
const SharedEventTarget &eventTarget) const;
void setFallbackComponentDescriptor(SharedComponentDescriptor descriptor);
const SharedComponentDescriptor getFallbackComponentDescriptor() const;
private:
better::map<ComponentHandle, SharedComponentDescriptor> _registryByHandle;
better::map<ComponentName, SharedComponentDescriptor> _registryByName;
SharedComponentDescriptor _fallbackComponentDescriptor;
};
} // namespace react

Просмотреть файл

@ -18,13 +18,22 @@ SharedShadowNode UIManager::createNode(
SystraceSection s("UIManager::createNode");
auto &componentDescriptor = componentDescriptorRegistry_->at(name);
auto fallbackDescriptor =
componentDescriptorRegistry_->getFallbackComponentDescriptor();
const auto &props = componentDescriptor.cloneProps(nullptr, rawProps);
const auto &state = componentDescriptor.createInitialState(props);
auto shadowNode = componentDescriptor.createShadowNode({
/* .tag = */ tag,
/* .rootTag = */ surfaceId,
/* .props = */ props,
/* .props = */
fallbackDescriptor != nullptr &&
fallbackDescriptor->getComponentHandle() ==
componentDescriptor.getComponentHandle()
? componentDescriptor.cloneProps(
nullptr, RawProps(folly::dynamic::object("name", name)))
: props,
/* .eventEmitter = */
componentDescriptor.createEventEmitter(std::move(eventTarget), tag),
/* .children = */ ShadowNodeFragment::childrenPlaceholder(),