Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add id prop to Text, TouchableWithoutFeedback and View components #34522

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Libraries/Components/Touchable/TouchableWithoutFeedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type Props = $ReadOnly<{|
disabled?: ?boolean,
focusable?: ?boolean,
hitSlop?: ?EdgeInsetsProp,
id?: string,
importantForAccessibility?: ?('auto' | 'yes' | 'no' | 'no-hide-descendants'),
nativeID?: ?string,
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
Expand Down Expand Up @@ -102,7 +103,6 @@ const PASSTHROUGH_PROPS = [
'accessibilityViewIsModal',
'hitSlop',
'importantForAccessibility',
'nativeID',
'onAccessibilityAction',
'onBlur',
'onFocus',
Expand Down Expand Up @@ -168,6 +168,7 @@ class TouchableWithoutFeedback extends React.Component<Props, State> {
ariaLive === 'off'
? 'none'
: ariaLive ?? this.props.accessibilityLiveRegion,
nativeID: this.props.id ?? this.props.nativeID,
};
for (const prop of PASSTHROUGH_PROPS) {
if (this.props[prop] !== undefined) {
Expand Down
3 changes: 3 additions & 0 deletions Libraries/Components/View/View.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ const View: React.AbstractComponent<
accessibilityRole,
'aria-hidden': ariaHidden,
focusable,
id,
importantForAccessibility,
nativeID,
pointerEvents,
role,
style,
Expand Down Expand Up @@ -161,6 +163,7 @@ const View: React.AbstractComponent<
? 'no-hide-descendants'
: importantForAccessibility
}
nativeID={id ?? nativeID}
{...restWithDefaultProps}
style={style}
pointerEvents={newPointerEvents}
Expand Down
9 changes: 9 additions & 0 deletions Libraries/Components/View/ViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,15 @@ export type ViewProps = $ReadOnly<{|
*/
collapsable?: ?boolean,

/**
* Used to locate this view from native classes.
*
* > This disables the 'layout-only view removal' optimization for this view!
*
* See https://reactnative.dev/docs/view#id
*/
id?: string,

/**
* Used to locate this view in end-to-end tests.
*
Expand Down
4 changes: 4 additions & 0 deletions Libraries/Text/Text.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ const Text: React.AbstractComponent<
'aria-expanded': ariaExpanded,
'aria-selected': ariaSelected,
ellipsizeMode,
id,
nativeID,
onLongPress,
onPress,
onPressIn,
Expand Down Expand Up @@ -205,6 +207,7 @@ const Text: React.AbstractComponent<
isHighlighted={isHighlighted}
isPressable={isPressable}
selectable={_selectable}
nativeID={id ?? nativeID}
numberOfLines={numberOfLines}
selectionColor={selectionColor}
style={style}
Expand All @@ -222,6 +225,7 @@ const Text: React.AbstractComponent<
allowFontScaling={allowFontScaling !== false}
ellipsizeMode={ellipsizeMode ?? 'tail'}
isHighlighted={isHighlighted}
nativeID={id ?? nativeID}
numberOfLines={numberOfLines}
selectionColor={selectionColor}
style={style}
Expand Down
7 changes: 7 additions & 0 deletions Libraries/Text/TextProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ export type TextProps = $ReadOnly<{|
*/
ellipsizeMode?: ?('clip' | 'head' | 'middle' | 'tail'),

/**
* Used to locate this view from native code.
*
* See https://reactnative.dev/docs/text#nativeid
*/
id?: string,

/**
* Specifies largest possible scale a font can reach when `allowFontScaling` is enabled.
* Possible values:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* 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.
*/

package com.facebook.react.tests;

import android.view.View;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.uimanager.util.ReactFindViewUtil;
import java.util.Arrays;
import java.util.List;

/**
* Tests that the 'id' property can be set on various views. The 'id' property is used
* to reference react managed views from native code.
*/
public class IdTestCase extends ReactAppInstrumentationTestCase {

@Override
protected String getReactApplicationKeyUnderTest() {
return "IdTestApp";
}

private final List<String> viewTags =
Arrays.asList(
"Image",
"Text",
"TouchableBounce",
"TouchableHighlight",
"TouchableOpacity",
"TouchableWithoutFeedback",
"TextInput",
"View");

private boolean mViewFound;

@Override
protected void setUp() throws Exception {
mViewFound = false;
ReactFindViewUtil.addViewListener(
new ReactFindViewUtil.OnViewFoundListener() {
@Override
public String getNativeId() {
return viewTags.get(0);
}

@Override
public void onViewFound(View view) {
mViewFound = true;
}
});
super.setUp();
}

public void testPropertyIsSetForViews() {
for (String nativeId : viewTags) {
View viewWithTag = ReactFindViewUtil.findView(getActivity().getRootView(), nativeId);
assertNotNull(
"View with id " + nativeId + " was not found. Check IdTestModule.js.",
viewWithTag);
}
}

public void testViewListener() {
assertTrue("OnViewFound callback was never invoked", mViewFound);
}

public void testFindView() {
mViewFound = false;
ReactFindViewUtil.findView(
getActivity().getRootView(),
new ReactFindViewUtil.OnViewFoundListener() {
@Override
public String getNativeId() {
return viewTags.get(0);
}

@Override
public void onViewFound(View view) {
mViewFound = true;
}
});
assertTrue(
"OnViewFound callback should have successfully been invoked synchronously", mViewFound);
}
}
76 changes: 76 additions & 0 deletions ReactAndroid/src/androidTest/js/IdTestModule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* 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
*/

'use strict';

const React = require('react');
const TouchableBounce = require('react-native/Libraries/Components/Touchable/TouchableBounce');

const {
Image,
StyleSheet,
Text,
TextInput,
TouchableHighlight,
TouchableOpacity,
TouchableWithoutFeedback,
View,
} = require('react-native');

/**
* All the views implemented on Android, each with the id property set.
* We test that:
* - The app renders fine
* - The id property is passed to the native views via the nativeID property
*/
class IdTestApp extends React.Component<{...}> {
render(): React.Node {
const uri =
'data:image/gif;base64,' +
'R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwAAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapy' +
'uvUUlvONmOZtfzgFzByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSpa/' +
'TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJlZeGl9i2icVqaNVailT6F5' +
'iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uisF81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97V' +
'riy/Xl4/f1cf5VWzXyym7PHhhx4dbgYKAAA7';
return (
<View>
<Image id="Image" source={{uri: uri}} style={styles.base} />
<Text id="Text">text</Text>
<TextInput id="TextInput" value="Text input" />
<TouchableBounce id="TouchableBounce">
<Text>TouchableBounce</Text>
</TouchableBounce>
<TouchableHighlight id="TouchableHighlight">
<Text>TouchableHighlight</Text>
</TouchableHighlight>
<TouchableOpacity id="TouchableOpacity">
<Text>TouchableOpacity</Text>
</TouchableOpacity>
<TouchableWithoutFeedback id="TouchableWithoutFeedback">
<View>
<Text>TouchableWithoutFeedback</Text>
</View>
</TouchableWithoutFeedback>
<View id="View" />
</View>
);
}
}

const styles = StyleSheet.create({
base: {
width: 150,
height: 50,
},
});

module.exports = {
IdTestApp,
};
4 changes: 4 additions & 0 deletions ReactAndroid/src/androidTest/js/TestApps.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ const apps = [
component: () =>
require('./ScrollViewTestModule').HorizontalScrollViewTestApp,
},
{
appKey: 'IdTestApp',
component: () => require('./IdTestModule').IdTestApp,
},
{
appKey: 'ImageOverlayColorTestApp',
component: () => require('./ImageOverlayColorTestApp'),
Expand Down