better support for de-emojified user display names (#451)

improvements to #450 to fix #449, especially for aria labels
This commit is contained in:
Nolan Lawson 2018-08-19 19:31:54 -07:00 committed by GitHub
parent 37e12e8d73
commit af1d4b63d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 89 additions and 24 deletions

View file

@ -1,6 +1,6 @@
<a href="/accounts/{verifyCredentials.id}"
class="compose-box-avatar"
aria-label="Profile for {verifyCredentials.display_name || verifyCredentials.acct}">
aria-label="Profile for {accessibleName}">
<Avatar account={verifyCredentials} size="small"/>
</a>
<a class="compose-box-display-name" href="/accounts/{verifyCredentials.id}">
@ -52,6 +52,7 @@
import Avatar from '../Avatar.html'
import { store } from '../../_store/store'
import AccountDisplayName from '../profile/AccountDisplayName.html'
import { removeEmoji } from '../../_utils/removeEmoji'
export default {
components: {
@ -60,7 +61,15 @@
},
store: () => store,
computed: {
verifyCredentials: ({ $currentVerifyCredentials }) => $currentVerifyCredentials
verifyCredentials: ({ $currentVerifyCredentials }) => $currentVerifyCredentials,
emojis: ({ verifyCredentials }) => (verifyCredentials.emojis || []),
displayName: ({ verifyCredentials }) => verifyCredentials.display_name || verifyCredentials.username,
accessibleName: ({ displayName, emojis, $omitEmojiInDisplayNames }) => {
if ($omitEmojiInDisplayNames) {
return removeEmoji(displayName, emojis) || displayName
}
return displayName
}
}
}
</script>

View file

@ -5,12 +5,10 @@
}
</style>
<script>
import { emojifyText, removeEmoji } from '../../_utils/emojifyText'
import { emojifyText } from '../../_utils/emojifyText'
import { store } from '../../_store/store'
import escapeHtml from 'escape-html'
import emojiRegex from 'emoji-regex'
let theEmojiRegex
import { removeEmoji } from '../../_utils/removeEmoji'
export default {
store: () => store,
@ -21,10 +19,9 @@
accountName = escapeHtml(accountName)
if ($omitEmojiInDisplayNames) { // display name emoji are annoying to some screenreader users
theEmojiRegex = theEmojiRegex || emojiRegex() // only init when needed
let emojiFreeAccountName = removeEmoji(accountName.replace(theEmojiRegex, ''), emojis).trim()
if (emojiFreeAccountName) {
return emojiFreeAccountName // only remove emoji if the resulting username is non-empty
let emojiFreeDisplayName = removeEmoji(accountName, emojis)
if (emojiFreeDisplayName) {
return emojiFreeDisplayName
}
}

View file

@ -5,7 +5,7 @@
<ExternalLink href={account.url}
showIcon="true"
normalIconColor="true"
ariaLabel="{account.display_name || account.acct} (opens in new window)"
ariaLabel="{accessibleName} (opens in new window)"
>
<AccountDisplayName {account} />
</ExternalLink>
@ -81,8 +81,21 @@
import Avatar from '../Avatar.html'
import ExternalLink from '../ExternalLink.html'
import AccountDisplayName from '../profile/AccountDisplayName.html'
import { removeEmoji } from '../../_utils/removeEmoji'
import { store } from '../../_store/store'
export default {
store: () => store,
computed: {
emojis: ({ account }) => (account.emojis || []),
displayName: ({ account }) => account.display_name || account.username,
accessibleName: ({ displayName, emojis, $omitEmojiInDisplayNames }) => {
if ($omitEmojiInDisplayNames) {
return removeEmoji(displayName, emojis) || displayName
}
return displayName
}
},
components: {
Avatar,
ExternalLink,

View file

@ -110,6 +110,7 @@
import { classname } from '../../_utils/classname'
import { checkDomAncestors } from '../../_utils/checkDomAncestors'
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
import { removeEmoji } from '../../_utils/removeEmoji'
const INPUT_TAGS = new Set(['a', 'button', 'input', 'textarea'])
const isUserInputElement = node => INPUT_TAGS.has(node.localName)
@ -208,9 +209,17 @@
originalStatus.media_attachments &&
originalStatus.media_attachments.length
),
ariaLabel: ({ originalAccount, originalStatus, visibility }) => (
originalAccountEmojis: ({ originalAccount }) => (originalAccount.emojis || []),
originalAccountDisplayName: ({ originalAccount }) => (originalAccount.display_name || originalAccount.username),
originalAccountAccessibleName: ({ originalAccountDisplayName, originalAccountEmojis, $omitEmojiInDisplayNames }) => {
if ($omitEmojiInDisplayNames) {
return removeEmoji(originalAccountDisplayName, originalAccountEmojis) || originalAccountDisplayName
}
return originalAccountDisplayName
},
ariaLabel: ({ originalAccountAccessibleName, originalStatus, visibility }) => (
(visibility === 'direct' ? 'Direct message' : 'Status') +
` by ${originalAccount.display_name || originalAccount.username}`
` by ${originalAccountAccessibleName}`
),
showHeader: ({ notification, status, timelineType }) => (
(notification && (notification.type === 'reblog' || notification.type === 'favourite')) ||

View file

@ -15,13 +15,3 @@ export function emojifyText (text, emojis, autoplayGifs) {
}
return text
}
export function removeEmoji (text, emojis) {
if (emojis) {
for (let emoji of emojis) {
let shortcodeWithColons = `:${emoji.shortcode}:`
text = replaceAll(text, shortcodeWithColons, '')
}
}
return text
}

View file

@ -0,0 +1,17 @@
import { replaceAll } from './strings'
import emojiRegex from 'emoji-regex'
let theEmojiRegex
export function removeEmoji (text, emojis) {
// remove custom emoji
if (emojis) {
for (let emoji of emojis) {
let shortcodeWithColons = `:${emoji.shortcode}:`
text = replaceAll(text, shortcodeWithColons, '')
}
}
// remove regular emoji
theEmojiRegex = theEmojiRegex || emojiRegex() // only init when needed, then cache
return text.replace(theEmojiRegex, '').trim()
}

View file

@ -1,6 +1,7 @@
import { loginAsFoobar } from '../roles'
import {
displayNameInComposeBox, generalSettingsButton, getNthStatusSelector, getUrl, homeNavButton,
avatarInComposeBox,
displayNameInComposeBox, generalSettingsButton, getNthStatus, getNthStatusSelector, getUrl, homeNavButton,
removeEmojiFromDisplayNamesInput,
settingsNavButton,
sleep
@ -38,6 +39,7 @@ test('Can remove emoji from user display names', async t => {
await t
.expect(displayNameInComposeBox.innerText).eql('🌈 foo 🌈')
.expect($('.compose-box-display-name img').exists).ok()
.expect(avatarInComposeBox.getAttribute('aria-label')).eql('Profile for 🌈 foo :blobpats: 🌈')
.click(settingsNavButton)
.click(generalSettingsButton)
.click(removeEmojiFromDisplayNamesInput)
@ -45,6 +47,7 @@ test('Can remove emoji from user display names', async t => {
.click(homeNavButton)
.expect(displayNameInComposeBox.innerText).eql('foo')
.expect($('.compose-box-display-name img').exists).notOk()
.expect(avatarInComposeBox.getAttribute('aria-label')).eql('Profile for foo')
.click(settingsNavButton)
.click(generalSettingsButton)
.click(removeEmojiFromDisplayNamesInput)
@ -52,6 +55,7 @@ test('Can remove emoji from user display names', async t => {
.click(homeNavButton)
.expect(displayNameInComposeBox.innerText).eql('🌈 foo 🌈')
.expect($('.compose-box-display-name img').exists).ok()
.expect(avatarInComposeBox.getAttribute('aria-label')).eql('Profile for 🌈 foo :blobpats: 🌈')
})
test('Cannot remove emoji from user display names if result would be empty', async t => {
@ -61,6 +65,7 @@ test('Cannot remove emoji from user display names if result would be empty', asy
await t
.expect(displayNameInComposeBox.innerText).eql('🌈 🌈')
.expect($('.compose-box-display-name img').exists).ok()
.expect(avatarInComposeBox.getAttribute('aria-label')).eql('Profile for 🌈 :blobpats: 🌈')
.click(settingsNavButton)
.click(generalSettingsButton)
.click(removeEmojiFromDisplayNamesInput)
@ -68,6 +73,7 @@ test('Cannot remove emoji from user display names if result would be empty', asy
.click(homeNavButton)
.expect(displayNameInComposeBox.innerText).eql('🌈 🌈')
.expect($('.compose-box-display-name img').exists).ok()
.expect(avatarInComposeBox.getAttribute('aria-label')).eql('Profile for 🌈 :blobpats: 🌈')
.click(settingsNavButton)
.click(generalSettingsButton)
.click(removeEmojiFromDisplayNamesInput)
@ -75,4 +81,28 @@ test('Cannot remove emoji from user display names if result would be empty', asy
.click(homeNavButton)
.expect(displayNameInComposeBox.innerText).eql('🌈 🌈')
.expect($('.compose-box-display-name img').exists).ok()
.expect(avatarInComposeBox.getAttribute('aria-label')).eql('Profile for 🌈 :blobpats: 🌈')
})
test('Check status aria labels for de-emojified text', async t => {
await updateUserDisplayNameAs('foobar', '🌈 foo :blobpats: 🌈')
await sleep(1000)
await loginAsFoobar(t)
await t
.click(displayNameInComposeBox)
.expect(getNthStatus(0).getAttribute('aria-label')).eql('Status by 🌈 foo :blobpats: 🌈')
.click(settingsNavButton)
.click(generalSettingsButton)
.click(removeEmojiFromDisplayNamesInput)
.expect(removeEmojiFromDisplayNamesInput.checked).ok()
.click(homeNavButton)
.click(displayNameInComposeBox)
.expect(getNthStatus(0).getAttribute('aria-label')).eql('Status by foo')
.click(settingsNavButton)
.click(generalSettingsButton)
.click(removeEmojiFromDisplayNamesInput)
.expect(removeEmojiFromDisplayNamesInput.checked).notOk()
.click(homeNavButton)
.click(displayNameInComposeBox)
.expect(getNthStatus(0).getAttribute('aria-label')).eql('Status by 🌈 foo :blobpats: 🌈')
})