From 71fda5e0c2093380d08761c945f1e3029af6697f Mon Sep 17 00:00:00 2001 From: Gabriel Donadel Dall'Agnol Date: Tue, 27 Sep 2022 04:05:15 -0700 Subject: [PATCH] feat: Add alt prop to Image component (#34550) Summary: This adds the `alt` prop to the `Image` component as requested on https://github.com/facebook/react-native/issues/34424. Using this new `alt` prop enables the `accessibility` prop and passes down the alt text to `accessibilityLabel`. This PR also updates RNTester ImageExample in order to facilitate the manual QA. #### Open questions - ~~On web `alt` text is displayed on the page if the image can't be loaded for some reason, should we implement this same behavior if the `Image` component fails to load `source`?~~ Not for now ## Changelog [General] [Added] - Add alt prop to Image component Pull Request resolved: https://github.com/facebook/react-native/pull/34550 Test Plan: 1. Open the RNTester app and navigate to the Image page 2. Test the `alt` prop through the `Accessibility Label via alt prop` section, this can be tested either by enabling Voice Over if you're using a real device or through the Accessibility Inspector if you're using a simulator https://user-images.githubusercontent.com/11707729/187790249-0d851363-c30e-41b6-8c24-73e72467f4ba.mov Reviewed By: lunaleaps Differential Revision: D39618453 Pulled By: cipolleschi fbshipit-source-id: 0e26b2574514e76ce7e98ddb578f587a9cc30ee9 --- Libraries/Image/Image.android.js | 4 +- Libraries/Image/Image.d.ts | 8 + Libraries/Image/Image.ios.js | 3 +- Libraries/Image/ImageProps.js | 9 +- .../js/examples/Image/ImageExample.js | 387 ++++++++++++------ 5 files changed, 289 insertions(+), 122 deletions(-) diff --git a/Libraries/Image/Image.android.js b/Libraries/Image/Image.android.js index 4e8973bb69fe63..f0851b1af2cdc7 100644 --- a/Libraries/Image/Image.android.js +++ b/Libraries/Image/Image.android.js @@ -181,7 +181,9 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => { ? loadingIndicatorSource.uri : null, ref: forwardedRef, - accessibilityLabel: props['aria-label'] ?? props.accessibilityLabel, + accessible: props.alt !== undefined ? true : props.accessible, + accessibilityLabel: + props['aria-label'] ?? props.accessibilityLabel ?? props.alt, accessibilityState: { busy: props['aria-busy'] ?? props.accessibilityState?.busy, checked: props['aria-checked'] ?? props.accessibilityState?.checked, diff --git a/Libraries/Image/Image.d.ts b/Libraries/Image/Image.d.ts index 6f22ea0563c97a..21c873844a56e9 100644 --- a/Libraries/Image/Image.d.ts +++ b/Libraries/Image/Image.d.ts @@ -245,6 +245,14 @@ export interface ImagePropsBase * A static image to display while downloading the final image off the network. */ defaultSource?: ImageURISource | number | undefined; + + /** + * The text that's read by the screen reader when the user interacts with + * the image. + * + * See https://reactnative.dev/docs/image#alt + */ + alt?: string | undefined; } export interface ImageProps extends ImagePropsBase { diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index c66ccd5865fdfe..456fae9de5d48a 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -173,7 +173,8 @@ const BaseImage = (props: ImagePropsType, forwardedRef) => { this._loadEventFired(`✔ onLoadStart (+${Date.now() - mountTime}ms)`) } @@ -185,7 +185,7 @@ class NetworkImageCallbackExample extends React.Component< {this.state.startLoadPrefetched ? ( this._loadEventFired( `✔ (prefetched) onLoadStart (+${Date.now() - mountTime}ms)`, @@ -213,7 +213,9 @@ class NetworkImageCallbackExample extends React.Component< } /> ) : null} - {this.state.events.join('\n')} + + {this.state.events.join('\n')} + ); } @@ -246,7 +248,7 @@ class NetworkImageExample extends React.Component< <> this.setState({loading: true})} onError={e => this.setState({error: e.nativeEvent.error, loading: false}) @@ -296,16 +298,8 @@ class ImageSizeExample extends React.Component< render(): React.Node { return ( - - + + Actual dimensions:{'\n'} Width: {this.state.width}, Height: {this.state.height} @@ -354,7 +348,7 @@ class MultipleSourcesExample extends React.Component< render(): React.Node { return ( - + Decrease image size @@ -367,7 +361,7 @@ class MultipleSourcesExample extends React.Component< - + Refresh Image @@ -486,7 +480,7 @@ class OnLayoutExample extends React.Component< return ( Adjust the image size to trigger the OnLayout handler. - + Decrease image size @@ -500,7 +494,7 @@ class OnLayoutExample extends React.Component< ); @@ -818,72 +949,32 @@ exports.examples = [ render: function (): React.Node { return ( - - + + @@ -897,23 +988,15 @@ exports.examples = [ @@ -925,12 +1008,12 @@ exports.examples = [ render: function (): React.Node { return ( - - - - - - + + + + + + ); }, @@ -939,7 +1022,7 @@ exports.examples = [ title: 'Nesting content inside component', render: function (): React.Node { return ( - + React @@ -971,22 +1054,26 @@ exports.examples = [ @@ -996,19 +1083,35 @@ exports.examples = [ @@ -1017,22 +1120,38 @@ exports.examples = [ @@ -1042,19 +1161,35 @@ exports.examples = [ @@ -1075,14 +1210,14 @@ exports.examples = [ Contain Cover @@ -1091,14 +1226,14 @@ exports.examples = [ Fill Scale Down @@ -1232,7 +1367,7 @@ exports.examples = [ description: 'Images shipped in a separate native bundle', render: function (): React.Node { return ( - + ); @@ -1294,6 +1429,20 @@ exports.examples = [ ); }, }, + { + title: 'Accessibility Label via alt prop', + description: + 'Using the alt prop markes an element as being accessibile, and passes the alt text to accessibilityLabel', + render: function (): React.Node { + return ( + Picture of people standing around a table + ); + }, + }, { title: 'Fade Duration', description: