fix(a11y): stable reaction labels — emoji glyph, custom emoji shortcode (#179)
CI / Secret scan (gitleaks) (push) Successful in 52s
CI / Build & Quality Checks (push) Canceled after 0s
CI / Trigger Desktop Build (push) Canceled after 0s
CI / Docker image build & smoke test (push) Canceled after 0s
CI / Playwright smoke (e2e) (push) Canceled after 0s
CI / Secret scan (gitleaks) (push) Successful in 52s
CI / Build & Quality Checks (push) Canceled after 0s
CI / Trigger Desktop Build (push) Canceled after 0s
CI / Docker image build & smoke test (push) Canceled after 0s
CI / Playwright smoke (e2e) (push) Canceled after 0s
The reaction button's aria-label used getShortcodeFor(), which returns undefined until the lazily loaded emoji data arrives. The same button read "🎉 reaction, 1 person" on first render and "tada reaction, 2 people" after any later re-render. It now always uses the emoji itself (screen readers speak it by its proper name, e.g. "party popper"). Custom (mxc) emoji were labelled just "custom emoji"; they now use the shortcode carried on the reaction event (":lotus_blob: reaction"). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
52d94058cf
commit
1b5e6a37f5
@@ -13,13 +13,21 @@ export const Reaction = as<
|
||||
mx: MatrixClient;
|
||||
count: number;
|
||||
reaction: string;
|
||||
/** Custom-emoji shortcode from the reaction event, used for its label. */
|
||||
shortcode?: string;
|
||||
useAuthentication?: boolean;
|
||||
}
|
||||
>(({ className, mx, count, reaction, useAuthentication, ...props }, ref) => {
|
||||
const shortcode = reaction.startsWith('mxc://')
|
||||
? 'custom emoji'
|
||||
: (getShortcodeFor(getHexcodeForEmoji(reaction)) ?? reaction);
|
||||
const label = `${shortcode} reaction, ${count} ${count === 1 ? 'person' : 'people'}`;
|
||||
>(({ className, mx, count, reaction, shortcode, useAuthentication, ...props }, ref) => {
|
||||
// Name a unicode reaction by the emoji itself: screen readers speak it by its
|
||||
// proper name ("party popper"). Don't use getShortcodeFor here — it returns
|
||||
// undefined until the lazy emoji data loads, so the same button would flip
|
||||
// between "🎉 reaction" and "tada reaction" depending on timing (#179).
|
||||
const name = reaction.startsWith('mxc://')
|
||||
? shortcode
|
||||
? `:${shortcode}:`
|
||||
: 'custom emoji'
|
||||
: reaction;
|
||||
const label = `${name} reaction, ${count} ${count === 1 ? 'person' : 'people'}`;
|
||||
|
||||
return (
|
||||
<Box
|
||||
|
||||
@@ -23,7 +23,7 @@ import { Room } from 'matrix-js-sdk';
|
||||
import { type Relations } from 'matrix-js-sdk/lib/models/relations';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||
import { factoryEventSentBy } from '../../../utils/matrix';
|
||||
import { eventWithShortcode, factoryEventSentBy } from '../../../utils/matrix';
|
||||
import { Reaction, ReactionTooltipMsg } from '../../../components/message';
|
||||
import { useRelations } from '../../../hooks/useRelations';
|
||||
import * as css from './styles.css';
|
||||
@@ -138,6 +138,7 @@ export const Reactions = as<'div', ReactionsProps>(
|
||||
key={key}
|
||||
mx={mx}
|
||||
reaction={key}
|
||||
shortcode={rEvents.find(eventWithShortcode)?.getContent().shortcode}
|
||||
count={events.size}
|
||||
onClick={canSendReaction ? () => onReactionToggle(mEventId, key) : undefined}
|
||||
onContextMenu={handleViewReaction}
|
||||
|
||||
@@ -107,6 +107,7 @@ export const ReactionViewer = as<'div', ReactionViewerProps>(
|
||||
key={key}
|
||||
mx={mx}
|
||||
reaction={key}
|
||||
shortcode={Array.from(evts).find(eventWithShortcode)?.getContent().shortcode}
|
||||
count={evts.size}
|
||||
role="option"
|
||||
aria-selected={key === selectedKey}
|
||||
|
||||
Reference in New Issue
Block a user