Преглед изворни кода

Neo: strip React Native-only text props on web (#5867)

sanitizeWebTextProps dropped only some RN props before Text and H1-H6
render a span or heading on web. The rest, like adjustsFontSizeToFit or
minimumFontScale, reached the DOM and React logged "React does not
recognize the `adjustsFontSizeToFit` prop on a DOM element".

Drop every RN TextProps key that isn't a DOM attribute, from a list
typed against RN's TextProps. Keep the role, aria-label, id and onClick
mappings. testID becomes data-testid, disabled stops onPress as it does
on native, and style arrays and pointerEvents flatten into one style
object, as View does on web. Native is unchanged: the sanitizer only
runs on web.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Andrey Yasko пре 6 дана
родитељ
комит
f302b6d8cf
1 измењених фајлова са 73 додато и 19 уклоњено
  1. 73 19
      neo/packages/app/design/typography.tsx

+ 73 - 19
neo/packages/app/design/typography.tsx

@@ -1,4 +1,4 @@
-import { Text as NativeText, Platform, type TextProps as RNTextProps } from 'react-native'
+import { Text as NativeText, Platform, StyleSheet, type TextProps as RNTextProps } from 'react-native'
 import type { ReactNode } from 'react'
 import { decodeText } from 'app/lib/util'
 import { appSetting } from 'app/lib/util'
@@ -29,29 +29,83 @@ export type HeadingProps = TextProps & {
     children?: ReactNode
 }
 
+/**
+ * React Native `TextProps` (react-native/Libraries/Text/Text.d.ts) that are not DOM attributes.
+ * Web drops them, so React doesn't log "React does not recognize the `adjustsFontSizeToFit` prop
+ * on a DOM element". `role`, `id`, `style` and the `aria-*` props are valid DOM and pass through.
+ */
+const NATIVE_ONLY_TEXT_PROPS = new Set<keyof RNTextProps>([
+    // Text
+    'allowFontScaling',
+    'disabled',
+    'ellipsizeMode',
+    'lineBreakMode',
+    'maxFontSizeMultiplier',
+    'minimumFontScale',
+    'nativeID',
+    'numberOfLines',
+    'pointerEvents',
+    'pressRetentionOffset',
+    'testID',
+    'onLayout',
+    'onLongPress',
+    'onPress',
+    'onPressIn',
+    'onPressOut',
+    'onTextLayout',
+    // Text, iOS
+    'adjustsFontSizeToFit',
+    'dynamicTypeRamp',
+    'lineBreakStrategyIOS',
+    'suppressHighlighting',
+    // Text, Android
+    'android_hyphenationFrequency',
+    'dataDetectorType',
+    'selectable',
+    'selectionColor',
+    'textBreakStrategy',
+    // Accessibility
+    'accessible',
+    'accessibilityActions',
+    'accessibilityElementsHidden',
+    'accessibilityHint',
+    'accessibilityIgnoresInvertColors',
+    'accessibilityLabel',
+    'accessibilityLabelledBy',
+    'accessibilityLanguage',
+    'accessibilityLargeContentTitle',
+    'accessibilityLiveRegion',
+    'accessibilityRespondsToUserInteraction',
+    'accessibilityRole',
+    'accessibilityShowsLargeContentViewer',
+    'accessibilityState',
+    'accessibilityValue',
+    'accessibilityViewIsModal',
+    'importantForAccessibility',
+    'onAccessibilityAction',
+    'onAccessibilityEscape',
+    'onAccessibilityTap',
+    'onMagicTap',
+    'screenReaderFocusable',
+])
+
 function sanitizeWebTextProps(props: Record<string, any>) {
     if (!props || !isWeb) return props;
-    const {
-        accessible,
-        accessibilityRole,
-        accessibilityLabel,
-        accessibilityHint,
-        allowFontScaling,
-        ellipsizeMode,
-        numberOfLines,
-        selectable,
-        textBreakStrategy,
-        maxFontSizeMultiplier,
-        onPress,
-        onLongPress,
-        onTextLayout,
-        nativeID,
-        ...domProps
-    } = props;
+    const domProps: Record<string, any> = {};
+    for (const key of Object.keys(props)) {
+        if (!NATIVE_ONLY_TEXT_PROPS.has(key as keyof RNTextProps)) domProps[key] = props[key];
+    }
+    const { accessibilityRole, accessibilityLabel, nativeID, onPress, disabled, testID, pointerEvents } = props;
     if (accessibilityRole && !domProps.role) domProps.role = accessibilityRole;
     if (accessibilityLabel && !domProps['aria-label']) domProps['aria-label'] = accessibilityLabel;
     if (nativeID && !domProps.id) domProps.id = nativeID;
-    if (onPress && !domProps.onClick) domProps.onClick = onPress;
+    // A disabled Text ignores presses on native too
+    if (onPress && !disabled && !domProps.onClick) domProps.onClick = onPress;
+    if (testID && domProps['data-testid'] == null) domProps['data-testid'] = testID;
+    // RN styles can be arrays; a DOM element takes one object
+    if (domProps.style || pointerEvents) {
+        domProps.style = { ...StyleSheet.flatten(domProps.style), ...(pointerEvents ? { pointerEvents } : null) };
+    }
     return domProps;
 }