diff --git a/Libraries/Components/ScrollView/ScrollViewNativeComponent.js b/Libraries/Components/ScrollView/ScrollViewNativeComponent.js index 49fa3bfc3c..ec2166b7f2 100644 --- a/Libraries/Components/ScrollView/ScrollViewNativeComponent.js +++ b/Libraries/Components/ScrollView/ScrollViewNativeComponent.js @@ -11,6 +11,7 @@ import type {ScrollViewNativeProps as Props} from './ScrollViewNativeComponentType'; import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; import * as NativeComponentRegistry from '../../NativeComponent/NativeComponentRegistry'; +import {ConditionallyIgnoredEventHandlers} from '../../NativeComponent/ViewConfigIgnore'; import Platform from '../../Utilities/Platform'; const RCTScrollViewViewConfig = @@ -138,6 +139,14 @@ const RCTScrollViewViewConfig = snapToOffsets: true, snapToStart: true, zoomScale: true, + ...ConditionallyIgnoredEventHandlers({ + onScrollBeginDrag: true, + onMomentumScrollEnd: true, + onScrollEndDrag: true, + onMomentumScrollBegin: true, + onScrollToTop: true, + onScroll: true, + }), }, }; diff --git a/Libraries/Components/TextInput/RCTTextInputViewConfig.js b/Libraries/Components/TextInput/RCTTextInputViewConfig.js index ee479389c8..52134db441 100644 --- a/Libraries/Components/TextInput/RCTTextInputViewConfig.js +++ b/Libraries/Components/TextInput/RCTTextInputViewConfig.js @@ -9,6 +9,7 @@ */ import type {PartialViewConfig} from '../../Renderer/shims/ReactNativeTypes'; +import {ConditionallyIgnoredEventHandlers} from '../../NativeComponent/ViewConfigIgnore'; type PartialViewConfigWithoutName = $Rest< PartialViewConfig, @@ -147,6 +148,15 @@ const RCTTextInputViewConfig = { clearTextOnFocus: true, showSoftInputOnFocus: true, autoFocus: true, + ...ConditionallyIgnoredEventHandlers({ + onChange: true, + onSelectionChange: true, + onContentSizeChange: true, + onScroll: true, + onChangeSync: true, + onKeyPressSync: true, + onTextInput: true, + }), }, }; diff --git a/Libraries/Image/ImageViewNativeComponent.js b/Libraries/Image/ImageViewNativeComponent.js index d0e0206827..69b21ea226 100644 --- a/Libraries/Image/ImageViewNativeComponent.js +++ b/Libraries/Image/ImageViewNativeComponent.js @@ -12,6 +12,7 @@ import type {ResolvedAssetSource} from './AssetSourceResolver'; import type {ImageProps} from './ImageProps'; import type {ViewProps} from '../Components/View/ViewPropTypes'; import * as NativeComponentRegistry from '../NativeComponent/NativeComponentRegistry'; +import {ConditionallyIgnoredEventHandlers} from '../NativeComponent/ViewConfigIgnore'; import type {HostComponent} from '../Renderer/shims/ReactNativeTypes'; import type { ColorValue, @@ -125,6 +126,14 @@ const ImageViewViewConfig = tintColor: { process: require('../StyleSheet/processColor'), }, + ...ConditionallyIgnoredEventHandlers({ + onLoadStart: true, + onLoad: true, + onLoadEnd: true, + onProgress: true, + onError: true, + onPartialLoad: true, + }), }, }; diff --git a/Libraries/NativeComponent/PlatformBaseViewConfig.js b/Libraries/NativeComponent/PlatformBaseViewConfig.js index 3d0d35152e..1487637b03 100644 --- a/Libraries/NativeComponent/PlatformBaseViewConfig.js +++ b/Libraries/NativeComponent/PlatformBaseViewConfig.js @@ -11,7 +11,10 @@ import {Platform} from 'react-native'; import type {PartialViewConfig} from '../Renderer/shims/ReactNativeTypes'; import ReactNativeStyleAttributes from '../Components/View/ReactNativeStyleAttributes'; -import {DynamicallyInjectedByGestureHandler} from './ViewConfigIgnore'; +import { + DynamicallyInjectedByGestureHandler, + ConditionallyIgnoredEventHandlers, +} from './ViewConfigIgnore'; type PartialViewConfigWithoutName = $Rest< PartialViewConfig, @@ -446,6 +449,14 @@ const PlatformBaseViewConfig: PartialViewConfigWithoutName = direction: true, style: ReactNativeStyleAttributes, + + ...ConditionallyIgnoredEventHandlers({ + onLayout: true, + onMagicTap: true, + onAccessibilityAction: true, + onAccessibilityEscape: true, + onAccessibilityTap: true, + }), }, }; diff --git a/Libraries/NativeComponent/ViewConfigIgnore.js b/Libraries/NativeComponent/ViewConfigIgnore.js index 78ccbb4044..73442ad705 100644 --- a/Libraries/NativeComponent/ViewConfigIgnore.js +++ b/Libraries/NativeComponent/ViewConfigIgnore.js @@ -8,6 +8,8 @@ * @format */ +import Platform from '../Utilities/Platform'; + const ignoredViewConfigProps = new WeakSet<{...}>(); /** @@ -19,6 +21,32 @@ export function DynamicallyInjectedByGestureHandler(object: T): T { return object; } +/** + * On iOS, ViewManager events declarations generate {eventName}: true entries + * in ViewConfig valueAttributes. In our Static ViewConfig infra, we generate + * these {eventName}: true entries during runtime by inspecting a ViewConfig's + * bubblingEventTypes, and directEventTypes. + * + * However, not all event declarations generate these {eventName}: true entries. + * So, the ViewConfig infra generates extra {eventName}: true entries for some + * events. These extra entries are harmless. So, the logic below makes the ViewConfig + * Validator ignore all extra {eventName}: true entries in static ViewConfig + * validAttributes. + * + * TODO(T110872225): Remove this logic + */ +export function ConditionallyIgnoredEventHandlers( + value: T, +): T | void { + if ( + Platform.OS === 'ios' && + !(global.RN$ViewConfigEventValidAttributesDisabled === true) + ) { + return value; + } + return undefined; +} + export function isIgnored(value: mixed): boolean { if (typeof value === 'object' && value != null) { return ignoredViewConfigProps.has(value); diff --git a/packages/babel-plugin-codegen/__tests__/__snapshots__/index-test.js.snap b/packages/babel-plugin-codegen/__tests__/__snapshots__/index-test.js.snap index cc95c05740..474b387833 100644 --- a/packages/babel-plugin-codegen/__tests__/__snapshots__/index-test.js.snap +++ b/packages/babel-plugin-codegen/__tests__/__snapshots__/index-test.js.snap @@ -24,6 +24,10 @@ interface NativeCommands { const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry'); +const { + ConditionallyIgnoredEventHandlers +} = require('react-native/Libraries/NativeComponent/ViewConfigIgnore'); + const { dispatchCommand } = require(\\"react-native/Libraries/Renderer/shims/ReactNative\\"); @@ -45,7 +49,11 @@ export const __INTERNAL_VIEW_CONFIG = { } }, validAttributes: { - boolean_default_true_optional_both: true + boolean_default_true_optional_both: true, + ...ConditionallyIgnoredEventHandlers({ + onDirectEventDefinedInlineNull: true, + onBubblingEventDefinedInlineNull: true + }) } }; export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG); @@ -85,6 +93,10 @@ interface NativeCommands { const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry'); +const { + ConditionallyIgnoredEventHandlers +} = require('react-native/Libraries/NativeComponent/ViewConfigIgnore'); + const { dispatchCommand } = require(\\"react-native/Libraries/Renderer/shims/ReactNative\\"); @@ -106,7 +118,11 @@ export const __INTERNAL_VIEW_CONFIG = { } }, validAttributes: { - boolean_default_true_optional_both: true + boolean_default_true_optional_both: true, + ...ConditionallyIgnoredEventHandlers({ + onDirectEventDefinedInlineNull: true, + onBubblingEventDefinedInlineNull: true + }) } }; export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG); diff --git a/packages/react-native-codegen/e2e/__tests__/components/__snapshots__/GenerateViewConfigJs-test.js.snap b/packages/react-native-codegen/e2e/__tests__/components/__snapshots__/GenerateViewConfigJs-test.js.snap index 9f50be6680..12fa79b3d4 100644 --- a/packages/react-native-codegen/e2e/__tests__/components/__snapshots__/GenerateViewConfigJs-test.js.snap +++ b/packages/react-native-codegen/e2e/__tests__/components/__snapshots__/GenerateViewConfigJs-test.js.snap @@ -200,6 +200,7 @@ Map { 'use strict'; const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry'); +const {ConditionallyIgnoredEventHandlers} = require('react-native/Libraries/NativeComponent/ViewConfigIgnore'); let nativeComponentName = 'EventNestedObjectPropsNativeComponentView'; @@ -218,6 +219,10 @@ export const __INTERNAL_VIEW_CONFIG = { validAttributes: { disabled: true, + + ...ConditionallyIgnoredEventHandlers({ + onChange: true, + }), }, }; @@ -243,6 +248,7 @@ Map { 'use strict'; const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry'); +const {ConditionallyIgnoredEventHandlers} = require('react-native/Libraries/NativeComponent/ViewConfigIgnore'); let nativeComponentName = 'EventPropsNativeComponentView'; @@ -289,6 +295,15 @@ export const __INTERNAL_VIEW_CONFIG = { validAttributes: { disabled: true, + + ...ConditionallyIgnoredEventHandlers({ + onChange: true, + onEventDirect: true, + onEventDirectWithPaperName: true, + onOrientationChange: true, + onEnd: true, + onEventBubblingWithPaperName: true, + }), }, }; @@ -426,6 +441,7 @@ Map { 'use strict'; const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry'); +const {ConditionallyIgnoredEventHandlers} = require('react-native/Libraries/NativeComponent/ViewConfigIgnore'); let nativeComponentName = 'RCTInterfaceOnlyComponent'; @@ -444,6 +460,10 @@ export const __INTERNAL_VIEW_CONFIG = { validAttributes: { title: true, + + ...ConditionallyIgnoredEventHandlers({ + onChange: true, + }), }, }; diff --git a/packages/react-native-codegen/src/generators/components/GenerateViewConfigJs.js b/packages/react-native-codegen/src/generators/components/GenerateViewConfigJs.js index b1872a9176..3a4bda260c 100644 --- a/packages/react-native-codegen/src/generators/components/GenerateViewConfigJs.js +++ b/packages/react-native-codegen/src/generators/components/GenerateViewConfigJs.js @@ -171,6 +171,23 @@ function normalizeInputEventName(name) { return name; } +// Replicates the behavior of viewConfig in RCTComponentData.m +function getValidAttributesForEvents(events, imports) { + imports.add( + "const {ConditionallyIgnoredEventHandlers} = require('react-native/Libraries/NativeComponent/ViewConfigIgnore');", + ); + + const validAttributes = j.objectExpression( + events.map(eventType => { + return j.property('init', j.identifier(eventType.name), j.literal(true)); + }), + ); + + return j.callExpression(j.identifier('ConditionallyIgnoredEventHandlers'), [ + validAttributes, + ]); +} + function generateBubblingEventInfo(event, nameOveride) { return j.property( 'init', @@ -243,6 +260,13 @@ function buildViewConfig( getReactDiffProcessValue(schemaProp.typeAnnotation), ); }), + ...(componentEvents.length > 0 + ? [ + j.spreadProperty( + getValidAttributesForEvents(componentEvents, imports), + ), + ] + : []), ]); const bubblingEventNames = component.events diff --git a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GenerateViewConfigJs-test.js.snap b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GenerateViewConfigJs-test.js.snap index 757ee7a65f..49f9a83c09 100644 --- a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GenerateViewConfigJs-test.js.snap +++ b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GenerateViewConfigJs-test.js.snap @@ -295,6 +295,7 @@ Map { 'use strict'; const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry'); +const {ConditionallyIgnoredEventHandlers} = require('react-native/Libraries/NativeComponent/ViewConfigIgnore'); let nativeComponentName = 'EventsNestedObjectNativeComponent'; @@ -313,6 +314,10 @@ export const __INTERNAL_VIEW_CONFIG = { validAttributes: { disabled: true, + + ...ConditionallyIgnoredEventHandlers({ + onChange: true, + }), }, }; @@ -338,6 +343,7 @@ Map { 'use strict'; const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry'); +const {ConditionallyIgnoredEventHandlers} = require('react-native/Libraries/NativeComponent/ViewConfigIgnore'); let nativeComponentName = 'EventsNativeComponent'; @@ -373,6 +379,13 @@ export const __INTERNAL_VIEW_CONFIG = { validAttributes: { disabled: true, + + ...ConditionallyIgnoredEventHandlers({ + onChange: true, + onEventDirect: true, + onOrientationChange: true, + onEnd: true, + }), }, }; @@ -398,6 +411,7 @@ Map { 'use strict'; const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry'); +const {ConditionallyIgnoredEventHandlers} = require('react-native/Libraries/NativeComponent/ViewConfigIgnore'); let nativeComponentName = 'RCTInterfaceOnlyComponent'; @@ -420,7 +434,12 @@ export const __INTERNAL_VIEW_CONFIG = { }, }, - validAttributes: {}, + validAttributes: { + ...ConditionallyIgnoredEventHandlers({ + onChange: true, + onDire tChange: true, + }), + }, }; export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG); @@ -688,6 +707,7 @@ Map { 'use strict'; const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry'); +const {ConditionallyIgnoredEventHandlers} = require('react-native/Libraries/NativeComponent/ViewConfigIgnore'); let nativeComponentName = 'RCTInterfaceOnlyComponent'; @@ -706,6 +726,10 @@ export const __INTERNAL_VIEW_CONFIG = { validAttributes: { accessibilityHint: true, + + ...ConditionallyIgnoredEventHandlers({ + onChange: true, + }), }, };