-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Mobile] Add external link inline component #33210
Changes from all commits
3eafe48
268032b
776e27c
f0e6c45
a43a43e
81ff9e1
ecc59c9
f3edfeb
4d2ff4f
9e751d1
8e12d48
4991880
34d57bb
b9c0c64
8727684
ade76f0
70c13f9
ce04ee0
dec04bc
4eba51b
85f8a01
0c82978
d449feb
60ade1d
8e51b87
5f71efe
3f092cf
52d62bc
5a3584e
5a701e5
67cd28b
79ea8b5
8ee995c
8d4d17e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,7 @@ | |
* WordPress dependencies | ||
*/ | ||
import { __, sprintf } from '@wordpress/i18n'; | ||
import { | ||
LinkSettingsNavigation, | ||
FooterMessageLink, | ||
} from '@wordpress/components'; | ||
import { LinkSettingsNavigation } from '@wordpress/components'; | ||
import { isURL } from '@wordpress/url'; | ||
import { useDispatch } from '@wordpress/data'; | ||
import { store as noticesStore } from '@wordpress/notices'; | ||
|
@@ -35,15 +32,9 @@ const EmbedLinkSettings = ( { | |
autoFocus, | ||
autoFill: true, | ||
}, | ||
footer: { | ||
label: ( | ||
<FooterMessageLink | ||
href={ __( | ||
'https://wordpress.org/support/article/embeds/' | ||
) } | ||
value={ __( 'Learn more about embeds' ) } | ||
/> | ||
), | ||
help: { | ||
url: __( 'https://wordpress.org/support/article/embeds/' ), | ||
moreLinkText: __( 'Learn more about embeds' ), | ||
Comment on lines
-38
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤔 This comment is quite broad and provides more questions than solutions. I understand if we'd prefer to move forward without addressing this. The replacement of the composable approach with a prop-driven approach is less than ideal IMO. I.e. the ability to pass React elements means we have more optionality in regards to what we pass without defining additional props. The prop-driven approach limits us to a very specific composition defined within For this specific example of My understanding for the rationale for this change is similar to my previous comment, it is difficult to pass both a string and a interactive link and not run into accessibility label issues. |
||
separatorType: 'topFullWidth', | ||
}, | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,23 +2,42 @@ | |
* External dependencies | ||
*/ | ||
|
||
import { TouchableOpacity, Text, Linking } from 'react-native'; | ||
import { Text, Linking } from 'react-native'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { external, Icon } from '@wordpress/icons'; | ||
import { usePreferredColorSchemeStyle } from '@wordpress/compose'; | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import style from './style.native.scss'; | ||
|
||
export function ExternalLink( { href, children } ) { | ||
const externalLink = usePreferredColorSchemeStyle( | ||
style[ 'external-link' ], | ||
style[ 'external-link__dark' ] | ||
); | ||
|
||
if ( typeof children !== 'string' ) { | ||
throw new Error( | ||
__( | ||
'The ExternalLink component only accepts a string as a child element. This is to ensure the element can be read by screen readers.' | ||
) | ||
); | ||
} | ||
|
||
return ( | ||
<TouchableOpacity | ||
<Text | ||
style={ externalLink } | ||
onPress={ () => Linking.openURL( href ) } | ||
accessibilityLabel={ __( 'Open link in a browser' ) } | ||
accessibilityLabel={ children } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To avoid future a11y label issues, it may be worth throwing an error in this component if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed that's a good idea, I've added an error message in 8d4d17e. |
||
accessibilityHint={ __( 'Opens link in a browser' ) } | ||
accessibilityRole={ 'link' } | ||
> | ||
<Text>{ children }</Text> | ||
<Icon icon={ external } /> | ||
</TouchableOpacity> | ||
{ children } | ||
</Text> | ||
); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
.external-link { | ||
color: $blue-50; | ||
} | ||
|
||
.external-link__dark { | ||
color: $blue-30; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# HelpText | ||
|
||
Renders help text with a `ExternalLink` appended at the end of the text. | ||
|
||
## Usage | ||
|
||
```jsx | ||
import { HelpText } from '@wordpress/components'; | ||
|
||
const HelpfulText = () => ( | ||
<HelpText url="https://wordpress.org" moreLinkText="Learn More">WordPress is an easy to use publishing tool</HelpText> | ||
); | ||
``` | ||
|
||
## Props | ||
|
||
The text that's displayed within the component's tags will be "clickable" and extenal link will be added at the end. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This statement feels out of place underneath the heading of "Props." Was this intended to be a description of the Also, I believe this only to be true on native, as it currently applies an |
||
|
||
### `url` | ||
|
||
An optional URL to link, opens in a new window when tapped or clicked. | ||
|
||
- Type: `String` | ||
- Required: No | ||
|
||
### `moreLinkText` | ||
|
||
An optional text string to display as the link label for the optional `url` prop. | ||
|
||
- Type: `String` | ||
- Required: No |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import { ExternalLink } from '../external-link'; | ||
|
||
/** | ||
* @param {Object} props | ||
* @param {string} props.moreLinkText | ||
* @param {import('react').ReactNode} props.children | ||
* @param {string} props.url | ||
*/ | ||
const HelpText = ( { moreLinkText, children, url } ) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤔 This comment is quite broad and provides more questions than solutions. I understand if we'd prefer to move forward without addressing this. This is the first time I have encountered us adding a web component. That may be OK, but it is a bit odd to add it when there is no usage of it anywhere. For the most part, I've seen the web project lead, and the native project sometimes splits existing web components. Additionally, there is a I raise this primarily as it is largely uncharted territory for me. WDYT? |
||
return ( | ||
<div> | ||
{ children } | ||
{ children && url && ' ' } | ||
{ url && ( | ||
<ExternalLink href={ url }>{ moreLinkText }</ExternalLink> | ||
) } | ||
</div> | ||
); | ||
}; | ||
|
||
export default HelpText; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { View, Text, AccessibilityInfo, Linking } from 'react-native'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { ExternalLink } from '@wordpress/components'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { usePreferredColorSchemeStyle } from '@wordpress/compose'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import styles from './styles.scss'; | ||
|
||
const HelpText = ( { moreLinkText, children, url, separatorType } ) => { | ||
if ( typeof children === 'string' ) { | ||
children = ( | ||
<Text style={ styles[ 'help-text__text' ] }>{ children }</Text> | ||
); | ||
} | ||
|
||
const separatorStyle = usePreferredColorSchemeStyle( | ||
styles[ 'help-text__separator' ], | ||
styles[ 'help-text__separator--dark' ] | ||
); | ||
|
||
const hasMoreLink = url && moreLinkText; | ||
|
||
return ( | ||
<> | ||
{ separatorType === 'topFullWidth' && ( | ||
<View style={ separatorStyle } /> | ||
) } | ||
<View style={ styles[ 'help-text' ] }> | ||
<Text | ||
accessibilityRole={ url ? 'link' : 'text' } | ||
accessibilityHint={ | ||
url ? __( 'Opens link in a browser' ) : null | ||
} | ||
Comment on lines
+39
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This combination of props along with the children text appear to work for iOS. However, it does not appear to read any label loud for Android. From testing briefly, a quick fix is adding Using something like |
||
onPress={ () => { | ||
AccessibilityInfo.isScreenReaderEnabled().then( | ||
( enabled ) => enabled && Linking.openURL( url ) | ||
); | ||
} } | ||
Comment on lines
+43
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to onPress={ url ? () => { /* [...] */ } : undefined } |
||
style={ styles[ 'help-text__text' ] } | ||
> | ||
{ children } | ||
{ children && hasMoreLink && ' ' } | ||
{ hasMoreLink && ( | ||
<ExternalLink href={ url }> | ||
{ moreLinkText } | ||
</ExternalLink> | ||
) } | ||
</Text> | ||
</View> | ||
</> | ||
); | ||
}; | ||
|
||
export default HelpText; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
.help-text { | ||
flex-direction: row; | ||
min-height: 48; | ||
align-items: center; | ||
justify-content: space-between; | ||
} | ||
|
||
.help-text__text { | ||
font-size: 12px; | ||
color: $gray; | ||
} | ||
|
||
.help-text__separator { | ||
background-color: $light-gray-400; | ||
height: 1px; | ||
} | ||
|
||
.help-text__separator--dark { | ||
background-color: $gray-70; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
.help-text { | ||
font-size: 12px; | ||
} | ||
|
||
.help-text__text { | ||
color: $gray; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 This comment is quite broad and provides more questions than solutions. I understand if we'd prefer to move forward without addressing this.
Here the web now relies upon passing
help
to a robust, composableTextControl
→BaseControl
component that manages help text and links; native now relies uponHelpText
composed further along in this hook file rather than the previous approach of passinghelp
down toTextControl
→Cell
.While native's
Cell
is quite complex and overly far-reaching — it is quite different from the web's composable approach inBaseControl
and likely not the best abstraction long term — I am hesitant move the help text fromTextControl
→Cell
to inlineHelpText
in this hook as it further diverges web and native component design, which is unfortunate.My interpretation for the rationale of this change is that we cannot pass a multiple children types — string and interactive link — on native. Doing so leads to the
Object Object
accessibility (a11y) label issue. My hope is that we could identify a way to address the a11y label issue while also avoiding the divergence from web'shelp
property here.An example of a possible approach might involve flattening
help
props rather than introducing theHelpText
component. The flattenedhelp
prop could be used for a11y labels, the originalhelp
prop could be rendered as normal for visible UI. Note: I didn't test any performance of such code.