diff --git a/packages/react-native/Libraries/Components/View/View.js b/packages/react-native/Libraries/Components/View/View.js
index be74cb04c0de..7bf5f641944b 100644
--- a/packages/react-native/Libraries/Components/View/View.js
+++ b/packages/react-native/Libraries/Components/View/View.js
@@ -10,6 +10,7 @@
import type {ViewProps} from './ViewPropTypes';
+import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
import TextAncestorContext from '../../Text/TextAncestorContext';
import ViewNativeComponent from './ViewNativeComponent';
import * as React from 'react';
@@ -28,96 +29,100 @@ component View(
) {
const hasTextAncestor = use(TextAncestorContext);
- const {
- accessibilityState,
- accessibilityValue,
- 'aria-busy': ariaBusy,
- 'aria-checked': ariaChecked,
- 'aria-disabled': ariaDisabled,
- 'aria-expanded': ariaExpanded,
- 'aria-hidden': ariaHidden,
- 'aria-label': ariaLabel,
- 'aria-labelledby': ariaLabelledBy,
- 'aria-live': ariaLive,
- 'aria-selected': ariaSelected,
- 'aria-valuemax': ariaValueMax,
- 'aria-valuemin': ariaValueMin,
- 'aria-valuenow': ariaValueNow,
- 'aria-valuetext': ariaValueText,
- id,
- tabIndex,
- ...otherProps
- } = props;
-
- // Since we destructured props, we can now treat it as mutable
- const processedProps = otherProps as {...ViewProps};
-
- const parsedAriaLabelledBy = ariaLabelledBy?.split(/\s*,\s*/g);
- if (parsedAriaLabelledBy !== undefined) {
- processedProps.accessibilityLabelledBy = parsedAriaLabelledBy;
- }
+ let resolvedProps = props;
+ if (!ReactNativeFeatureFlags.enableNativeViewPropTransformations()) {
+ const {
+ accessibilityState,
+ accessibilityValue,
+ 'aria-busy': ariaBusy,
+ 'aria-checked': ariaChecked,
+ 'aria-disabled': ariaDisabled,
+ 'aria-expanded': ariaExpanded,
+ 'aria-hidden': ariaHidden,
+ 'aria-label': ariaLabel,
+ 'aria-labelledby': ariaLabelledBy,
+ 'aria-live': ariaLive,
+ 'aria-selected': ariaSelected,
+ 'aria-valuemax': ariaValueMax,
+ 'aria-valuemin': ariaValueMin,
+ 'aria-valuenow': ariaValueNow,
+ 'aria-valuetext': ariaValueText,
+ id,
+ tabIndex,
+ ...otherProps
+ } = props;
+
+ const processedProps = otherProps as {...ViewProps};
+
+ const parsedAriaLabelledBy = ariaLabelledBy?.split(/\s*,\s*/g);
+ if (parsedAriaLabelledBy !== undefined) {
+ processedProps.accessibilityLabelledBy = parsedAriaLabelledBy;
+ }
- if (ariaLabel !== undefined) {
- processedProps.accessibilityLabel = ariaLabel;
- }
+ if (ariaLabel !== undefined) {
+ processedProps.accessibilityLabel = ariaLabel;
+ }
- if (ariaLive !== undefined) {
- processedProps.accessibilityLiveRegion =
- ariaLive === 'off' ? 'none' : ariaLive;
- }
+ if (ariaLive !== undefined) {
+ processedProps.accessibilityLiveRegion =
+ ariaLive === 'off' ? 'none' : ariaLive;
+ }
- if (ariaHidden !== undefined) {
- processedProps.accessibilityElementsHidden = ariaHidden;
- if (ariaHidden === true) {
- processedProps.importantForAccessibility = 'no-hide-descendants';
+ if (ariaHidden !== undefined) {
+ processedProps.accessibilityElementsHidden = ariaHidden;
+ if (ariaHidden === true) {
+ processedProps.importantForAccessibility = 'no-hide-descendants';
+ }
}
- }
- if (id !== undefined) {
- processedProps.nativeID = id;
- }
+ if (id !== undefined) {
+ processedProps.nativeID = id;
+ }
- if (tabIndex !== undefined) {
- processedProps.focusable = !tabIndex;
- }
+ if (tabIndex !== undefined) {
+ processedProps.focusable = !tabIndex;
+ }
- if (
- accessibilityState != null ||
- ariaBusy != null ||
- ariaChecked != null ||
- ariaDisabled != null ||
- ariaExpanded != null ||
- ariaSelected != null
- ) {
- processedProps.accessibilityState = {
- busy: ariaBusy ?? accessibilityState?.busy,
- checked: ariaChecked ?? accessibilityState?.checked,
- disabled: ariaDisabled ?? accessibilityState?.disabled,
- expanded: ariaExpanded ?? accessibilityState?.expanded,
- selected: ariaSelected ?? accessibilityState?.selected,
- };
- }
+ if (
+ accessibilityState != null ||
+ ariaBusy != null ||
+ ariaChecked != null ||
+ ariaDisabled != null ||
+ ariaExpanded != null ||
+ ariaSelected != null
+ ) {
+ processedProps.accessibilityState = {
+ busy: ariaBusy ?? accessibilityState?.busy,
+ checked: ariaChecked ?? accessibilityState?.checked,
+ disabled: ariaDisabled ?? accessibilityState?.disabled,
+ expanded: ariaExpanded ?? accessibilityState?.expanded,
+ selected: ariaSelected ?? accessibilityState?.selected,
+ };
+ }
+
+ if (
+ accessibilityValue != null ||
+ ariaValueMax != null ||
+ ariaValueMin != null ||
+ ariaValueNow != null ||
+ ariaValueText != null
+ ) {
+ processedProps.accessibilityValue = {
+ max: ariaValueMax ?? accessibilityValue?.max,
+ min: ariaValueMin ?? accessibilityValue?.min,
+ now: ariaValueNow ?? accessibilityValue?.now,
+ text: ariaValueText ?? accessibilityValue?.text,
+ };
+ }
- if (
- accessibilityValue != null ||
- ariaValueMax != null ||
- ariaValueMin != null ||
- ariaValueNow != null ||
- ariaValueText != null
- ) {
- processedProps.accessibilityValue = {
- max: ariaValueMax ?? accessibilityValue?.max,
- min: ariaValueMin ?? accessibilityValue?.min,
- now: ariaValueNow ?? accessibilityValue?.now,
- text: ariaValueText ?? accessibilityValue?.text,
- };
+ resolvedProps = processedProps;
}
const actualView =
ref == null ? (
-
+
) : (
-
+
);
if (hasTextAncestor) {
diff --git a/packages/react-native/Libraries/Components/View/__tests__/View-itest.js b/packages/react-native/Libraries/Components/View/__tests__/View-itest.js
index 0ad79c3d02d8..267d683170ff 100644
--- a/packages/react-native/Libraries/Components/View/__tests__/View-itest.js
+++ b/packages/react-native/Libraries/Components/View/__tests__/View-itest.js
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
- * @fantom_flags enableNativeCSSParsing:*
+ * @fantom_flags enableNativeCSSParsing:* enableNativeViewPropTransformations:*
* @format
*/
@@ -578,6 +578,140 @@ describe('', () => {
});
});
+ describe('overlapping aria-label and accessibilityLabel', () => {
+ it('preserves accessibilityLabel when aria-label is removed', () => {
+ const root = Fantom.createRoot();
+
+ // Set both aria-label and accessibilityLabel
+ Fantom.runTask(() => {
+ root.render(
+ ,
+ );
+ });
+
+ // aria-label should take precedence
+ expect(
+ root.getRenderedOutput({props: ['accessibilityLabel']}).toJSX(),
+ ).toEqual();
+
+ // Remove aria-label but keep accessibilityLabel
+ Fantom.runTask(() => {
+ root.render(
+ ,
+ );
+ });
+
+ // accessibilityLabel should still be "native value"
+ expect(
+ root.getRenderedOutput({props: ['accessibilityLabel']}).toJSX(),
+ ).toEqual();
+ });
+ });
+
+ describe('overlapping aria-hidden and importantForAccessibility', () => {
+ it('preserves importantForAccessibility when aria-hidden is removed', () => {
+ const root = Fantom.createRoot();
+
+ // Set both aria-hidden and importantForAccessibility
+ Fantom.runTask(() => {
+ root.render(
+ ,
+ );
+ });
+
+ expect(
+ root
+ .getRenderedOutput({props: ['importantForAccessibility']})
+ .toJSX(),
+ ).toEqual(
+ ,
+ );
+
+ // Remove aria-hidden but keep importantForAccessibility
+ Fantom.runTask(() => {
+ root.render(
+ ,
+ );
+ });
+
+ // importantForAccessibility should still be "no-hide-descendants"
+ expect(
+ root
+ .getRenderedOutput({props: ['importantForAccessibility']})
+ .toJSX(),
+ ).toEqual(
+ ,
+ );
+ });
+ });
+
+ describe('aria-hidden={false} with importantForAccessibility', () => {
+ it('does not overwrite explicit importantForAccessibility', () => {
+ const root = Fantom.createRoot();
+
+ // Set importantForAccessibility="yes" and aria-hidden={false}.
+ // aria-hidden={false} should NOT reset importantForAccessibility
+ // to Auto, it should preserve the explicit "yes" value.
+ Fantom.runTask(() => {
+ root.render(
+ ,
+ );
+ });
+
+ expect(
+ root
+ .getRenderedOutput({props: ['importantForAccessibility']})
+ .toJSX(),
+ ).toEqual();
+ });
+ });
+
+ describe('overlapping aria-live and accessibilityLiveRegion', () => {
+ it('preserves accessibilityLiveRegion when aria-live is removed', () => {
+ const root = Fantom.createRoot();
+
+ // Set both aria-live and accessibilityLiveRegion
+ Fantom.runTask(() => {
+ root.render(
+ ,
+ );
+ });
+
+ // Remove aria-live but keep accessibilityLiveRegion
+ Fantom.runTask(() => {
+ root.render(
+ ,
+ );
+ });
+
+ // accessibilityLiveRegion should still be "assertive"
+ expect(
+ root
+ .getRenderedOutput({props: ['accessibilityLiveRegion']})
+ .toJSX(),
+ ).toEqual();
+ });
+ });
+
describe('aria-live', () => {
it('is mapped to accessibilityLiveRegion', () => {
const root = Fantom.createRoot();
diff --git a/packages/react-native/Libraries/NativeComponent/BaseViewConfig.android.js b/packages/react-native/Libraries/NativeComponent/BaseViewConfig.android.js
index f2c8c78798d3..cdd9eceaead2 100644
--- a/packages/react-native/Libraries/NativeComponent/BaseViewConfig.android.js
+++ b/packages/react-native/Libraries/NativeComponent/BaseViewConfig.android.js
@@ -214,6 +214,7 @@ const validAttributesForNonEventProps = {
renderToHardwareTextureAndroid: true,
testID: true,
nativeID: true,
+ id: true,
accessibilityLabelledBy: true,
accessibilityLabel: true,
accessibilityHint: true,
@@ -226,6 +227,19 @@ const validAttributesForNonEventProps = {
experimental_accessibilityOrder: true,
importantForAccessibility: true,
screenReaderFocusable: true,
+ 'aria-busy': true,
+ 'aria-checked': true,
+ 'aria-disabled': true,
+ 'aria-expanded': true,
+ 'aria-hidden': true,
+ 'aria-label': true,
+ 'aria-labelledby': true,
+ 'aria-live': true,
+ 'aria-selected': true,
+ 'aria-valuemax': true,
+ 'aria-valuemin': true,
+ 'aria-valuenow': true,
+ 'aria-valuetext': true,
role: true,
rotation: true,
scaleX: true,
@@ -368,6 +382,7 @@ const validAttributesForNonEventProps = {
borderBlockEndColor: colorAttribute,
borderBlockStartColor: colorAttribute,
focusable: true,
+ tabIndex: true,
backfaceVisibility: true,
} as const;
diff --git a/packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js b/packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js
index 913ebc34b534..2c0ca3437148 100644
--- a/packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js
+++ b/packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js
@@ -218,7 +218,22 @@ const validAttributesForNonEventProps = {
transformOrigin: true,
accessibilityRole: true,
accessibilityState: true,
+ 'aria-busy': true,
+ 'aria-checked': true,
+ 'aria-disabled': true,
+ 'aria-expanded': true,
+ 'aria-hidden': true,
+ 'aria-label': true,
+ 'aria-labelledby': true,
+ 'aria-live': true,
+ 'aria-selected': true,
+ 'aria-valuemax': true,
+ 'aria-valuemin': true,
+ 'aria-valuenow': true,
+ 'aria-valuetext': true,
+ tabIndex: true,
nativeID: true,
+ id: true,
pointerEvents: true,
removeClippedSubviews: true,
role: true,
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt
index a66fb344c78a..eeec9f2a911e 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<<2ca6b65f1103a486e4e5a006de629e76>>
+ * @generated SignedSource<<38f50776ad2b88fa69b710dbd67315e2>>
*/
/**
@@ -288,6 +288,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableNativeCSSParsing(): Boolean = accessor.enableNativeCSSParsing()
+ /**
+ * When enabled, View.js passes aria-*, id, and tabIndex props directly to native, relying on C++ prop parsing instead of JS-side transformations.
+ */
+ @JvmStatic
+ public fun enableNativeViewPropTransformations(): Boolean = accessor.enableNativeViewPropTransformations()
+
/**
* Enable network event reporting hooks in each native platform through `NetworkReporter` (Web Perf APIs + CDP). This flag should be combined with `fuseboxNetworkInspectionEnabled` to enable Network CDP debugging.
*/
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt
index 164b90ee3e9e..214d1d356bf0 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<<202662ab1c26ed104cfe837162d4f9a2>>
+ * @generated SignedSource<>
*/
/**
@@ -63,6 +63,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableMainQueueCoordinatorOnIOSCache: Boolean? = null
private var enableModuleArgumentNSNullConversionIOSCache: Boolean? = null
private var enableNativeCSSParsingCache: Boolean? = null
+ private var enableNativeViewPropTransformationsCache: Boolean? = null
private var enableNetworkEventReportingCache: Boolean? = null
private var enablePreparedTextLayoutCache: Boolean? = null
private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null
@@ -494,6 +495,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
+ override fun enableNativeViewPropTransformations(): Boolean {
+ var cached = enableNativeViewPropTransformationsCache
+ if (cached == null) {
+ cached = ReactNativeFeatureFlagsCxxInterop.enableNativeViewPropTransformations()
+ enableNativeViewPropTransformationsCache = cached
+ }
+ return cached
+ }
+
override fun enableNetworkEventReporting(): Boolean {
var cached = enableNetworkEventReportingCache
if (cached == null) {
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt
index 0ba312d6258b..6ead7879cc7f 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<<03f5b76fefda14757a43414f6624601a>>
+ * @generated SignedSource<<85d23b5f33a6a09c1e42b976a3a99f2f>>
*/
/**
@@ -114,6 +114,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun enableNativeCSSParsing(): Boolean
+ @DoNotStrip @JvmStatic public external fun enableNativeViewPropTransformations(): Boolean
+
@DoNotStrip @JvmStatic public external fun enableNetworkEventReporting(): Boolean
@DoNotStrip @JvmStatic public external fun enablePreparedTextLayout(): Boolean
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt
index 01469ff2539f..1e6f48d7c16d 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<<7713ee7b0947f0ae8c66b73413a7f226>>
+ * @generated SignedSource<<4adf4e6f40b726bf9d3680301fb92e1f>>
*/
/**
@@ -109,6 +109,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableNativeCSSParsing(): Boolean = false
+ override fun enableNativeViewPropTransformations(): Boolean = false
+
override fun enableNetworkEventReporting(): Boolean = true
override fun enablePreparedTextLayout(): Boolean = false
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt
index 1ce18fbaf475..5b1fd8c2a1b7 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<<79cf67c605a76059f010cf2ccd0ec64b>>
+ * @generated SignedSource<<2115ff3be1f3944d499090d3089d185b>>
*/
/**
@@ -67,6 +67,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableMainQueueCoordinatorOnIOSCache: Boolean? = null
private var enableModuleArgumentNSNullConversionIOSCache: Boolean? = null
private var enableNativeCSSParsingCache: Boolean? = null
+ private var enableNativeViewPropTransformationsCache: Boolean? = null
private var enableNetworkEventReportingCache: Boolean? = null
private var enablePreparedTextLayoutCache: Boolean? = null
private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null
@@ -541,6 +542,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
+ override fun enableNativeViewPropTransformations(): Boolean {
+ var cached = enableNativeViewPropTransformationsCache
+ if (cached == null) {
+ cached = currentProvider.enableNativeViewPropTransformations()
+ accessedFeatureFlags.add("enableNativeViewPropTransformations")
+ enableNativeViewPropTransformationsCache = cached
+ }
+ return cached
+ }
+
override fun enableNetworkEventReporting(): Boolean {
var cached = enableNetworkEventReportingCache
if (cached == null) {
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt
index 59b5af3095b8..f574fa643c0f 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<<655d1ff3caeb3d8e95d44176d65b951b>>
+ * @generated SignedSource<<1bbbe280815927e0f977f1d1e7787937>>
*/
/**
@@ -109,6 +109,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun enableNativeCSSParsing(): Boolean
+ @DoNotStrip public fun enableNativeViewPropTransformations(): Boolean
+
@DoNotStrip public fun enableNetworkEventReporting(): Boolean
@DoNotStrip public fun enablePreparedTextLayout(): Boolean
diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp
index 5467c7072e81..4a2a9b544184 100644
--- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp
+++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<<175e48107d9bd1d713a4da0a252a58bf>>
+ * @generated SignedSource<>
*/
/**
@@ -297,6 +297,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
+ bool enableNativeViewPropTransformations() override {
+ static const auto method =
+ getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableNativeViewPropTransformations");
+ return method(javaProvider_);
+ }
+
bool enableNetworkEventReporting() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableNetworkEventReporting");
@@ -774,6 +780,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableNativeCSSParsing(
return ReactNativeFeatureFlags::enableNativeCSSParsing();
}
+bool JReactNativeFeatureFlagsCxxInterop::enableNativeViewPropTransformations(
+ facebook::jni::alias_ref /*unused*/) {
+ return ReactNativeFeatureFlags::enableNativeViewPropTransformations();
+}
+
bool JReactNativeFeatureFlagsCxxInterop::enableNetworkEventReporting(
facebook::jni::alias_ref /*unused*/) {
return ReactNativeFeatureFlags::enableNetworkEventReporting();
@@ -1149,6 +1160,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableNativeCSSParsing",
JReactNativeFeatureFlagsCxxInterop::enableNativeCSSParsing),
+ makeNativeMethod(
+ "enableNativeViewPropTransformations",
+ JReactNativeFeatureFlagsCxxInterop::enableNativeViewPropTransformations),
makeNativeMethod(
"enableNetworkEventReporting",
JReactNativeFeatureFlagsCxxInterop::enableNetworkEventReporting),
diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h
index 69cb7c48e47e..e2a03055ab86 100644
--- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h
+++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<>
+ * @generated SignedSource<<78e35f9316b9568e7a44153d542b6ebd>>
*/
/**
@@ -159,6 +159,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool enableNativeCSSParsing(
facebook::jni::alias_ref);
+ static bool enableNativeViewPropTransformations(
+ facebook::jni::alias_ref);
+
static bool enableNetworkEventReporting(
facebook::jni::alias_ref);
diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp
index 08bfc040f3de..6de9c614fc18 100644
--- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp
+++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<<5208616bc5d84f040ad0fa5e85acd6c4>>
+ * @generated SignedSource<<8643070722a65532f6c928ed82ca2aa5>>
*/
/**
@@ -198,6 +198,10 @@ bool ReactNativeFeatureFlags::enableNativeCSSParsing() {
return getAccessor().enableNativeCSSParsing();
}
+bool ReactNativeFeatureFlags::enableNativeViewPropTransformations() {
+ return getAccessor().enableNativeViewPropTransformations();
+}
+
bool ReactNativeFeatureFlags::enableNetworkEventReporting() {
return getAccessor().enableNetworkEventReporting();
}
diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h
index 731038a34561..fdbcfc434f8e 100644
--- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h
+++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<<1cbe2158c5242b2980ddafafe9084d7c>>
+ * @generated SignedSource<<47cc177671195fd67010ecdba712596a>>
*/
/**
@@ -254,6 +254,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool enableNativeCSSParsing();
+ /**
+ * When enabled, View.js passes aria-*, id, and tabIndex props directly to native, relying on C++ prop parsing instead of JS-side transformations.
+ */
+ RN_EXPORT static bool enableNativeViewPropTransformations();
+
/**
* Enable network event reporting hooks in each native platform through `NetworkReporter` (Web Perf APIs + CDP). This flag should be combined with `fuseboxNetworkInspectionEnabled` to enable Network CDP debugging.
*/
diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp
index 90f98c98f1da..cf9c347b5c3e 100644
--- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp
+++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<>
+ * @generated SignedSource<>
*/
/**
@@ -803,6 +803,24 @@ bool ReactNativeFeatureFlagsAccessor::enableNativeCSSParsing() {
return flagValue.value();
}
+bool ReactNativeFeatureFlagsAccessor::enableNativeViewPropTransformations() {
+ auto flagValue = enableNativeViewPropTransformations_.load();
+
+ if (!flagValue.has_value()) {
+ // This block is not exclusive but it is not necessary.
+ // If multiple threads try to initialize the feature flag, we would only
+ // be accessing the provider multiple times but the end state of this
+ // instance and the returned flag value would be the same.
+
+ markFlagAsAccessed(43, "enableNativeViewPropTransformations");
+
+ flagValue = currentProvider_->enableNativeViewPropTransformations();
+ enableNativeViewPropTransformations_ = flagValue;
+ }
+
+ return flagValue.value();
+}
+
bool ReactNativeFeatureFlagsAccessor::enableNetworkEventReporting() {
auto flagValue = enableNetworkEventReporting_.load();
@@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNetworkEventReporting() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(43, "enableNetworkEventReporting");
+ markFlagAsAccessed(44, "enableNetworkEventReporting");
flagValue = currentProvider_->enableNetworkEventReporting();
enableNetworkEventReporting_ = flagValue;
@@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePreparedTextLayout() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(44, "enablePreparedTextLayout");
+ markFlagAsAccessed(45, "enablePreparedTextLayout");
flagValue = currentProvider_->enablePreparedTextLayout();
enablePreparedTextLayout_ = flagValue;
@@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(45, "enablePropsUpdateReconciliationAndroid");
+ markFlagAsAccessed(46, "enablePropsUpdateReconciliationAndroid");
flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid();
enablePropsUpdateReconciliationAndroid_ = flagValue;
@@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(46, "enableSwiftUIBasedFilters");
+ markFlagAsAccessed(47, "enableSwiftUIBasedFilters");
flagValue = currentProvider_->enableSwiftUIBasedFilters();
enableSwiftUIBasedFilters_ = flagValue;
@@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(47, "enableViewCulling");
+ markFlagAsAccessed(48, "enableViewCulling");
flagValue = currentProvider_->enableViewCulling();
enableViewCulling_ = flagValue;
@@ -902,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(48, "enableViewRecycling");
+ markFlagAsAccessed(49, "enableViewRecycling");
flagValue = currentProvider_->enableViewRecycling();
enableViewRecycling_ = flagValue;
@@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForImage() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(49, "enableViewRecyclingForImage");
+ markFlagAsAccessed(50, "enableViewRecyclingForImage");
flagValue = currentProvider_->enableViewRecyclingForImage();
enableViewRecyclingForImage_ = flagValue;
@@ -938,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForScrollView() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(50, "enableViewRecyclingForScrollView");
+ markFlagAsAccessed(51, "enableViewRecyclingForScrollView");
flagValue = currentProvider_->enableViewRecyclingForScrollView();
enableViewRecyclingForScrollView_ = flagValue;
@@ -956,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(51, "enableViewRecyclingForText");
+ markFlagAsAccessed(52, "enableViewRecyclingForText");
flagValue = currentProvider_->enableViewRecyclingForText();
enableViewRecyclingForText_ = flagValue;
@@ -974,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(52, "enableViewRecyclingForView");
+ markFlagAsAccessed(53, "enableViewRecyclingForView");
flagValue = currentProvider_->enableViewRecyclingForView();
enableViewRecyclingForView_ = flagValue;
@@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimenta
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(53, "enableVirtualViewContainerStateExperimental");
+ markFlagAsAccessed(54, "enableVirtualViewContainerStateExperimental");
flagValue = currentProvider_->enableVirtualViewContainerStateExperimental();
enableVirtualViewContainerStateExperimental_ = flagValue;
@@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewDebugFeatures() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(54, "enableVirtualViewDebugFeatures");
+ markFlagAsAccessed(55, "enableVirtualViewDebugFeatures");
flagValue = currentProvider_->enableVirtualViewDebugFeatures();
enableVirtualViewDebugFeatures_ = flagValue;
@@ -1028,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::fixFindShadowNodeByTagRaceCondition() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(55, "fixFindShadowNodeByTagRaceCondition");
+ markFlagAsAccessed(56, "fixFindShadowNodeByTagRaceCondition");
flagValue = currentProvider_->fixFindShadowNodeByTagRaceCondition();
fixFindShadowNodeByTagRaceCondition_ = flagValue;
@@ -1046,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(56, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
+ markFlagAsAccessed(57, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact();
fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue;
@@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::fixTextClippingAndroid15useBoundsForWidth(
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(57, "fixTextClippingAndroid15useBoundsForWidth");
+ markFlagAsAccessed(58, "fixTextClippingAndroid15useBoundsForWidth");
flagValue = currentProvider_->fixTextClippingAndroid15useBoundsForWidth();
fixTextClippingAndroid15useBoundsForWidth_ = flagValue;
@@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxAssertSingleHostState() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(58, "fuseboxAssertSingleHostState");
+ markFlagAsAccessed(59, "fuseboxAssertSingleHostState");
flagValue = currentProvider_->fuseboxAssertSingleHostState();
fuseboxAssertSingleHostState_ = flagValue;
@@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(59, "fuseboxEnabledRelease");
+ markFlagAsAccessed(60, "fuseboxEnabledRelease");
flagValue = currentProvider_->fuseboxEnabledRelease();
fuseboxEnabledRelease_ = flagValue;
@@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(60, "fuseboxNetworkInspectionEnabled");
+ markFlagAsAccessed(61, "fuseboxNetworkInspectionEnabled");
flagValue = currentProvider_->fuseboxNetworkInspectionEnabled();
fuseboxNetworkInspectionEnabled_ = flagValue;
@@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(61, "hideOffscreenVirtualViewsOnIOS");
+ markFlagAsAccessed(62, "hideOffscreenVirtualViewsOnIOS");
flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS();
hideOffscreenVirtualViewsOnIOS_ = flagValue;
@@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(62, "overrideBySynchronousMountPropsAtMountingAndroid");
+ markFlagAsAccessed(63, "overrideBySynchronousMountPropsAtMountingAndroid");
flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid();
overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue;
@@ -1172,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(63, "perfIssuesEnabled");
+ markFlagAsAccessed(64, "perfIssuesEnabled");
flagValue = currentProvider_->perfIssuesEnabled();
perfIssuesEnabled_ = flagValue;
@@ -1190,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(64, "perfMonitorV2Enabled");
+ markFlagAsAccessed(65, "perfMonitorV2Enabled");
flagValue = currentProvider_->perfMonitorV2Enabled();
perfMonitorV2Enabled_ = flagValue;
@@ -1208,7 +1226,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(65, "preparedTextCacheSize");
+ markFlagAsAccessed(66, "preparedTextCacheSize");
flagValue = currentProvider_->preparedTextCacheSize();
preparedTextCacheSize_ = flagValue;
@@ -1226,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(66, "preventShadowTreeCommitExhaustion");
+ markFlagAsAccessed(67, "preventShadowTreeCommitExhaustion");
flagValue = currentProvider_->preventShadowTreeCommitExhaustion();
preventShadowTreeCommitExhaustion_ = flagValue;
@@ -1244,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(67, "shouldPressibilityUseW3CPointerEventsForHover");
+ markFlagAsAccessed(68, "shouldPressibilityUseW3CPointerEventsForHover");
flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover();
shouldPressibilityUseW3CPointerEventsForHover_ = flagValue;
@@ -1262,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(68, "shouldTriggerResponderTransferOnScrollAndroid");
+ markFlagAsAccessed(69, "shouldTriggerResponderTransferOnScrollAndroid");
flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid();
shouldTriggerResponderTransferOnScrollAndroid_ = flagValue;
@@ -1280,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause()
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(69, "skipActivityIdentityAssertionOnHostPause");
+ markFlagAsAccessed(70, "skipActivityIdentityAssertionOnHostPause");
flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause();
skipActivityIdentityAssertionOnHostPause_ = flagValue;
@@ -1298,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::syncAndroidClipToPaddingWithOverflow() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(70, "syncAndroidClipToPaddingWithOverflow");
+ markFlagAsAccessed(71, "syncAndroidClipToPaddingWithOverflow");
flagValue = currentProvider_->syncAndroidClipToPaddingWithOverflow();
syncAndroidClipToPaddingWithOverflow_ = flagValue;
@@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(71, "traceTurboModulePromiseRejectionsOnAndroid");
+ markFlagAsAccessed(72, "traceTurboModulePromiseRejectionsOnAndroid");
flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid();
traceTurboModulePromiseRejectionsOnAndroid_ = flagValue;
@@ -1334,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit(
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(72, "updateRuntimeShadowNodeReferencesOnCommit");
+ markFlagAsAccessed(73, "updateRuntimeShadowNodeReferencesOnCommit");
flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit();
updateRuntimeShadowNodeReferencesOnCommit_ = flagValue;
@@ -1352,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommitT
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(73, "updateRuntimeShadowNodeReferencesOnCommitThread");
+ markFlagAsAccessed(74, "updateRuntimeShadowNodeReferencesOnCommitThread");
flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommitThread();
updateRuntimeShadowNodeReferencesOnCommitThread_ = flagValue;
@@ -1370,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(74, "useAlwaysAvailableJSErrorHandling");
+ markFlagAsAccessed(75, "useAlwaysAvailableJSErrorHandling");
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
useAlwaysAvailableJSErrorHandling_ = flagValue;
@@ -1388,7 +1406,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(75, "useFabricInterop");
+ markFlagAsAccessed(76, "useFabricInterop");
flagValue = currentProvider_->useFabricInterop();
useFabricInterop_ = flagValue;
@@ -1406,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(76, "useNativeViewConfigsInBridgelessMode");
+ markFlagAsAccessed(77, "useNativeViewConfigsInBridgelessMode");
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
@@ -1424,7 +1442,7 @@ bool ReactNativeFeatureFlagsAccessor::useNestedScrollViewAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(77, "useNestedScrollViewAndroid");
+ markFlagAsAccessed(78, "useNestedScrollViewAndroid");
flagValue = currentProvider_->useNestedScrollViewAndroid();
useNestedScrollViewAndroid_ = flagValue;
@@ -1442,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(78, "useSharedAnimatedBackend");
+ markFlagAsAccessed(79, "useSharedAnimatedBackend");
flagValue = currentProvider_->useSharedAnimatedBackend();
useSharedAnimatedBackend_ = flagValue;
@@ -1460,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(79, "useTraitHiddenOnAndroid");
+ markFlagAsAccessed(80, "useTraitHiddenOnAndroid");
flagValue = currentProvider_->useTraitHiddenOnAndroid();
useTraitHiddenOnAndroid_ = flagValue;
@@ -1478,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(80, "useTurboModuleInterop");
+ markFlagAsAccessed(81, "useTurboModuleInterop");
flagValue = currentProvider_->useTurboModuleInterop();
useTurboModuleInterop_ = flagValue;
@@ -1496,7 +1514,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(81, "useTurboModules");
+ markFlagAsAccessed(82, "useTurboModules");
flagValue = currentProvider_->useTurboModules();
useTurboModules_ = flagValue;
@@ -1514,7 +1532,7 @@ bool ReactNativeFeatureFlagsAccessor::useUnorderedMapInDifferentiator() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(82, "useUnorderedMapInDifferentiator");
+ markFlagAsAccessed(83, "useUnorderedMapInDifferentiator");
flagValue = currentProvider_->useUnorderedMapInDifferentiator();
useUnorderedMapInDifferentiator_ = flagValue;
@@ -1532,7 +1550,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(83, "viewCullingOutsetRatio");
+ markFlagAsAccessed(84, "viewCullingOutsetRatio");
flagValue = currentProvider_->viewCullingOutsetRatio();
viewCullingOutsetRatio_ = flagValue;
@@ -1550,7 +1568,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(84, "viewTransitionEnabled");
+ markFlagAsAccessed(85, "viewTransitionEnabled");
flagValue = currentProvider_->viewTransitionEnabled();
viewTransitionEnabled_ = flagValue;
@@ -1568,7 +1586,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
- markFlagAsAccessed(85, "virtualViewPrerenderRatio");
+ markFlagAsAccessed(86, "virtualViewPrerenderRatio");
flagValue = currentProvider_->virtualViewPrerenderRatio();
virtualViewPrerenderRatio_ = flagValue;
diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h
index 454d483bf89b..754cf0afa06e 100644
--- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h
+++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<>
+ * @generated SignedSource<<63a0714c9c06697555b9b66f2bd77288>>
*/
/**
@@ -75,6 +75,7 @@ class ReactNativeFeatureFlagsAccessor {
bool enableMainQueueCoordinatorOnIOS();
bool enableModuleArgumentNSNullConversionIOS();
bool enableNativeCSSParsing();
+ bool enableNativeViewPropTransformations();
bool enableNetworkEventReporting();
bool enablePreparedTextLayout();
bool enablePropsUpdateReconciliationAndroid();
@@ -129,7 +130,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr currentProvider_;
bool wasOverridden_;
- std::array, 86> accessedFeatureFlags_;
+ std::array, 87> accessedFeatureFlags_;
std::atomic> commonTestFlag_;
std::atomic> cdpInteractionMetricsEnabled_;
@@ -174,6 +175,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic> enableMainQueueCoordinatorOnIOS_;
std::atomic> enableModuleArgumentNSNullConversionIOS_;
std::atomic> enableNativeCSSParsing_;
+ std::atomic> enableNativeViewPropTransformations_;
std::atomic> enableNetworkEventReporting_;
std::atomic> enablePreparedTextLayout_;
std::atomic> enablePropsUpdateReconciliationAndroid_;
diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h
index 1807361bc1fa..aa3675116ca4 100644
--- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h
+++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<>
+ * @generated SignedSource<<613758ace44bd00d1156d05f365f1119>>
*/
/**
@@ -199,6 +199,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
+ bool enableNativeViewPropTransformations() override {
+ return false;
+ }
+
bool enableNetworkEventReporting() override {
return true;
}
diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h
index 9b51cd8ea334..97a94b58dcb2 100644
--- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h
+++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<>
+ * @generated SignedSource<<0ed8edbf386bc66719f23b2514d05074>>
*/
/**
@@ -432,6 +432,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::enableNativeCSSParsing();
}
+ bool enableNativeViewPropTransformations() override {
+ auto value = values_["enableNativeViewPropTransformations"];
+ if (!value.isNull()) {
+ return value.getBool();
+ }
+
+ return ReactNativeFeatureFlagsDefaults::enableNativeViewPropTransformations();
+ }
+
bool enableNetworkEventReporting() override {
auto value = values_["enableNetworkEventReporting"];
if (!value.isNull()) {
diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h
index 0c6e9ae47a47..b9cc111cd84a 100644
--- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h
+++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<>
+ * @generated SignedSource<<37ac0b0a8ecb4ee585cd6c0d1b14bfce>>
*/
/**
@@ -68,6 +68,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool enableMainQueueCoordinatorOnIOS() = 0;
virtual bool enableModuleArgumentNSNullConversionIOS() = 0;
virtual bool enableNativeCSSParsing() = 0;
+ virtual bool enableNativeViewPropTransformations() = 0;
virtual bool enableNetworkEventReporting() = 0;
virtual bool enablePreparedTextLayout() = 0;
virtual bool enablePropsUpdateReconciliationAndroid() = 0;
diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp
index bb1057121cec..701e31305c76 100644
--- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp
+++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<>
+ * @generated SignedSource<<11d5240e0fd4bce14794689d7521cda3>>
*/
/**
@@ -259,6 +259,11 @@ bool NativeReactNativeFeatureFlags::enableNativeCSSParsing(
return ReactNativeFeatureFlags::enableNativeCSSParsing();
}
+bool NativeReactNativeFeatureFlags::enableNativeViewPropTransformations(
+ jsi::Runtime& /*runtime*/) {
+ return ReactNativeFeatureFlags::enableNativeViewPropTransformations();
+}
+
bool NativeReactNativeFeatureFlags::enableNetworkEventReporting(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableNetworkEventReporting();
diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h
index 8e919af8df5a..72a9b5568d17 100644
--- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h
+++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<<567578eba7383f26c5d745c57d110e61>>
+ * @generated SignedSource<>
*/
/**
@@ -122,6 +122,8 @@ class NativeReactNativeFeatureFlags
bool enableNativeCSSParsing(jsi::Runtime& runtime);
+ bool enableNativeViewPropTransformations(jsi::Runtime& runtime);
+
bool enableNetworkEventReporting(jsi::Runtime& runtime);
bool enablePreparedTextLayout(jsi::Runtime& runtime);
diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp
index cc39827f8eff..9bf1bb2b65b8 100644
--- a/packages/react-native/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp
+++ b/packages/react-native/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp
@@ -15,6 +15,24 @@
namespace facebook::react {
+static AccessibilityLabelledBy parseCommaSeparatedList(const std::string& str) {
+ AccessibilityLabelledBy result;
+ size_t pos = 0;
+ while (pos < str.size()) {
+ auto commaPos = str.find(',', pos);
+ if (commaPos == std::string::npos) {
+ commaPos = str.size();
+ }
+ auto start = str.find_first_not_of(' ', pos);
+ if (start < commaPos) {
+ auto end = str.find_last_not_of(' ', commaPos - 1);
+ result.value.push_back(str.substr(start, end - start + 1));
+ }
+ pos = commaPos + 1;
+ }
+ return result;
+}
+
AccessibilityProps::AccessibilityProps(
const PropsParserContext& context,
const AccessibilityProps& sourceProps,
@@ -216,7 +234,14 @@ AccessibilityProps::AccessibilityProps(
rawProps,
"testID",
sourceProps.testId,
- "")) {
+ "")),
+ canonicalAccessibilityLabel_(sourceProps.canonicalAccessibilityLabel_),
+ canonicalAccessibilityLiveRegion_(
+ sourceProps.canonicalAccessibilityLiveRegion_),
+ canonicalImportantForAccessibility_(
+ sourceProps.canonicalImportantForAccessibility_),
+ canonicalAccessibilityElementsHidden_(
+ sourceProps.canonicalAccessibilityElementsHidden_) {
// It is a (severe!) perf deoptimization to request props out-of-order.
// Thus, since we need to request the same prop twice here
// (accessibilityRole) we "must" do them subsequently here to prevent
@@ -254,6 +279,244 @@ AccessibilityProps::AccessibilityProps(
} else {
fromRawValue(context, *precedentRoleValue, accessibilityTraits);
}
+
+ if (ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ static auto defaults = AccessibilityProps{};
+
+ // Update canonical values from explicit props if present.
+ // These track the user-set accessibility* prop values separately
+ // from any aria-* override, so we can restore them when an
+ // aria-* alias is cleared.
+ auto* explicitLabel = rawProps.at("accessibilityLabel", nullptr, nullptr);
+ if (explicitLabel != nullptr) {
+ canonicalAccessibilityLabel_ = explicitLabel->hasValue()
+ ? accessibilityLabel
+ : defaults.accessibilityLabel;
+ }
+
+ auto* explicitLiveRegion =
+ rawProps.at("accessibilityLiveRegion", nullptr, nullptr);
+ if (explicitLiveRegion != nullptr) {
+ canonicalAccessibilityLiveRegion_ = explicitLiveRegion->hasValue()
+ ? accessibilityLiveRegion
+ : defaults.accessibilityLiveRegion;
+ }
+
+ auto* explicitIFA =
+ rawProps.at("importantForAccessibility", nullptr, nullptr);
+ if (explicitIFA != nullptr) {
+ canonicalImportantForAccessibility_ = explicitIFA->hasValue()
+ ? importantForAccessibility
+ : defaults.importantForAccessibility;
+ }
+
+ auto* explicitAEH =
+ rawProps.at("accessibilityElementsHidden", nullptr, nullptr);
+ if (explicitAEH != nullptr) {
+ canonicalAccessibilityElementsHidden_ = explicitAEH->hasValue()
+ ? accessibilityElementsHidden
+ : defaults.accessibilityElementsHidden;
+ }
+
+ // aria-label -> accessibilityLabel
+ auto* ariaLabel = rawProps.at("aria-label", nullptr, nullptr);
+ if (ariaLabel != nullptr) {
+ if (ariaLabel->hasValue()) {
+ fromRawValue(context, *ariaLabel, accessibilityLabel);
+ } else {
+ accessibilityLabel = canonicalAccessibilityLabel_;
+ }
+ }
+
+ // aria-labelledby -> accessibilityLabelledBy (comma-split string ->
+ // array)
+ auto* ariaLabelledBy = rawProps.at("aria-labelledby", nullptr, nullptr);
+ if (ariaLabelledBy != nullptr) {
+ if (ariaLabelledBy->hasValue()) {
+ if (ariaLabelledBy->hasType()) {
+ accessibilityLabelledBy =
+ parseCommaSeparatedList((std::string)*ariaLabelledBy);
+ } else {
+ fromRawValue(context, *ariaLabelledBy, accessibilityLabelledBy);
+ }
+ } else {
+ accessibilityLabelledBy = defaults.accessibilityLabelledBy;
+ }
+ }
+
+ // aria-live -> accessibilityLiveRegion (map "off" -> None)
+ auto* ariaLive = rawProps.at("aria-live", nullptr, nullptr);
+ if (ariaLive != nullptr) {
+ if (ariaLive->hasValue()) {
+ if (ariaLive->hasType()) {
+ auto str = (std::string)*ariaLive;
+ if (str == "off") {
+ accessibilityLiveRegion = AccessibilityLiveRegion::None;
+ } else {
+ fromRawValue(context, *ariaLive, accessibilityLiveRegion);
+ }
+ }
+ } else {
+ accessibilityLiveRegion = canonicalAccessibilityLiveRegion_;
+ }
+ }
+
+ // aria-hidden -> accessibilityElementsHidden +
+ // importantForAccessibility
+ auto* ariaHidden = rawProps.at("aria-hidden", nullptr, nullptr);
+ if (ariaHidden != nullptr) {
+ if (ariaHidden->hasValue()) {
+ fromRawValue(context, *ariaHidden, accessibilityElementsHidden);
+ if (accessibilityElementsHidden) {
+ importantForAccessibility =
+ ImportantForAccessibility::NoHideDescendants;
+ } else {
+ importantForAccessibility = canonicalImportantForAccessibility_;
+ }
+ } else {
+ accessibilityElementsHidden = canonicalAccessibilityElementsHidden_;
+ importantForAccessibility = canonicalImportantForAccessibility_;
+ }
+ }
+
+ // aria-busy -> accessibilityState.busy
+ auto* ariaBusy = rawProps.at("aria-busy", nullptr, nullptr);
+ if (ariaBusy != nullptr) {
+ if (ariaBusy->hasValue()) {
+ if (!accessibilityState.has_value()) {
+ accessibilityState = AccessibilityState{};
+ }
+ fromRawValue(context, *ariaBusy, accessibilityState->busy);
+ } else {
+ if (accessibilityState.has_value()) {
+ accessibilityState->busy = AccessibilityState{}.busy;
+ }
+ }
+ }
+
+ // aria-checked -> accessibilityState.checked
+ auto* ariaChecked = rawProps.at("aria-checked", nullptr, nullptr);
+ if (ariaChecked != nullptr) {
+ if (ariaChecked->hasValue()) {
+ if (!accessibilityState.has_value()) {
+ accessibilityState = AccessibilityState{};
+ }
+ if (ariaChecked->hasType()) {
+ if ((std::string)*ariaChecked == "mixed") {
+ accessibilityState->checked = AccessibilityState::Mixed;
+ }
+ } else if (ariaChecked->hasType()) {
+ accessibilityState->checked = (bool)*ariaChecked
+ ? AccessibilityState::Checked
+ : AccessibilityState::Unchecked;
+ }
+ } else {
+ if (accessibilityState.has_value()) {
+ accessibilityState->checked = AccessibilityState{}.checked;
+ }
+ }
+ }
+
+ // aria-disabled -> accessibilityState.disabled
+ auto* ariaDisabled = rawProps.at("aria-disabled", nullptr, nullptr);
+ if (ariaDisabled != nullptr) {
+ if (ariaDisabled->hasValue()) {
+ if (!accessibilityState.has_value()) {
+ accessibilityState = AccessibilityState{};
+ }
+ fromRawValue(context, *ariaDisabled, accessibilityState->disabled);
+ } else {
+ if (accessibilityState.has_value()) {
+ accessibilityState->disabled = AccessibilityState{}.disabled;
+ }
+ }
+ }
+
+ // aria-expanded -> accessibilityState.expanded
+ auto* ariaExpanded = rawProps.at("aria-expanded", nullptr, nullptr);
+ if (ariaExpanded != nullptr) {
+ if (ariaExpanded->hasValue()) {
+ if (!accessibilityState.has_value()) {
+ accessibilityState = AccessibilityState{};
+ }
+ fromRawValue(context, *ariaExpanded, accessibilityState->expanded);
+ } else {
+ if (accessibilityState.has_value()) {
+ accessibilityState->expanded = AccessibilityState{}.expanded;
+ }
+ }
+ }
+
+ // aria-selected -> accessibilityState.selected
+ auto* ariaSelected = rawProps.at("aria-selected", nullptr, nullptr);
+ if (ariaSelected != nullptr) {
+ if (ariaSelected->hasValue()) {
+ if (!accessibilityState.has_value()) {
+ accessibilityState = AccessibilityState{};
+ }
+ fromRawValue(context, *ariaSelected, accessibilityState->selected);
+ } else {
+ if (accessibilityState.has_value()) {
+ accessibilityState->selected = AccessibilityState{}.selected;
+ }
+ }
+ }
+
+ // If all aria-state fields have been reset to defaults, clear the
+ // optional entirely so the view reports no accessibilityState.
+ if (accessibilityState.has_value() &&
+ *accessibilityState == AccessibilityState{}) {
+ accessibilityState = std::nullopt;
+ }
+
+ // aria-valuemax -> accessibilityValue.max
+ auto* ariaValueMax = rawProps.at("aria-valuemax", nullptr, nullptr);
+ if (ariaValueMax != nullptr) {
+ if (ariaValueMax->hasValue()) {
+ if (ariaValueMax->hasType()) {
+ accessibilityValue.max = (int)*ariaValueMax;
+ }
+ } else {
+ accessibilityValue.max = std::nullopt;
+ }
+ }
+
+ // aria-valuemin -> accessibilityValue.min
+ auto* ariaValueMin = rawProps.at("aria-valuemin", nullptr, nullptr);
+ if (ariaValueMin != nullptr) {
+ if (ariaValueMin->hasValue()) {
+ if (ariaValueMin->hasType()) {
+ accessibilityValue.min = (int)*ariaValueMin;
+ }
+ } else {
+ accessibilityValue.min = std::nullopt;
+ }
+ }
+
+ // aria-valuenow -> accessibilityValue.now
+ auto* ariaValueNow = rawProps.at("aria-valuenow", nullptr, nullptr);
+ if (ariaValueNow != nullptr) {
+ if (ariaValueNow->hasValue()) {
+ if (ariaValueNow->hasType()) {
+ accessibilityValue.now = (int)*ariaValueNow;
+ }
+ } else {
+ accessibilityValue.now = std::nullopt;
+ }
+ }
+
+ // aria-valuetext -> accessibilityValue.text
+ auto* ariaValueText = rawProps.at("aria-valuetext", nullptr, nullptr);
+ if (ariaValueText != nullptr) {
+ if (ariaValueText->hasValue()) {
+ if (ariaValueText->hasType()) {
+ accessibilityValue.text = (std::string)*ariaValueText;
+ }
+ } else {
+ accessibilityValue.text = std::nullopt;
+ }
+ }
+ }
}
}
@@ -267,26 +530,56 @@ void AccessibilityProps::setProp(
switch (hash) {
RAW_SET_PROP_SWITCH_CASE_BASIC(accessible);
RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityState);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLabel);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityOrder);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLabelledBy);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityHint);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLanguage);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityShowsLargeContentViewer);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLargeContentTitle);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityValue);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityActions);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityViewIsModal);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityElementsHidden);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityIgnoresInvertColors);
- RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityRespondsToUserInteraction);
- RAW_SET_PROP_SWITCH_CASE_BASIC(onAccessibilityTap);
- RAW_SET_PROP_SWITCH_CASE_BASIC(onAccessibilityMagicTap);
- RAW_SET_PROP_SWITCH_CASE_BASIC(onAccessibilityEscape);
- RAW_SET_PROP_SWITCH_CASE_BASIC(onAccessibilityAction);
- RAW_SET_PROP_SWITCH_CASE_BASIC(importantForAccessibility);
- RAW_SET_PROP_SWITCH_CASE_BASIC(role);
- RAW_SET_PROP_SWITCH_CASE(testId, "testID");
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("accessibilityLabel"): {
+ fromRawValue(
+ context, value, accessibilityLabel, defaults.accessibilityLabel);
+ canonicalAccessibilityLabel_ = accessibilityLabel;
+ return;
+ }
+ RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityOrder);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLabelledBy);
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("accessibilityLiveRegion"): {
+ fromRawValue(
+ context,
+ value,
+ accessibilityLiveRegion,
+ defaults.accessibilityLiveRegion);
+ canonicalAccessibilityLiveRegion_ = accessibilityLiveRegion;
+ return;
+ }
+ RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityHint);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLanguage);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityShowsLargeContentViewer);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLargeContentTitle);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityValue);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityActions);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityViewIsModal);
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("accessibilityElementsHidden"): {
+ fromRawValue(
+ context,
+ value,
+ accessibilityElementsHidden,
+ defaults.accessibilityElementsHidden);
+ canonicalAccessibilityElementsHidden_ = accessibilityElementsHidden;
+ return;
+ }
+ RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityIgnoresInvertColors);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityRespondsToUserInteraction);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(onAccessibilityTap);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(onAccessibilityMagicTap);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(onAccessibilityEscape);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(onAccessibilityAction);
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("importantForAccessibility"): {
+ fromRawValue(
+ context,
+ value,
+ importantForAccessibility,
+ defaults.importantForAccessibility);
+ canonicalImportantForAccessibility_ = importantForAccessibility;
+ return;
+ }
+ RAW_SET_PROP_SWITCH_CASE_BASIC(role);
+ RAW_SET_PROP_SWITCH_CASE(testId, "testID");
case CONSTEXPR_RAW_PROPS_KEY_HASH("accessibilityRole"): {
AccessibilityTraits traits = AccessibilityTraits::None;
std::string roleString;
@@ -299,6 +592,223 @@ void AccessibilityProps::setProp(
accessibilityRole = roleString;
return;
}
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-label"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ fromRawValue(context, value, accessibilityLabel);
+ } else {
+ accessibilityLabel = canonicalAccessibilityLabel_;
+ }
+ return;
+ }
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-labelledby"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ if (value.hasType()) {
+ accessibilityLabelledBy = parseCommaSeparatedList((std::string)value);
+ } else {
+ fromRawValue(context, value, accessibilityLabelledBy);
+ }
+ } else {
+ accessibilityLabelledBy = defaults.accessibilityLabelledBy;
+ }
+ return;
+ }
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-live"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ if (value.hasType()) {
+ auto str = (std::string)value;
+ if (str == "off") {
+ accessibilityLiveRegion = AccessibilityLiveRegion::None;
+ } else {
+ fromRawValue(context, value, accessibilityLiveRegion);
+ }
+ }
+ } else {
+ accessibilityLiveRegion = canonicalAccessibilityLiveRegion_;
+ }
+ return;
+ }
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-hidden"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ fromRawValue(context, value, accessibilityElementsHidden);
+ if (accessibilityElementsHidden) {
+ importantForAccessibility =
+ ImportantForAccessibility::NoHideDescendants;
+ } else {
+ importantForAccessibility = canonicalImportantForAccessibility_;
+ }
+ } else {
+ accessibilityElementsHidden = canonicalAccessibilityElementsHidden_;
+ importantForAccessibility = canonicalImportantForAccessibility_;
+ }
+ return;
+ }
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-busy"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ if (!accessibilityState.has_value()) {
+ accessibilityState = AccessibilityState{};
+ }
+ fromRawValue(context, value, accessibilityState->busy);
+ } else {
+ if (accessibilityState.has_value()) {
+ accessibilityState->busy = AccessibilityState{}.busy;
+ if (*accessibilityState == AccessibilityState{}) {
+ accessibilityState = std::nullopt;
+ }
+ }
+ }
+ return;
+ }
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-checked"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ if (!accessibilityState.has_value()) {
+ accessibilityState = AccessibilityState{};
+ }
+ if (value.hasType()) {
+ if ((std::string)value == "mixed") {
+ accessibilityState->checked = AccessibilityState::Mixed;
+ }
+ } else if (value.hasType()) {
+ accessibilityState->checked = (bool)value
+ ? AccessibilityState::Checked
+ : AccessibilityState::Unchecked;
+ }
+ } else {
+ if (accessibilityState.has_value()) {
+ accessibilityState->checked = AccessibilityState{}.checked;
+ if (*accessibilityState == AccessibilityState{}) {
+ accessibilityState = std::nullopt;
+ }
+ }
+ }
+ return;
+ }
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-disabled"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ if (!accessibilityState.has_value()) {
+ accessibilityState = AccessibilityState{};
+ }
+ fromRawValue(context, value, accessibilityState->disabled);
+ } else {
+ if (accessibilityState.has_value()) {
+ accessibilityState->disabled = AccessibilityState{}.disabled;
+ if (*accessibilityState == AccessibilityState{}) {
+ accessibilityState = std::nullopt;
+ }
+ }
+ }
+ return;
+ }
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-expanded"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ if (!accessibilityState.has_value()) {
+ accessibilityState = AccessibilityState{};
+ }
+ fromRawValue(context, value, accessibilityState->expanded);
+ } else {
+ if (accessibilityState.has_value()) {
+ accessibilityState->expanded = AccessibilityState{}.expanded;
+ if (*accessibilityState == AccessibilityState{}) {
+ accessibilityState = std::nullopt;
+ }
+ }
+ }
+ return;
+ }
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-selected"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ if (!accessibilityState.has_value()) {
+ accessibilityState = AccessibilityState{};
+ }
+ fromRawValue(context, value, accessibilityState->selected);
+ } else {
+ if (accessibilityState.has_value()) {
+ accessibilityState->selected = AccessibilityState{}.selected;
+ if (*accessibilityState == AccessibilityState{}) {
+ accessibilityState = std::nullopt;
+ }
+ }
+ }
+ return;
+ }
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-valuemax"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ if (value.hasType()) {
+ accessibilityValue.max = (int)value;
+ }
+ } else {
+ accessibilityValue.max = std::nullopt;
+ }
+ return;
+ }
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-valuemin"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ if (value.hasType()) {
+ accessibilityValue.min = (int)value;
+ }
+ } else {
+ accessibilityValue.min = std::nullopt;
+ }
+ return;
+ }
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-valuenow"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ if (value.hasType()) {
+ accessibilityValue.now = (int)value;
+ }
+ } else {
+ accessibilityValue.now = std::nullopt;
+ }
+ return;
+ }
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("aria-valuetext"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ if (value.hasType()) {
+ accessibilityValue.text = (std::string)value;
+ }
+ } else {
+ accessibilityValue.text = std::nullopt;
+ }
+ return;
+ }
}
}
diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/AccessibilityProps.h b/packages/react-native/ReactCommon/react/renderer/components/view/AccessibilityProps.h
index c664b8461e2f..625b71707174 100644
--- a/packages/react-native/ReactCommon/react/renderer/components/view/AccessibilityProps.h
+++ b/packages/react-native/ReactCommon/react/renderer/components/view/AccessibilityProps.h
@@ -62,6 +62,15 @@ class AccessibilityProps {
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const;
#endif
+
+ private:
+ // Canonical prop values for overlap detection between aria-* and
+ // accessibility* props. These track the values from the explicit
+ // accessibility* props, separate from any aria-* override.
+ std::string canonicalAccessibilityLabel_;
+ AccessibilityLiveRegion canonicalAccessibilityLiveRegion_{AccessibilityLiveRegion::None};
+ ImportantForAccessibility canonicalImportantForAccessibility_{ImportantForAccessibility::Auto};
+ bool canonicalAccessibilityElementsHidden_{false};
};
} // namespace facebook::react
diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/platform/android/react/renderer/components/view/HostPlatformViewProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/platform/android/react/renderer/components/view/HostPlatformViewProps.cpp
index 3237f54cbdce..b47d7f1beec6 100644
--- a/packages/react-native/ReactCommon/react/renderer/components/view/platform/android/react/renderer/components/view/HostPlatformViewProps.cpp
+++ b/packages/react-native/ReactCommon/react/renderer/components/view/platform/android/react/renderer/components/view/HostPlatformViewProps.cpp
@@ -141,7 +141,23 @@ HostPlatformViewProps::HostPlatformViewProps(
rawProps,
"nextFocusUp",
sourceProps.nextFocusUp,
- {})) {}
+ {})) {
+ if (!ReactNativeFeatureFlags::enableCppPropsIteratorSetter()) {
+ if (ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ // tabIndex -> focusable
+ auto* tabIndexValue = rawProps.at("tabIndex", nullptr, nullptr);
+ if (tabIndexValue != nullptr) {
+ if (tabIndexValue->hasValue()) {
+ int tabIndex = 0;
+ fromRawValue(context, *tabIndexValue, tabIndex);
+ focusable = tabIndex == 0;
+ } else {
+ focusable = {};
+ }
+ }
+ }
+ }
+}
#define VIEW_EVENT_CASE(eventType) \
case CONSTEXPR_RAW_PROPS_KEY_HASH("on" #eventType): { \
@@ -181,6 +197,19 @@ void HostPlatformViewProps::setProp(
RAW_SET_PROP_SWITCH_CASE_BASIC(nextFocusLeft);
RAW_SET_PROP_SWITCH_CASE_BASIC(nextFocusRight);
RAW_SET_PROP_SWITCH_CASE_BASIC(nextFocusUp);
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("tabIndex"): {
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ if (value.hasValue()) {
+ int tabIndex = 0;
+ fromRawValue(context, value, tabIndex);
+ focusable = tabIndex == 0;
+ } else {
+ focusable = defaults.focusable;
+ }
+ return;
+ }
}
}
diff --git a/packages/react-native/ReactCommon/react/renderer/core/Props.cpp b/packages/react-native/ReactCommon/react/renderer/core/Props.cpp
index 1f86cfb08665..3b7d126bb6d7 100644
--- a/packages/react-native/ReactCommon/react/renderer/core/Props.cpp
+++ b/packages/react-native/ReactCommon/react/renderer/core/Props.cpp
@@ -32,6 +32,20 @@ void Props::initialize(
nativeId = ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
? sourceProps.nativeId
: convertRawProp(context, rawProps, "nativeID", sourceProps.nativeId, {});
+
+ if (!ReactNativeFeatureFlags::enableCppPropsIteratorSetter()) {
+ if (ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ // id -> nativeId
+ auto* idValue = rawProps.at("id", nullptr, nullptr);
+ if (idValue != nullptr) {
+ if (idValue->hasValue()) {
+ fromRawValue(context, *idValue, nativeId);
+ } else {
+ nativeId = {};
+ }
+ }
+ }
+ }
#ifdef RN_SERIALIZABLE_STATE
if (!ReactNativeFeatureFlags::enableExclusivePropsUpdateAndroid()) {
initializeDynamicProps(sourceProps, rawProps, filterObjectKeys);
@@ -48,6 +62,12 @@ void Props::setProp(
case CONSTEXPR_RAW_PROPS_KEY_HASH("nativeID"):
fromRawValue(context, value, nativeId, {});
return;
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("id"):
+ if (!ReactNativeFeatureFlags::enableNativeViewPropTransformations()) {
+ return;
+ }
+ fromRawValue(context, value, nativeId, {});
+ return;
}
}
diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js
index 957047b5bcba..4a3fafab275d 100644
--- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js
+++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js
@@ -507,6 +507,17 @@ const definitions: FeatureFlagDefinitions = {
},
ossReleaseStage: 'none',
},
+ enableNativeViewPropTransformations: {
+ defaultValue: false,
+ metadata: {
+ dateAdded: '2026-02-26',
+ description:
+ 'When enabled, View.js passes aria-*, id, and tabIndex props directly to native, relying on C++ prop parsing instead of JS-side transformations.',
+ expectedReleaseValue: true,
+ purpose: 'experimentation',
+ },
+ ossReleaseStage: 'none',
+ },
enableNetworkEventReporting: {
defaultValue: true,
metadata: {
diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js
index ed2200b5c999..824e851550d6 100644
--- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js
+++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<<6c49874d2e5f3f9dfa6c64945026f4b7>>
+ * @generated SignedSource<<5072c12941f3173be27ac422320c3caa>>
* @flow strict
* @noformat
*/
@@ -91,6 +91,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{
enableMainQueueCoordinatorOnIOS: Getter,
enableModuleArgumentNSNullConversionIOS: Getter,
enableNativeCSSParsing: Getter,
+ enableNativeViewPropTransformations: Getter,
enableNetworkEventReporting: Getter,
enablePreparedTextLayout: Getter,
enablePropsUpdateReconciliationAndroid: Getter,
@@ -377,6 +378,10 @@ export const enableModuleArgumentNSNullConversionIOS: Getter = createNa
* Parse CSS strings using the Fabric CSS parser instead of ViewConfig processing
*/
export const enableNativeCSSParsing: Getter = createNativeFlagGetter('enableNativeCSSParsing', false);
+/**
+ * When enabled, View.js passes aria-*, id, and tabIndex props directly to native, relying on C++ prop parsing instead of JS-side transformations.
+ */
+export const enableNativeViewPropTransformations: Getter = createNativeFlagGetter('enableNativeViewPropTransformations', false);
/**
* Enable network event reporting hooks in each native platform through `NetworkReporter` (Web Perf APIs + CDP). This flag should be combined with `fuseboxNetworkInspectionEnabled` to enable Network CDP debugging.
*/
diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js
index 6c01f3b3bba5..1cc964cdf0f2 100644
--- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js
+++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
- * @generated SignedSource<>
+ * @generated SignedSource<<3fd91ee5bd5c4d53241b3946d8e0b056>>
* @flow strict
* @noformat
*/
@@ -68,6 +68,7 @@ export interface Spec extends TurboModule {
+enableMainQueueCoordinatorOnIOS?: () => boolean;
+enableModuleArgumentNSNullConversionIOS?: () => boolean;
+enableNativeCSSParsing?: () => boolean;
+ +enableNativeViewPropTransformations?: () => boolean;
+enableNetworkEventReporting?: () => boolean;
+enablePreparedTextLayout?: () => boolean;
+enablePropsUpdateReconciliationAndroid?: () => boolean;