Skip to content
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

Add accessibility props for external buttons #57

Merged
merged 3 commits into from
Jun 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/assets/icon-external-arrow-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 39 additions & 6 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import React from 'react';
import {useTheme} from '@shopify/restyle';
import {Platform, StyleSheet, Text, TextStyle, TouchableOpacity, ViewStyle, ActivityIndicator} from 'react-native';
import {Theme} from 'shared/theme';
import {
Platform,
StyleSheet,
Text,
TextStyle,
TouchableOpacity,
ViewStyle,
ActivityIndicator,
AccessibilityRole,
} from 'react-native';
import {Theme, palette} from 'shared/theme';
import {useI18n} from '@shopify/react-i18n';

import {Box} from './Box';
import {Ripple} from './Ripple';
import {Icon} from './Icon';

export interface ButtonProps {
text?: string;
Expand All @@ -13,9 +24,19 @@ export interface ButtonProps {
color?: keyof Theme['colors'];
disabled?: boolean;
loading?: boolean;
externalLink?: boolean;
}

export const Button = ({text, onPress, variant, color: buttonColorName, disabled, loading}: ButtonProps) => {
export const Button = ({
text,
onPress,
variant,
color: buttonColorName,
disabled,
loading,
externalLink,
}: ButtonProps) => {
const [i18n] = useI18n();
const theme = useTheme<Theme>();
const variantProps = theme.buttonVariants[variant];
const disabledProps = disabled ? variantProps.disabled || {} : {};
Expand All @@ -26,6 +47,14 @@ export const Button = ({text, onPress, variant, color: buttonColorName, disabled
const buttonColor = buttonColorName && theme.colors[buttonColorName];

const onPressHandler = loading ? () => {} : onPress;
const externalLinkProps = externalLink
? {
accessibilityLabel: text,
accessibilityHint: i18n.translate('Home.ExternalLinkHint'),
accessibilityRole: 'link' as AccessibilityRole,
}
: {};
const externalArrowIcon = textColor === palette.white ? 'icon-external-arrow-light' : 'icon-external-arrow';

const content = (
<Box
Expand All @@ -34,24 +63,28 @@ export const Button = ({text, onPress, variant, color: buttonColorName, disabled
justifyContent="center"
style={{backgroundColor: color, minHeight: height, borderWidth, borderColor: buttonColor}}
paddingHorizontal="m"
flexDirection="row"
>
{loading ? (
<ActivityIndicator color={textColor} size="large" />
) : (
<Text style={{color: textColor || buttonColor, fontWeight, fontFamily, fontSize}}>{text}</Text>
<>
<Text style={{color: textColor || buttonColor, fontWeight, fontFamily, fontSize}}>{text}</Text>
{externalLink && <Icon name={externalArrowIcon} />}
</>
)}
</Box>
);

if (Platform.OS === 'android') {
return (
<Ripple rippleContainerBorderRadius={4} rippleDuration={500} onPress={onPressHandler}>
<Ripple rippleContainerBorderRadius={4} rippleDuration={500} onPress={onPressHandler} {...externalLinkProps}>
{content}
</Ripple>
);
}
return (
<TouchableOpacity onPress={onPressHandler} style={styles.stretch} disabled={disabled}>
<TouchableOpacity onPress={onPressHandler} style={styles.stretch} disabled={disabled} {...externalLinkProps}>
{content}
</TouchableOpacity>
);
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import IconChevron from 'assets/icon-chevron.svg';
import IconClose from 'assets/icon-close.svg';
import IconEnterCode from 'assets/icon-enter-code.svg';
import IconExternalArrow from 'assets/icon-external-arrow.svg';
import IconExternalArrowLight from 'assets/icon-external-arrow-light.svg';
import IconImportant from 'assets/icon-important.svg';
import IconMessages from 'assets/icon-messages.svg';
import IconNotify from 'assets/icon-notify.svg';
Expand Down Expand Up @@ -38,6 +39,7 @@ const ICONS = {
'icon-enter-code': IconEnterCode,
'icon-ellipsis': Ellipsis,
'icon-external-arrow': IconExternalArrow,
'icon-external-arrow-light': IconExternalArrowLight,
'icon-important': IconImportant,
'icon-messages': IconMessages,
'icon-notify': IconNotify,
Expand Down
1 change: 1 addition & 0 deletions src/locale/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"NoConnectivity": "No internet connection",
"NoConnectivityDetailed": "COVID Shield needs internet access to continue checking for potential exposure.",
"AppName": "COVID Shield",
"ExternalLinkHint": "Opens in a new window",
"LastCheckedMinutes": {
"One": "Last checked {number} minute ago",
"Other": "Last checked {number} minutes ago"
Expand Down
1 change: 1 addition & 0 deletions src/locale/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"NoConnectivity": "Aucune connexion Internet",
"NoConnectivityDetailed": "COVID Shield a besoin d'un accès Internet pour continuer de vérifier les expositions potentielles à la COVID-19.",
"AppName": "COVID Shield",
"ExternalLinkHint": "Ouvre dans une nouvelle fenêtre",
"LastCheckedMinutes": {
"One": "Dernière vérification il y a {number} minute",
"Other": "Dernière vérification il y a {number} minutes"
Expand Down
2 changes: 1 addition & 1 deletion src/locale/translations/index.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/screens/home/views/DiagnosedShareView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const DiagnosedShareView = () => {
text={i18n.translate('Home.SignalDataSharedCTA')}
variant="bigHollow"
color="bodyText"
externalLink
onPress={toSymptomTracker}
/>
</Box>
Expand Down
2 changes: 1 addition & 1 deletion src/screens/home/views/DiagnosedView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const DiagnosedView = () => {
{i18n.translate(pluralizeKey('Home.SignalDataSharedDetailed', daysDiff), {number: daysDiff})}
</Text>
<Box alignSelf="stretch">
<Button text={i18n.translate('Home.SignalDataSharedCTA')} variant="bigFlat" onPress={onAction} />
<Button text={i18n.translate('Home.SignalDataSharedCTA')} variant="bigFlat" externalLink onPress={onAction} />
</Box>
</BaseHomeView>
);
Expand Down
2 changes: 1 addition & 1 deletion src/screens/home/views/ExposureView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const ExposureView = () => {
</Text>
<LastCheckedDisplay />
<Box alignSelf="stretch" marginTop="l">
<Button text={i18n.translate('Home.SeeGuidance')} variant="bigFlat" onPress={onAction} />
<Button text={i18n.translate('Home.SeeGuidance')} variant="bigFlat" externalLink onPress={onAction} />
</Box>
</BaseHomeView>
);
Expand Down
17 changes: 12 additions & 5 deletions src/screens/home/views/InfoShareView.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React, {useCallback} from 'react';
import {TouchableOpacity, Linking} from 'react-native';
import {TouchableOpacity, TouchableOpacityProps, Linking} from 'react-native';
import {Box, Text, Icon, IconProps} from 'components';
import {useNavigation} from '@react-navigation/native';
import {useI18n} from '@shopify/react-i18n';

interface InfoShareItemProps {
interface InfoShareItemProps extends TouchableOpacityProps {
onPress: () => void;
text: string;
icon: IconProps['name'];
}
const InfoShareItem = ({onPress, text, icon}: InfoShareItemProps) => (
const InfoShareItem = ({onPress, text, icon, ...touchableProps}: InfoShareItemProps) => (
<>
<TouchableOpacity onPress={onPress}>
<TouchableOpacity onPress={onPress} {...touchableProps}>
<Box paddingVertical="s" flexDirection="row" alignContent="center" justifyContent="space-between">
<Text variant="bodyText" marginVertical="s" color="overlayBodyText">
{text}
Expand Down Expand Up @@ -39,7 +39,14 @@ export const InfoShareView = () => {
return (
<>
<Box paddingHorizontal="m" borderRadius={10} backgroundColor="infoBlockNeutralBackground">
<InfoShareItem onPress={onSymptomps} text={i18n.translate('Info.CheckSymptoms')} icon="icon-external-arrow" />
<InfoShareItem
onPress={onSymptomps}
text={i18n.translate('Info.CheckSymptoms')}
icon="icon-external-arrow"
accessibilityLabel={i18n.translate('Info.CheckSymptoms')}
accessibilityRole="link"
Copy link
Member

@svinkle svinkle May 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing accessibleLabel here. Otherwise only the accessibilityHint text will be announced.

accessibilityHint={i18n.translate('Home.ExternalLinkHint')}
/>
<InfoShareItem onPress={onShare} text={i18n.translate('Info.TellAFriend')} icon="icon-share" />
<InfoShareItem onPress={onLearnMore} text={i18n.translate('Info.LearnMore')} icon="icon-chevron" />
</Box>
Expand Down