From 8fc9d5c728b4c949ad1bda68ffba4b05b8358284 Mon Sep 17 00:00:00 2001 From: James Teh Date: Sat, 3 Dec 2022 06:54:03 +1000 Subject: [PATCH] feat: Allow image descriptions to be read automatically by screen readers without needing to expand media. (#2269) Fixes #2257. Co-authored-by: Nolan Lawson --- .../_a11y/getAccessibleLabelForStatus.js | 6 ++++- src/routes/_components/status/Status.html | 11 +++++++-- tests/spec/022-status-aria-label.js | 23 ++++++++++++++++++- tests/spec/120-status-aria-label.js | 2 +- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/routes/_a11y/getAccessibleLabelForStatus.js b/src/routes/_a11y/getAccessibleLabelForStatus.js index 18a4e574..bb418c31 100644 --- a/src/routes/_a11y/getAccessibleLabelForStatus.js +++ b/src/routes/_a11y/getAccessibleLabelForStatus.js @@ -37,12 +37,15 @@ function cleanupText (text) { export function getAccessibleLabelForStatus (originalAccount, account, plainTextContent, shortInlineFormattedDate, spoilerText, showContent, reblog, notification, visibility, omitEmojiInDisplayNames, - disableLongAriaLabels, showMedia, showPoll) { + disableLongAriaLabels, showMedia, sensitive, sensitiveShown, mediaAttachments, showPoll) { const originalAccountDisplayName = getAccountAccessibleName(originalAccount, omitEmojiInDisplayNames) const contentTextToShow = (showContent || !spoilerText) ? cleanupText(plainTextContent) : formatIntl('intl.contentWarningContent', { spoiler: cleanupText(spoilerText) }) const mediaTextToShow = showMedia && 'intl.hasMedia' + const mediaDescText = (showMedia && (!sensitive || sensitiveShown)) + ? mediaAttachments.map(media => media.description) + : [] const pollTextToShow = showPoll && 'intl.hasPoll' const privacyText = getPrivacyText(visibility) @@ -57,6 +60,7 @@ export function getAccessibleLabelForStatus (originalAccount, account, plainText originalAccountDisplayName, contentTextToShow, mediaTextToShow, + ...mediaDescText, pollTextToShow, shortInlineFormattedDate, `@${originalAccount.acct}`, diff --git a/src/routes/_components/status/Status.html b/src/routes/_components/status/Status.html index e00a5a01..7cf5c755 100644 --- a/src/routes/_components/status/Status.html +++ b/src/routes/_components/status/Status.html @@ -270,6 +270,13 @@ originalStatus.media_attachments && originalStatus.media_attachments.length ), + mediaAttachments: ({ originalStatus }) => ( + originalStatus.media_attachments + ), + sensitiveShown: ({ $sensitivesShown, uuid }) => !!$sensitivesShown[uuid], + sensitive: ({ originalStatus, $markMediaAsSensitive, $neverMarkMediaAsSensitive }) => ( + !$neverMarkMediaAsSensitive && ($markMediaAsSensitive || originalStatus.sensitive) + ), originalAccountEmojis: ({ originalAccount }) => (originalAccount.emojis || []), originalStatusEmojis: ({ originalStatus }) => (originalStatus.emojis || []), originalAccountDisplayName: ({ originalAccount }) => (originalAccount.display_name || originalAccount.username), @@ -288,12 +295,12 @@ ariaLabel: ({ originalAccount, account, plainTextContent, shortInlineFormattedDate, spoilerText, showContent, reblog, notification, visibility, $omitEmojiInDisplayNames, $disableLongAriaLabels, - showMedia, showPoll + showMedia, sensitive, sensitiveShown, mediaAttachments, showPoll }) => ( getAccessibleLabelForStatus(originalAccount, account, plainTextContent, shortInlineFormattedDate, spoilerText, showContent, reblog, notification, visibility, $omitEmojiInDisplayNames, $disableLongAriaLabels, - showMedia, showPoll + showMedia, sensitive, sensitiveShown, mediaAttachments, showPoll ) ), showHeader: ({ notification, status, timelineType }) => ( diff --git a/tests/spec/022-status-aria-label.js b/tests/spec/022-status-aria-label.js index 114b3503..f2963df1 100644 --- a/tests/spec/022-status-aria-label.js +++ b/tests/spec/022-status-aria-label.js @@ -2,7 +2,7 @@ import { loginAsFoobar } from '../roles' import { generalSettingsButton, getNthShowOrHideButton, - getNthStatus, getNthStatusRelativeDateTime, homeNavButton, + getNthStatus, getNthStatusAndSensitiveButton, getNthStatusRelativeDateTime, homeNavButton, notificationsNavButton, scrollToStatus, settingsNavButton @@ -39,6 +39,7 @@ test('aria-labels for CWed statuses', async t => { .expect(getNthStatus(1 + kittenIdx).getAttribute('aria-label')).match( /foobar, Content warning: kitten CW, .* ago, @foobar, Public/i ) + // toggle the CW button .click(getNthShowOrHideButton(1 + kittenIdx)) .expect(getNthStatus(1 + kittenIdx).getAttribute('aria-label')).match( /foobar, here's a kitten with a CW, .* ago, @foobar, Public/i @@ -47,6 +48,26 @@ test('aria-labels for CWed statuses', async t => { .expect(getNthStatus(1 + kittenIdx).getAttribute('aria-label')).match( /foobar, Content warning: kitten CW, .* ago, @foobar, Public/i ) + // toggle the "show sensitive media" button + .click(getNthStatusAndSensitiveButton(1 + kittenIdx, 1)) + .expect(getNthStatus(1 + kittenIdx).getAttribute('aria-label')).match( + /foobar, Content warning: kitten CW, has media, kitten, .* ago, @foobar, Public/i + ) + .click(getNthStatusAndSensitiveButton(1 + kittenIdx, 1)) + .expect(getNthStatus(1 + kittenIdx).getAttribute('aria-label')).match( + /foobar, Content warning: kitten CW, .* ago, @foobar, Public/i + ) +}) + +test('aria-labels for two media attachments', async t => { + await loginAsFoobar(t) + const twoKittensIdx = homeTimeline.findIndex(_ => _.content === 'here\'s 2 kitten photos') + await scrollToStatus(t, 1 + twoKittensIdx) + await t + .hover(getNthStatus(1 + twoKittensIdx)) + .expect(getNthStatus(1 + twoKittensIdx).getAttribute('aria-label')).match( + /foobar, here's 2 kitten photos, has media, kitten, kitten, .* ago, @foobar, Public/i + ) }) test('aria-labels for notifications', async t => { diff --git a/tests/spec/120-status-aria-label.js b/tests/spec/120-status-aria-label.js index 4ba32d17..43a1d9a3 100644 --- a/tests/spec/120-status-aria-label.js +++ b/tests/spec/120-status-aria-label.js @@ -11,7 +11,7 @@ test('aria-labels for statuses with no content text', async t => { await t .hover(getNthStatus(1)) .expect(getNthStatus(1).getAttribute('aria-label')).match( - /foobar, has media, (.+ ago|just now), @foobar, Public/i + /foobar, has media, kitteh, (.+ ago|just now), @foobar, Public/i ) })