fix accessibility label in notifications (#4305)
* fix accessibility label in notifications * add accessibility options to expand post * inherit from outside, but always include `activate` * include option to disable label/hint on previewable avatar * fix hidden elements still being read on voiceover * make it work for followers too * extract variable * fix hint * update wording elsewherezio/stable
parent
b51640fbc0
commit
708a80e7a7
|
@ -194,10 +194,36 @@ let FeedItem = ({
|
|||
]}
|
||||
href={itemHref}
|
||||
noFeedback
|
||||
accessible={
|
||||
(item.type === 'post-like' && authors.length === 1) ||
|
||||
item.type === 'repost'
|
||||
accessible={!isAuthorsExpanded}
|
||||
accessibilityActions={
|
||||
authors.length > 1
|
||||
? [
|
||||
{
|
||||
name: 'toggleAuthorsExpanded',
|
||||
label: isAuthorsExpanded
|
||||
? _(msg`Collapse list of users`)
|
||||
: _(msg`Expand list of users`),
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
name: 'viewProfile',
|
||||
label: _(
|
||||
msg`View ${
|
||||
authors[0].profile.displayName || authors[0].profile.handle
|
||||
}'s profile`,
|
||||
),
|
||||
},
|
||||
]
|
||||
}
|
||||
onAccessibilityAction={e => {
|
||||
if (e.nativeEvent.actionName === 'activate') {
|
||||
onBeforePress()
|
||||
}
|
||||
if (e.nativeEvent.actionName === 'toggleAuthorsExpanded') {
|
||||
onToggleAuthorsExpanded()
|
||||
}
|
||||
}}
|
||||
onBeforePress={onBeforePress}>
|
||||
<View style={[styles.layoutIcon, a.pr_sm]}>
|
||||
{/* TODO: Prevent conditional rendering and move toward composable
|
||||
|
@ -332,16 +358,14 @@ function CondensedAuthorsList({
|
|||
profile={authors[0].profile}
|
||||
moderation={authors[0].moderation.ui('avatar')}
|
||||
type={authors[0].profile.associated?.labeler ? 'labeler' : 'user'}
|
||||
accessible={false}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<TouchableOpacity
|
||||
accessibilityLabel={_(msg`Show users`)}
|
||||
accessibilityHint={_(
|
||||
msg`Opens an expanded list of users in this notification`,
|
||||
)}
|
||||
accessibilityRole="none"
|
||||
onPress={onToggleAuthorsExpanded}>
|
||||
<View style={styles.avis}>
|
||||
{authors.slice(0, MAX_AUTHORS).map(author => (
|
||||
|
@ -351,6 +375,7 @@ function CondensedAuthorsList({
|
|||
profile={author.profile}
|
||||
moderation={author.moderation.ui('avatar')}
|
||||
type={author.profile.associated?.labeler ? 'labeler' : 'user'}
|
||||
accessible={false}
|
||||
/>
|
||||
</View>
|
||||
))}
|
||||
|
@ -392,48 +417,45 @@ function ExpandedAuthorsList({
|
|||
}, [heightInterp, visible])
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style={[
|
||||
heightStyle,
|
||||
styles.overflowHidden,
|
||||
visible ? s.mb10 : undefined,
|
||||
]}>
|
||||
{authors.map(author => (
|
||||
<NewLink
|
||||
key={author.profile.did}
|
||||
label={_(msg`See profile`)}
|
||||
to={makeProfileLink({
|
||||
did: author.profile.did,
|
||||
handle: author.profile.handle,
|
||||
})}
|
||||
style={styles.expandedAuthor}>
|
||||
<View style={styles.expandedAuthorAvi}>
|
||||
<ProfileHoverCard did={author.profile.did}>
|
||||
<UserAvatar
|
||||
size={35}
|
||||
avatar={author.profile.avatar}
|
||||
moderation={author.moderation.ui('avatar')}
|
||||
type={author.profile.associated?.labeler ? 'labeler' : 'user'}
|
||||
/>
|
||||
</ProfileHoverCard>
|
||||
</View>
|
||||
<View style={s.flex1}>
|
||||
<Text
|
||||
type="lg-bold"
|
||||
numberOfLines={1}
|
||||
style={pal.text}
|
||||
lineHeight={1.2}>
|
||||
{sanitizeDisplayName(
|
||||
author.profile.displayName || author.profile.handle,
|
||||
)}
|
||||
|
||||
<Text style={[pal.textLight]} lineHeight={1.2}>
|
||||
{sanitizeHandle(author.profile.handle)}
|
||||
<Animated.View style={[heightStyle, styles.overflowHidden]}>
|
||||
{visible &&
|
||||
authors.map(author => (
|
||||
<NewLink
|
||||
key={author.profile.did}
|
||||
label={author.profile.displayName || author.profile.handle}
|
||||
accessibilityHint={_(msg`Opens this profile`)}
|
||||
to={makeProfileLink({
|
||||
did: author.profile.did,
|
||||
handle: author.profile.handle,
|
||||
})}
|
||||
style={styles.expandedAuthor}>
|
||||
<View style={styles.expandedAuthorAvi}>
|
||||
<ProfileHoverCard did={author.profile.did}>
|
||||
<UserAvatar
|
||||
size={35}
|
||||
avatar={author.profile.avatar}
|
||||
moderation={author.moderation.ui('avatar')}
|
||||
type={author.profile.associated?.labeler ? 'labeler' : 'user'}
|
||||
/>
|
||||
</ProfileHoverCard>
|
||||
</View>
|
||||
<View style={s.flex1}>
|
||||
<Text
|
||||
type="lg-bold"
|
||||
numberOfLines={1}
|
||||
style={pal.text}
|
||||
lineHeight={1.2}>
|
||||
{sanitizeDisplayName(
|
||||
author.profile.displayName || author.profile.handle,
|
||||
)}
|
||||
|
||||
<Text style={[pal.textLight]} lineHeight={1.2}>
|
||||
{sanitizeHandle(author.profile.handle)}
|
||||
</Text>
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</NewLink>
|
||||
))}
|
||||
</View>
|
||||
</NewLink>
|
||||
))}
|
||||
</Animated.View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -64,6 +64,8 @@ export const Link = memo(function Link({
|
|||
anchorNoUnderline,
|
||||
navigationAction,
|
||||
onBeforePress,
|
||||
accessibilityActions,
|
||||
onAccessibilityAction,
|
||||
...props
|
||||
}: Props) {
|
||||
const t = useTheme()
|
||||
|
@ -89,6 +91,11 @@ export const Link = memo(function Link({
|
|||
[closeModal, navigation, navigationAction, href, openLink, onBeforePress],
|
||||
)
|
||||
|
||||
const accessibilityActionsWithActivate = [
|
||||
...(accessibilityActions || []),
|
||||
{name: 'activate', label: title},
|
||||
]
|
||||
|
||||
if (noFeedback) {
|
||||
return (
|
||||
<WebAuxClickWrapper>
|
||||
|
@ -97,6 +104,14 @@ export const Link = memo(function Link({
|
|||
onPress={onPress}
|
||||
accessible={accessible}
|
||||
accessibilityRole="link"
|
||||
accessibilityActions={accessibilityActionsWithActivate}
|
||||
onAccessibilityAction={e => {
|
||||
if (e.nativeEvent.actionName === 'activate') {
|
||||
onPress()
|
||||
} else {
|
||||
onAccessibilityAction?.(e)
|
||||
}
|
||||
}}
|
||||
{...props}
|
||||
android_ripple={{
|
||||
color: t.atoms.bg_contrast_25.backgroundColor,
|
||||
|
|
|
@ -53,6 +53,7 @@ interface PreviewableUserAvatarProps extends BaseUserAvatarProps {
|
|||
profile: AppBskyActorDefs.ProfileViewBasic
|
||||
disableHoverCard?: boolean
|
||||
onBeforePress?: () => void
|
||||
accessible?: boolean
|
||||
}
|
||||
|
||||
const BLUR_AMOUNT = isWeb ? 5 : 100
|
||||
|
@ -386,6 +387,7 @@ let PreviewableUserAvatar = ({
|
|||
profile,
|
||||
disableHoverCard,
|
||||
onBeforePress,
|
||||
accessible = true,
|
||||
...rest
|
||||
}: PreviewableUserAvatarProps): React.ReactNode => {
|
||||
const {_} = useLingui()
|
||||
|
@ -399,7 +401,12 @@ let PreviewableUserAvatar = ({
|
|||
return (
|
||||
<ProfileHoverCard did={profile.did} disable={disableHoverCard}>
|
||||
<Link
|
||||
label={_(msg`See profile`)}
|
||||
label={
|
||||
accessible
|
||||
? _(msg`${profile.displayName || profile.handle}'s avatar`)
|
||||
: undefined
|
||||
}
|
||||
accessibilityHint={accessible ? _(msg`Opens this profile`) : undefined}
|
||||
to={makeProfileLink({
|
||||
did: profile.did,
|
||||
handle: profile.handle,
|
||||
|
|
Loading…
Reference in New Issue