Add Example for InteropLayer on RNTester (#51260)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51260

This is a preparatory change that adds an example to RNTester for the Fabric interop layer on iOS.
This example is needed to create a Jest E2E tests that will make sure that the Fabric Interop Layer can properly add views as subviews.

We discovered the bug thanks to react-native-maps.

## Changelog:
[Internal] - Add Example for the Fabric Interop Layer to RNTester iOS

Reviewed By: cortinico

Differential Revision: D74579737

fbshipit-source-id: 0c1bbb06790b01313cd98aa4b7152d8aba0cded3
This commit is contained in:
Riccardo Cipolleschi
2025-05-14 10:11:03 -07:00
committed by Facebook GitHub Bot
parent 337ae3bacb
commit e9c8fee4ef
6 changed files with 221 additions and 0 deletions
@@ -0,0 +1,15 @@
/*
* 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.
*/
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface InteropTestView : UIView
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,18 @@
/*
* 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.
*/
#import "RCTInteropTestView.h"
@implementation InteropTestView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
return self;
}
@end
@@ -0,0 +1,14 @@
/*
* 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.
*/
#import <React/RCTViewManager.h>
NS_ASSUME_NONNULL_BEGIN
@interface InteropTestViewManager : RCTViewManager
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,20 @@
/*
* 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.
*/
#import "RCTInteropTestViewManager.h"
#import "RCTInteropTestView.h"
@implementation InteropTestViewManager
RCT_EXPORT_MODULE(InteropTestView)
- (UIView *)view
{
return [[InteropTestView alloc] init];
}
@end
@@ -0,0 +1,149 @@
/**
* 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
*/
'use strict';
import type {ViewProps} from 'react-native';
import React, {useState} from 'react';
import {
Button,
StyleSheet,
Text,
View,
requireNativeComponent,
useColorScheme,
} from 'react-native';
type SectionProps = {
title: string,
children?: React.Node,
};
const WHITE = '#ffffff';
const BLACK = '#000000';
// ========== JS Definition of the Native RCTInteropTestView component ========
type InteropTestViewProps = {
// Add custom props here if needed
...ViewProps,
};
const NativeInteropTestView =
requireNativeComponent<InteropTestViewProps>('InteropTestView');
const InteropTestView = (props: InteropTestViewProps) => {
return <NativeInteropTestView {...props} />;
};
// =============================================================================
function Section({children, title}: SectionProps): React.Node {
const isDarkMode = useColorScheme() === 'dark';
return (
<View style={styles.sectionContainer}>
<Text
style={[
styles.sectionTitle,
{
color: isDarkMode ? WHITE : BLACK,
},
]}>
{title}
</Text>
<Text
style={[
styles.sectionDescription,
{
color: isDarkMode ? WHITE : BLACK,
},
]}>
{children}
</Text>
</View>
);
}
function AddChildrenForInteropLayer() {
const isDarkMode = useColorScheme() === 'dark';
const [squares, setSquares] = useState<Array<number>>([0, 1, 2, 3, 4]);
const addMarker = () => {
setSquares(p => [...p, p.length + 1]);
};
return (
<View
style={{
backgroundColor: isDarkMode ? BLACK : WHITE,
}}>
<Section title="Squares">
<Button title="Add Marker" onPress={addMarker} />
<Text>{`Number of squares: ${squares.length}`}</Text>
</Section>
<Section title="Custom native view">
<InteropTestView style={styles.customView}>
{squares.map((_, index) => (
<View key={index} style={styles.customViewChild} />
))}
</InteropTestView>
</Section>
<Section title="Regular view">
<View style={styles.customView}>
{squares.map((_, index) => (
<View key={index} style={styles.customViewChild} />
))}
</View>
</Section>
</View>
);
}
const styles = StyleSheet.create({
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
},
highlight: {
fontWeight: '700',
},
customView: {
width: 300,
height: 200,
backgroundColor: 'yellow',
flexWrap: 'wrap',
gap: 10,
},
customViewChild: {
width: 50,
height: 50,
backgroundColor: 'blue',
},
});
exports.title = 'Fabric Interop Layer';
exports.category = 'UI';
exports.description = 'A set test cases for the Fabric Interop Layer.';
exports.examples = [
{
title: 'Add children to Interop Layer',
description: 'Add children to Interop Layer',
name: 'Add Children to interop layer',
render(): React.Node {
return <AddChildrenForInteropLayer />;
},
},
];
@@ -146,6 +146,11 @@ const Components: Array<RNTesterModuleInfo> = [
category: 'UI',
module: require('../examples/NewArchitecture/NewArchitectureExample'),
},
{
key: 'FabricInteropLayer',
category: 'UI',
module: require('../examples/FabricInteropLayer/FabricInteropLayer'),
},
{
key: 'PerformanceComparisonExample',
category: 'Basic',