From a81789d96e8f05faf683aa8c824dc7bbdaab762b Mon Sep 17 00:00:00 2001 From: Peter Abbondanzo Date: Wed, 9 Oct 2024 16:17:09 -0700 Subject: [PATCH] Fix dark mode text (#46898) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46898 Replaces *many* `Text` component usages with `RNTesterText`: a thin wrapper around `Text` that applies color based on the color scheme chosen by the user. It makes text legible for dark mode across 41 different example files. This changes intentionally do not touch a few larger component sites that expand beyond RNTester, like `Animated` and `NewAppScreen` Changelog: [Internal] Differential Revision: D64053464 --- .../js/components/ListExampleShared.js | 9 +- .../js/components/RNTesterSettingSwitchRow.js | 6 +- .../rn-tester/js/components/RNTesterText.js | 38 + .../AccessibilityAndroidExample.js | 111 +- .../Accessibility/AccessibilityExample.js | 1749 ++++++++--------- .../js/examples/Alert/AlertExample.js | 7 +- .../js/examples/AppState/AppStateExample.js | 22 +- .../examples/Appearance/AppearanceExample.js | 5 +- .../examples/Dimensions/DimensionsExample.js | 14 +- .../js/examples/Image/ImageExample.js | 108 +- .../InvalidProps/InvalidPropsExample.js | 21 +- .../JSResponderHandlerExample.js | 16 +- .../js/examples/Keyboard/KeyboardExample.js | 17 +- .../KeyboardAvoidingViewExample.js | 23 +- .../examples/Layout/LayoutAnimationExample.js | 54 +- .../js/examples/Layout/LayoutEventsExample.js | 38 +- .../js/examples/Layout/LayoutExample.js | 37 +- .../LinearGradient/LinearGradientExample.js | 6 +- .../js/examples/Linking/LinkingExample.js | 18 +- .../js/examples/Modal/ModalOnShow.js | 11 +- .../js/examples/Modal/ModalPresentation.js | 37 +- .../NativeAnimationsExample.js | 26 +- .../OSSLibraryExample/OSSLibraryExample.js | 5 +- .../OrientationChangeExample.js | 7 +- .../Performance/PerformanceApiExample.js | 77 +- .../PermissionsAndroid/PermissionsExample.js | 18 +- .../examples/PixelRatio/PixelRatioExample.js | 40 +- .../PlatformColor/PlatformColorExample.js | 31 +- .../js/examples/Pressable/PressableExample.js | 4 +- .../RefreshControl/RefreshControlExample.js | 12 +- .../examples/ScrollView/ScrollViewExample.js | 43 +- .../SectionList/SectionList-scrollable.js | 21 +- .../js/examples/Share/ShareExample.js | 25 +- .../js/examples/StatusBar/StatusBarExample.js | 124 +- .../js/examples/Switch/SwitchExample.js | 19 +- .../js/examples/Timer/TimerExample.js | 9 +- .../ToastAndroid/ToastAndroidExample.js | 22 +- .../js/examples/Touchable/TouchableExample.js | 113 +- .../NativeCxxModuleExampleExample.js | 24 +- .../TurboModule/SampleLegacyModuleExample.js | 18 +- .../TurboModule/SampleTurboModuleExample.js | 31 +- .../js/examples/Vibration/VibrationExample.js | 23 +- .../rn-tester/js/examples/View/ViewExample.js | 146 +- .../js/examples/WebSocket/WebSocketExample.js | 36 +- 44 files changed, 1651 insertions(+), 1570 deletions(-) create mode 100644 packages/rn-tester/js/components/RNTesterText.js diff --git a/packages/rn-tester/js/components/ListExampleShared.js b/packages/rn-tester/js/components/ListExampleShared.js index 7260f2fbd07e66..05fda82f9cdf9f 100644 --- a/packages/rn-tester/js/components/ListExampleShared.js +++ b/packages/rn-tester/js/components/ListExampleShared.js @@ -10,8 +10,9 @@ 'use strict'; -const React = require('react'); -const { +import RNTesterText from './RNTesterText'; +import React from 'react'; +import { ActivityIndicator, Animated, Image, @@ -22,7 +23,7 @@ const { TextInput, TouchableHighlight, View, -} = require('react-native'); +} from 'react-native'; export type Item = { title: string, @@ -260,7 +261,7 @@ function renderSmallSwitchOption( } return ( - {label}: + {label}: { return ( - {label} + {label} ); diff --git a/packages/rn-tester/js/components/RNTesterText.js b/packages/rn-tester/js/components/RNTesterText.js new file mode 100644 index 00000000000000..82cc99f54277bc --- /dev/null +++ b/packages/rn-tester/js/components/RNTesterText.js @@ -0,0 +1,38 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +import type {TextProps} from 'react-native/Libraries/Text/TextProps'; + +import {RNTesterThemeContext} from './RNTesterTheme'; +import React, {useContext, useMemo} from 'react'; +import {Text} from 'react-native'; + +type Props = $ReadOnly<{ + ...TextProps, + variant?: 'body' | 'label' | 'caption', +}>; + +export default function RNTesterText(props: Props): React.Node { + const {style, variant, ...rest} = props; + const theme = useContext(RNTesterThemeContext); + const color = useMemo(() => { + switch (variant) { + case 'body': + return theme.LabelColor; + case 'label': + return theme.SecondaryLabelColor; + case 'caption': + return theme.TertiaryLabelColor; + default: + return theme.LabelColor; + } + }, [variant, theme]); + return ; +} diff --git a/packages/rn-tester/js/examples/Accessibility/AccessibilityAndroidExample.js b/packages/rn-tester/js/examples/Accessibility/AccessibilityAndroidExample.js index 08ea74daba6759..f3713f727593e9 100644 --- a/packages/rn-tester/js/examples/Accessibility/AccessibilityAndroidExample.js +++ b/packages/rn-tester/js/examples/Accessibility/AccessibilityAndroidExample.js @@ -12,15 +12,9 @@ import RNTesterBlock from '../../components/RNTesterBlock'; import RNTesterPage from '../../components/RNTesterPage'; -import { - Alert, - StyleSheet, - Text, - TouchableWithoutFeedback, - View, -} from 'react-native'; - -const React = require('react'); +import RNTesterText from '../../components/RNTesterText'; +import {Alert, StyleSheet, TouchableWithoutFeedback, View} from 'react-native'; +import React from 'react'; const importantForAccessibilityValues = [ 'auto', @@ -67,27 +61,27 @@ class AccessibilityAndroidExample extends React.Component< return ( - - + + Bacon {this.state.count} Ipsum{'\n'} - - Dolor sit amet{'\n'} - Eggsecetur{'\n'} - {'\n'} - + + Dolor sit amet{'\n'} + Eggsecetur{'\n'} + {'\n'} + http://github.com - - + + - Click me + Click me - Clicked {this.state.count} times + Clicked {this.state.count} times @@ -102,7 +96,7 @@ class AccessibilityAndroidExample extends React.Component< ] }> - Hello + Hello - world + world - + Change importantForAccessibility for background layout. - + - Background layout importantForAccessibility - + + Background layout importantForAccessibility + + { importantForAccessibilityValues[ this.state.backgroundImportantForAcc ] } - + - + Change importantForAccessibility for forground layout. - + - Forground layout importantForAccessibility - + + Forground layout importantForAccessibility + + { importantForAccessibilityValues[ this.state.forgroundImportantForAcc ] } - + - + In the following example, the words "test", "inline links", "another link", and "link that spans multiple lines because the text is so long", should each be independently focusable elements, announced as their content followed by ", Link". - - + + They should be focused in order from top to bottom *after* the contents of the entire paragraph. - - + + Focusing on the paragraph itself should also announce that there are "links available", and opening Talkback's links menu should show these same links. - - + + Clicking on each link, or selecting the link From Talkback's links menu should trigger an alert. - - + + The links that wraps to multiple lines will intentionally only draw a focus outline around the first line, but using the "explore by touch" tap-and-drag gesture should move focus to this link even if the second line is touched. - - + + Using the "Explore by touch" gesture and touching an area that is *not* a link should move focus to the entire paragraph. - - Example - + + Example + This is a{' '} - { Alert.alert('pressed test'); }}> test - {' '} + {' '} of{' '} - { Alert.alert('pressed Inline Links'); }}> inline links - {' '} + {' '} in React Native. Here's{' '} - { Alert.alert('pressed another link'); }}> another link - + . Here is a{' '} - { Alert.alert('pressed long link'); }}> link that spans multiple lines because the text is so long. - + This sentence has no links in it. - + ); @@ -241,6 +239,9 @@ class AccessibilityAndroidExample extends React.Component< } const styles = StyleSheet.create({ + buttonText: { + color: 'black', + }, touchableContainer: { position: 'absolute', left: 10, diff --git a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js index 2848d48c6a4b4e..308ec71e43ba98 100644 --- a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js +++ b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js @@ -13,15 +13,13 @@ import type {PressEvent} from 'react-native/Libraries/Types/CoreEventTypes'; import type {EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter'; -import {RNTesterThemeContext} from '../../components/RNTesterTheme'; - -const RNTesterBlock = require('../../components/RNTesterBlock'); -const checkImageSource = require('./check.png'); -const mixedCheckboxImageSource = require('./mixed.png'); -const uncheckImageSource = require('./uncheck.png'); -const React = require('react'); -const {createRef} = require('react'); -const { +import RNTesterBlock from '../../components/RNTesterBlock'; +import RNTesterText from '../../components/RNTesterText'; +import checkImageSource from './check.png'; +import mixedCheckboxImageSource from './mixed.png'; +import uncheckImageSource from './uncheck.png'; +import React, {createRef} from 'react'; +import { AccessibilityInfo, Alert, Button, @@ -31,13 +29,12 @@ const { ScrollView, StyleSheet, Switch, - Text, TextInput, TouchableNativeFeedback, TouchableOpacity, TouchableWithoutFeedback, View, -} = require('react-native'); +} from 'react-native'; const styles = StyleSheet.create({ sectionContainer: { @@ -106,214 +103,208 @@ const styles = StyleSheet.create({ class AccessibilityExample extends React.Component<{}> { render(): React.Node { return ( - - {theme => ( - - - - Text's accessibilityLabel is the raw text itself unless it is - set explicitly. - - + + + + Text's accessibilityLabel is the raw text itself unless it is set + explicitly. + + - - - This text component's accessibilityLabel is set explicitly. - - + + + This text component's accessibilityLabel is set explicitly. + + - - - This is text one. - This is text two. - - + + + + This is text one. + + + This is text two. + + + - - - This is text one. - This is text two. - - + + + + This is text one. + + + This is text two. + + + - - - This is text one. - This is text two. - - + + + + This is text one. + + + This is text two. + + + - - - - This view's children are hidden from the accessibility tree - - - + + + + This view's children are hidden from the accessibility tree + + + - {/* Android screen readers will say the accessibility hint instead of the text + {/* Android screen readers will say the accessibility hint instead of the text since the view doesn't have a label. */} - - - This is text one. - This is text two. - - + + + + This is text one. + + + This is text two. + + + - - - This is text one. - This is text two. - - + + + + This is text one. + + + This is text two. + + + - - - This is a title. - - + + + This is a title. + + - - - This is a title. - - + + This is a title. + - - Alert.alert('Link has been clicked!')} - accessibilityRole="link"> - - - Click me - - - - + + Alert.alert('Link has been clicked!')} + accessibilityRole="link"> + + Click me + + + - - Alert.alert('Button has been pressed!')} - accessibilityRole="button"> - Click me - - + + Alert.alert('Button has been pressed!')} + accessibilityRole="button"> + Click me + + - - Alert.alert('Button has been pressed!')} - accessibilityRole="button" - accessibilityState={{disabled: true}} - disabled={true}> - - - I am disabled. Clicking me will not trigger any action. - - - - + + Alert.alert('Button has been pressed!')} + accessibilityRole="button" + accessibilityState={{disabled: true}} + disabled={true}> + + + I am disabled. Clicking me will not trigger any action. + + + + - - Alert.alert('Disabled Button has been pressed!')} - accessibilityLabel={ - 'You are pressing Disabled TouchableOpacity' - } - accessibilityState={{disabled: true}}> - - - I am disabled. Clicking me will not trigger any action. - - - - - - - - This view is selected and disabled. - - - + + Alert.alert('Disabled Button has been pressed!')} + accessibilityLabel={'You are pressing Disabled TouchableOpacity'} + accessibilityState={{disabled: true}}> + + + I am disabled. Clicking me will not trigger any action. + + + + + + + This view is selected and disabled. + + - - - - Accessible view with label, hint, role, and state - - - + + + + Accessible view with label, hint, role, and state + + + - - - - Accessible view with label, hint, role, and state - - - + + + + Accessible view with label, hint, role, and state + + + - - - - Mail Address - - - - First Name - - - - - - - - Enable Notifications - - - - + + + Mail Address + + First Name + - )} - + + + + + Enable Notifications + + + + + ); } } @@ -321,170 +312,138 @@ class AccessibilityExample extends React.Component<{}> { class AutomaticContentGrouping extends React.Component<{}> { render(): React.Node { return ( - - {theme => ( - - - - - - Text number 1 with a role - - - Text number 2 - - - - - - - { - switch (event.nativeEvent.actionName) { - case 'cut': - Alert.alert('Alert', 'cut action success'); - break; - case 'copy': - Alert.alert('Alert', 'copy action success'); - break; - case 'paste': - Alert.alert('Alert', 'paste action success'); - break; - } - }} - accessibilityRole="button"> - - - Text number 1 - - - Text number 2Text number 3 - - - - + + + + + + Text number 1 with a role + + Text number 2 + + + - - - - Text number 1 - - - - + + { + switch (event.nativeEvent.actionName) { + case 'cut': + Alert.alert('Alert', 'cut action success'); + break; + case 'copy': + Alert.alert('Alert', 'copy action success'); + break; + case 'paste': + Alert.alert('Alert', 'paste action success'); + break; + } + }} + accessibilityRole="button"> + + Text number 1 + + Text number 2 + Text number 3 + + + + - - - - - Text number 1 - - console.warn('onPress child')} - accessible={false} - accessibilityLabel="this is my label" - accessibilityRole="image" - accessibilityState={{disabled: true}} - accessibilityValue={{ - text: 'this is the accessibility value', - }}> - - Text number 3 - - - - - + + + + Text number 1 + + + + - + + + + Text number 1 - + focusable={true} + onPress={() => console.warn('onPress child')} + accessible={false} + accessibilityLabel="this is my label" + accessibilityRole="image" + accessibilityState={{disabled: true}} + accessibilityValue={{ + text: 'this is the accessibility value', + }}> + Text number 3 - + + + - - - - Text number 2 - - Text number 3Text number 4 - - - - + + + + + - - console.warn('onPress child')} - accessible={true} - accessibilityRole="button"> - - - - - + + + + Text number 2 + + Text number 3 + Text number 4 + + + + - - - - - + + console.warn('onPress child')} + accessible={true} + accessibilityRole="button"> + + + + + + + + + - )} - + + ); } } @@ -516,20 +475,14 @@ class CheckboxExample extends React.Component< render(): React.Node { return ( - - {theme => ( - - - Checkbox example - - - )} - + + Checkbox example + ); } } @@ -554,20 +507,14 @@ class SwitchExample extends React.Component< render(): React.Node { return ( - - {theme => ( - - - Switch example - - - )} - + + Switch example + ); } } @@ -626,9 +573,9 @@ class SelectionExample extends React.Component< }} style={styles.touchable} accessibilityHint={accessibilityHint}> - + {`Selectable TouchableOpacity Example ${touchableHint}`} - + - {theme => ( - - - Expandable element example - - - )} - + + Expandable element example + ); } } @@ -756,59 +697,55 @@ class NestedCheckBox extends React.Component< render(): React.Node { return ( - - {theme => ( - - - - Meat - - - - Beef - - - - Bacon - - - )} - + + + + Meat + + + + Beef + + + + Bacon + + ); } } @@ -816,220 +753,168 @@ class NestedCheckBox extends React.Component< class AccessibilityRoleAndStateExample extends React.Component<{}> { render(): React.Node { const content = [ - This is some text, - This is some text, - This is some text, - This is some text, - This is some text, - This is some text, - This is some text, + This is some text, + This is some text, + This is some text, + This is some text, + This is some text, + This is some text, + This is some text, ]; return ( - - {theme => ( - - - - - {content} - - - - - - - {content} - - - - - - - {content} - - - - - - - - Alert example - - - - - - Combobox example - - - - - Menu example - - - - - Menu bar example - - - - - Menu item example - - - - - Progress bar example - - - - - Radio button example - - - - - Radio group example - - - - - Scrollbar example - - - - - Spin button example - - - - - - Tab example - - - - - Tab list example - - - - - Timer example - - - - - Toolbar example - - - - - State busy example - - - - - Drop Down List example - - - - - Pager example - - - - - Toggle Button example - - - - - Viewgroup example - - - - - Webview example - - - - - - Nested checkbox with delayed state change - - - - + + + + {content} + + + + + {content} + + + + + {content} + + + + + + Alert example + + + + Combobox example + + + Menu example + + + Menu bar example + + + Menu item example + + + Progress bar example + + + Radio button example + + + Radio group example + + + Scrollbar example + + + Spin button example + + + + Tab example + + + Tab list example + + + Timer example + + + Toolbar example + + + State busy example + + + Drop Down List example + + + Pager example + + + Toggle Button example + + + Viewgroup example + + + Webview example + + + + + Nested checkbox with delayed state change + + - )} - + + ); } } @@ -1037,150 +922,138 @@ class AccessibilityRoleAndStateExample extends React.Component<{}> { class AccessibilityActionsExample extends React.Component<{}> { render(): React.Node { return ( - - {theme => ( - - - { - switch (event.nativeEvent.actionName) { - case 'activate': - Alert.alert('Alert', 'View is clicked'); - break; - } - }}> - Click me - - + + + { + switch (event.nativeEvent.actionName) { + case 'activate': + Alert.alert('Alert', 'View is clicked'); + break; + } + }}> + Click me + + - - { - switch (event.nativeEvent.actionName) { - case 'cut': - Alert.alert('Alert', 'cut action success'); - break; - case 'copy': - Alert.alert('Alert', 'copy action success'); - break; - case 'paste': - Alert.alert('Alert', 'paste action success'); - break; - } - }}> - - This view supports many actions. - - - + + { + switch (event.nativeEvent.actionName) { + case 'cut': + Alert.alert('Alert', 'cut action success'); + break; + case 'copy': + Alert.alert('Alert', 'copy action success'); + break; + case 'paste': + Alert.alert('Alert', 'paste action success'); + break; + } + }}> + This view supports many actions. + + - - { - switch (event.nativeEvent.actionName) { - case 'increment': - Alert.alert('Alert', 'increment action success'); - break; - case 'decrement': - Alert.alert('Alert', 'decrement action success'); - break; - } - }}> - Slider - - + + { + switch (event.nativeEvent.actionName) { + case 'increment': + Alert.alert('Alert', 'increment action success'); + break; + case 'decrement': + Alert.alert('Alert', 'decrement action success'); + break; + } + }}> + Slider + + - - { - switch (event.nativeEvent.actionName) { - case 'cut': - Alert.alert('Alert', 'cut action success'); - break; - case 'copy': - Alert.alert('Alert', 'copy action success'); - break; - case 'paste': - Alert.alert('Alert', 'paste action success'); - break; - } - }} - onPress={() => Alert.alert('Button has been pressed!')} - accessibilityRole="button"> - - - Click me - - - - + + { + switch (event.nativeEvent.actionName) { + case 'cut': + Alert.alert('Alert', 'cut action success'); + break; + case 'copy': + Alert.alert('Alert', 'copy action success'); + break; + case 'paste': + Alert.alert('Alert', 'paste action success'); + break; + } + }} + onPress={() => Alert.alert('Button has been pressed!')} + accessibilityRole="button"> + + Click me + + + - -