Accessibility: Ensure focus order of post elements matches visual reading order (#39169)

This commit is contained in:
diondiondion 2026-05-26 19:42:49 +02:00 committed by GitHub
parent 1962e4743c
commit d20d049226
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 46 additions and 34 deletions

View File

@ -20,7 +20,8 @@ export interface StatusHeaderProps {
status: Status; status: Status;
account?: Account; account?: Account;
avatarSize?: number; avatarSize?: number;
children?: ReactNode; contentBeforeDate?: ReactNode;
contentAfterDate?: ReactNode;
wrapperProps?: HTMLAttributes<HTMLDivElement>; wrapperProps?: HTMLAttributes<HTMLDivElement>;
displayNameProps?: DisplayNameProps; displayNameProps?: DisplayNameProps;
onHeaderClick?: MouseEventHandler<HTMLDivElement>; onHeaderClick?: MouseEventHandler<HTMLDivElement>;
@ -33,10 +34,11 @@ export type StatusHeaderRenderFn = (args: StatusHeaderProps) => ReactNode;
export const StatusHeader: FC<StatusHeaderProps> = ({ export const StatusHeader: FC<StatusHeaderProps> = ({
status, status,
account, account,
children,
className, className,
avatarSize = 48, avatarSize = 48,
wrapperProps, wrapperProps,
contentBeforeDate,
contentAfterDate,
onHeaderClick, onHeaderClick,
}) => { }) => {
const statusAccount = status.get('account') as Account | undefined; const statusAccount = status.get('account') as Account | undefined;
@ -51,6 +53,14 @@ export const StatusHeader: FC<StatusHeaderProps> = ({
className={classNames('status__info', className)} className={classNames('status__info', className)}
/* eslint-enable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ /* eslint-enable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */
> >
<StatusDisplayName
statusAccount={statusAccount}
friendAccount={account}
avatarSize={avatarSize}
/>
{contentBeforeDate}
<Link <Link
to={`/@${statusAccount?.acct}/${status.get('id') as string}`} to={`/@${statusAccount?.acct}/${status.get('id') as string}`}
className='status__relative-time' className='status__relative-time'
@ -60,13 +70,7 @@ export const StatusHeader: FC<StatusHeaderProps> = ({
{editedAt && <StatusEditedAt editedAt={editedAt} />} {editedAt && <StatusEditedAt editedAt={editedAt} />}
</Link> </Link>
<StatusDisplayName {contentAfterDate}
statusAccount={statusAccount}
friendAccount={account}
avatarSize={avatarSize}
/>
{children}
</div> </div>
); );
}; };

View File

@ -225,17 +225,20 @@ export const QuotedStatus: React.FC<QuotedStatusProps> = ({
const intl = useIntl(); const intl = useIntl();
const headerRenderFn: StatusHeaderRenderFn = useCallback( const headerRenderFn: StatusHeaderRenderFn = useCallback(
(props) => ( (props) => (
<StatusHeader {...props}> <StatusHeader
{onQuoteCancel && ( {...props}
<IconButton contentAfterDate={
onClick={onQuoteCancel} onQuoteCancel && (
className='status__quote-cancel' <IconButton
title={intl.formatMessage(quoteCancelMessage)} onClick={onQuoteCancel}
icon='cancel-fill' className='status__quote-cancel'
iconComponent={CancelFillIcon} title={intl.formatMessage(quoteCancelMessage)}
/> icon='cancel-fill'
)} iconComponent={CancelFillIcon}
</StatusHeader> />
)
}
/>
), ),
[intl, onQuoteCancel], [intl, onQuoteCancel],
); );

View File

@ -22,18 +22,22 @@ export const renderPinnedStatusHeader: StatusHeaderRenderFn = ({
return <StatusHeader {...args} />; return <StatusHeader {...args} />;
} }
return ( return (
<StatusHeader {...args} className={classes.pinnedStatusHeader}> <StatusHeader
<Badge {...args}
className={classes.pinnedBadge} className={classes.pinnedStatusHeader}
icon={<Icon id='pinned' icon={IconPinned} />} contentBeforeDate={
label={ <Badge
<FormattedMessage className={classes.pinnedBadge}
id='account.timeline.pinned' icon={<Icon id='pinned' icon={IconPinned} />}
defaultMessage='Pinned' label={
/> <FormattedMessage
} id='account.timeline.pinned'
/> defaultMessage='Pinned'
</StatusHeader> />
}
/>
}
/>
); );
}; };

View File

@ -126,4 +126,7 @@
.pinnedBadge { .pinnedBadge {
justify-self: end; justify-self: end;
// Allow "click to open post" event to pass through
pointer-events: none;
} }

View File

@ -1613,7 +1613,6 @@ body > [data-popper-placement] {
font-size: 15px; font-size: 15px;
line-height: 22px; line-height: 22px;
height: 40px; height: 40px;
order: 2;
flex: 0 0 auto; flex: 0 0 auto;
color: var(--color-text-secondary); color: var(--color-text-secondary);
} }
@ -1666,7 +1665,6 @@ body > [data-popper-placement] {
.status__quote-cancel { .status__quote-cancel {
align-self: self-start; align-self: self-start;
order: 5;
} }
.status__info { .status__info {