feat: aria-labels and buttons contain more media info (#1743)

* feat: aria-labels and buttons contain more media info

fixes #1733

* fix lint
This commit is contained in:
Nolan Lawson 2020-04-25 19:03:39 -07:00 committed by GitHub
parent bfb1da6bd0
commit 1f0d67fcc4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 53 additions and 17 deletions

View file

@ -36,11 +36,13 @@ function cleanupText (text) {
export function getAccessibleLabelForStatus (originalAccount, account, plainTextContent, export function getAccessibleLabelForStatus (originalAccount, account, plainTextContent,
timeagoFormattedDate, spoilerText, showContent, timeagoFormattedDate, spoilerText, showContent,
reblog, notification, visibility, omitEmojiInDisplayNames, reblog, notification, visibility, omitEmojiInDisplayNames,
disableLongAriaLabels) { disableLongAriaLabels, showMedia, showPoll) {
const originalAccountDisplayName = getAccountAccessibleName(originalAccount, omitEmojiInDisplayNames) const originalAccountDisplayName = getAccountAccessibleName(originalAccount, omitEmojiInDisplayNames)
const contentTextToShow = (showContent || !spoilerText) const contentTextToShow = (showContent || !spoilerText)
? cleanupText(plainTextContent) ? cleanupText(plainTextContent)
: `Content warning: ${cleanupText(spoilerText)}` : `Content warning: ${cleanupText(spoilerText)}`
const mediaTextToShow = showMedia && 'has media'
const pollTextToShow = showPoll && 'has poll'
const privacyText = getPrivacyText(visibility) const privacyText = getPrivacyText(visibility)
if (disableLongAriaLabels) { if (disableLongAriaLabels) {
@ -53,6 +55,8 @@ export function getAccessibleLabelForStatus (originalAccount, account, plainText
getNotificationText(notification, omitEmojiInDisplayNames), getNotificationText(notification, omitEmojiInDisplayNames),
originalAccountDisplayName, originalAccountDisplayName,
contentTextToShow, contentTextToShow,
mediaTextToShow,
pollTextToShow,
timeagoFormattedDate, timeagoFormattedDate,
`@${originalAccount.acct}`, `@${originalAccount.acct}`,
privacyText, privacyText,

View file

@ -2,7 +2,7 @@
<button id={elementId} <button id={elementId}
type="button" type="button"
class="inline-media play-video-button focus-after {$largeInlineMedia ? '' : 'fixed-size'} {type === 'audio' ? 'play-audio-button' : ''}" class="inline-media play-video-button focus-after {$largeInlineMedia ? '' : 'fixed-size'} {type === 'audio' ? 'play-audio-button' : ''}"
aria-label="Play {type === 'video' ? 'video' : 'audio'}: {description}" aria-label={videoOrAudioButtonLabel}
{tabindex} {tabindex}
aria-hidden={ariaHidden} aria-hidden={ariaHidden}
style={inlineMediaStyle}> style={inlineMediaStyle}>
@ -25,7 +25,7 @@
<button id={elementId} <button id={elementId}
type="button" type="button"
class="inline-media show-image-button focus-after {$largeInlineMedia ? '' : 'fixed-size'}" class="inline-media show-image-button focus-after {$largeInlineMedia ? '' : 'fixed-size'}"
aria-label="Show image: {description}" aria-label={imageButtonAriaLabel}
title={description} title={description}
on:mouseover="set({mouseover: event})" on:mouseover="set({mouseover: event})"
style={inlineMediaStyle} style={inlineMediaStyle}
@ -34,7 +34,7 @@
> >
{#if type === 'gifv' && $autoplayGifs && !blurhash} {#if type === 'gifv' && $autoplayGifs && !blurhash}
<AutoplayVideo <AutoplayVideo
ariaLabel="Animated GIF: {description}" ariaLabel="Animated image: {description}"
poster={previewUrl} poster={previewUrl}
src={url} src={url}
width={inlineWidth} width={inlineWidth}
@ -44,7 +44,7 @@
{:elseif type === 'gifv'} {:elseif type === 'gifv'}
<NonAutoplayGifv <NonAutoplayGifv
class={noNativeWidthHeight ? 'no-native-width-height' : ''} class={noNativeWidthHeight ? 'no-native-width-height' : ''}
label="Animated GIF: {description}" label="Animated image: {description}"
poster={previewUrl} poster={previewUrl}
{blurhash} {blurhash}
src={url} src={url}
@ -166,7 +166,13 @@ export default {
} }
}, },
tabindex: ({ showAsSensitive }) => showAsSensitive ? '-1' : '0', tabindex: ({ showAsSensitive }) => showAsSensitive ? '-1' : '0',
ariaHidden: ({ showAsSensitive }) => showAsSensitive ariaHidden: ({ showAsSensitive }) => showAsSensitive,
imageButtonAriaLabel: ({ type, description }) => (
`Show ${type === 'gifv' ? 'animated image' : 'image'}: ${description}`
),
videoOrAudioButtonLabel: ({ type, description }) => (
`Play ${type === 'video' ? 'video' : 'audio'}: ${description}`
)
}, },
methods: { methods: {
onClick () { onClick () {

View file

@ -280,11 +280,14 @@
reblog: ({ status }) => status.reblog, reblog: ({ status }) => status.reblog,
ariaLabel: ({ ariaLabel: ({
originalAccount, account, plainTextContent, timeagoFormattedDate, spoilerText, originalAccount, account, plainTextContent, timeagoFormattedDate, spoilerText,
showContent, reblog, notification, visibility, $omitEmojiInDisplayNames, $disableLongAriaLabels showContent, reblog, notification, visibility, $omitEmojiInDisplayNames, $disableLongAriaLabels,
showMedia, showPoll
}) => ( }) => (
getAccessibleLabelForStatus(originalAccount, account, plainTextContent, getAccessibleLabelForStatus(originalAccount, account, plainTextContent,
timeagoFormattedDate, spoilerText, showContent, timeagoFormattedDate, spoilerText, showContent,
reblog, notification, visibility, $omitEmojiInDisplayNames, $disableLongAriaLabels) reblog, notification, visibility, $omitEmojiInDisplayNames, $disableLongAriaLabels,
showMedia, showPoll
)
), ),
showHeader: ({ notification, status, timelineType }) => ( showHeader: ({ notification, status, timelineType }) => (
(notification && ['reblog', 'favourite', 'poll'].includes(notification.type)) || (notification && ['reblog', 'favourite', 'poll'].includes(notification.type)) ||

View file

@ -1,4 +1,11 @@
import { closeDialogButton, getNthStatus, getNthStatusSelector, modalDialogContents, scrollToStatus } from '../utils' import {
closeDialogButton,
getNthStatus,
getNthStatusMediaButton,
getNthStatusSelector,
modalDialogContents,
scrollToStatus
} from '../utils'
import { loginAsFoobar } from '../roles' import { loginAsFoobar } from '../roles'
import { Selector as $ } from 'testcafe' import { Selector as $ } from 'testcafe'
import { homeTimeline } from '../fixtures' import { homeTimeline } from '../fixtures'
@ -13,8 +20,10 @@ test('shows sensitive images and videos', async t => {
const videoIdx = homeTimeline.findIndex(_ => _.content === 'secret video') const videoIdx = homeTimeline.findIndex(_ => _.content === 'secret video')
await scrollToStatus(t, 1 + kittenIdx) await scrollToStatus(t, 1 + kittenIdx)
await t.expect($(`${getNthStatusSelector(1 + kittenIdx)} .status-media img`).getAttribute('src')).match(/^blob:http:\/\/localhost/) await t.expect($(`${getNthStatusSelector(1 + kittenIdx)} .status-media img`).getAttribute('src'))
.match(/^blob:http:\/\/localhost/)
.click($(`${getNthStatusSelector(1 + kittenIdx)} .status-sensitive-media-button`)) .click($(`${getNthStatusSelector(1 + kittenIdx)} .status-sensitive-media-button`))
.expect($(getNthStatusMediaButton(1 + kittenIdx)).getAttribute('aria-label')).eql('Show image: kitten')
.expect($(`${getNthStatusSelector(1 + kittenIdx)} .status-media img`).getAttribute('alt')).eql('kitten') .expect($(`${getNthStatusSelector(1 + kittenIdx)} .status-media img`).getAttribute('alt')).eql('kitten')
.expect($(`${getNthStatusSelector(1 + kittenIdx)} .status-media img`).getAttribute('src')).match(/^http:\/\//) .expect($(`${getNthStatusSelector(1 + kittenIdx)} .status-media img`).getAttribute('src')).match(/^http:\/\//)
.hover(getNthStatus(1 + videoIdx)) .hover(getNthStatus(1 + videoIdx))
@ -31,7 +40,9 @@ test('click and close image and video modals', async t => {
await scrollToStatus(t, 1 + videoIdx) await scrollToStatus(t, 1 + videoIdx)
await t.expect(modalDialogContents.exists).notOk() await t.expect(modalDialogContents.exists).notOk()
.click($(`${getNthStatusSelector(1 + videoIdx)} .play-video-button`)) .expect($(getNthStatusMediaButton(1 + videoIdx)).getAttribute('aria-label'))
.eql('Play video: kitten')
.click($(getNthStatusMediaButton(1 + videoIdx)))
.expect(modalDialogContents.exists).ok() .expect(modalDialogContents.exists).ok()
.expect($('.modal-dialog video').getAttribute('src')).contains('mp4') .expect($('.modal-dialog video').getAttribute('src')).contains('mp4')
.expect($('.modal-dialog video').getAttribute('poster')).contains('png') .expect($('.modal-dialog video').getAttribute('poster')).contains('png')
@ -39,7 +50,9 @@ test('click and close image and video modals', async t => {
.expect(modalDialogContents.exists).notOk() .expect(modalDialogContents.exists).notOk()
.hover(getNthStatus(1 + kittenIdx - 1)) .hover(getNthStatus(1 + kittenIdx - 1))
.hover(getNthStatus(1 + kittenIdx)) .hover(getNthStatus(1 + kittenIdx))
.click($(`${getNthStatusSelector(1 + kittenIdx)} .show-image-button`)) .expect($(getNthStatusMediaButton(1 + kittenIdx)).getAttribute('aria-label'))
.eql('Show animated image: kitten')
.click($(getNthStatusMediaButton(1 + kittenIdx)))
.expect(modalDialogContents.exists).ok() .expect(modalDialogContents.exists).ok()
.expect($('.modal-dialog video').getAttribute('src')).contains('mp4') .expect($('.modal-dialog video').getAttribute('src')).contains('mp4')
.expect($('.modal-dialog video').getAttribute('poster')).contains('png') .expect($('.modal-dialog video').getAttribute('poster')).contains('png')

View file

@ -1,6 +1,6 @@
import { loginAsFoobar } from '../roles' import { loginAsFoobar } from '../roles'
import { getNthStatus } from '../utils' import { getNthStatus } from '../utils'
import { postAs, postEmptyStatusWithMediaAs } from '../serverActions' import { createPollAs, postAs, postEmptyStatusWithMediaAs } from '../serverActions'
fixture`120-status-aria-label.js` fixture`120-status-aria-label.js`
.page`http://localhost:4002` .page`http://localhost:4002`
@ -11,7 +11,17 @@ test('aria-labels for statuses with no content text', async t => {
await t await t
.hover(getNthStatus(1)) .hover(getNthStatus(1))
.expect(getNthStatus(1).getAttribute('aria-label')).match( .expect(getNthStatus(1).getAttribute('aria-label')).match(
/foobar, (.+ ago|just now), @foobar, Public/i /foobar, has media, (.+ ago|just now), @foobar, Public/i
)
})
test('aria-labels for statuses with polls', async t => {
await createPollAs('foobar', 'here is my poll', ['yolo', 'whatever'])
await loginAsFoobar(t)
await t
.hover(getNthStatus(1))
.expect(getNthStatus(1).getAttribute('aria-label')).match(
/foobar, here is my poll, has poll, (.+ ago|just now), @foobar, Public/i
) )
}) })