|
|
@@ -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;
|
|
|
}
|
|
|
|