fix(react): address Doctor findings (#1143)

Accessibility/semantics pass aligned with React Doctor: role-based spans/divs become real buttons, role=status becomes <output>, modals use native <dialog>, author/peer flags render as <img>, and embed/challenge iframes are sandboxed. Includes review follow-ups: correct button chrome/centering/themed dialog colors and focus rings, the missing aria-label i18n keys (all languages), the Yandex image-search URL, clearer .bso setup steps, a keyboard-accessible image-search dropdown, and removal of the post-edit ('comment edited') display (mod edits unchanged).
This commit is contained in:
Tommaso Casaburi
2026-05-29 17:09:07 +07:00
committed by GitHub
parent ac95e58433
commit e082c7b428
132 changed files with 1753 additions and 1224 deletions
@@ -70,8 +70,8 @@ const CopyLinkButton = ({ cid, communityAddress, linkType, onClose }: CopyLinkBu
onClose();
};
return (
<div
role='button'
<button
type='button'
tabIndex={0}
onClick={copyDirectLink}
onKeyDown={(e) => {
@@ -82,7 +82,7 @@ const CopyLinkButton = ({ cid, communityAddress, linkType, onClose }: CopyLinkBu
}}
>
<div className={styles.postMenuItem}>{t('copy_direct_link')}</div>
</div>
</button>
);
};
@@ -93,8 +93,8 @@ const CopyContentIdButton = ({ cid, onClose }: { cid: string; onClose: () => voi
onClose();
};
return (
<div
role='button'
<button
type='button'
tabIndex={0}
onClick={copyContentId}
onKeyDown={(e) => {
@@ -105,7 +105,7 @@ const CopyContentIdButton = ({ cid, onClose }: { cid: string; onClose: () => voi
}}
>
<div className={styles.postMenuItem}>{t('copy_content_id')}</div>
</div>
</button>
);
};
@@ -116,8 +116,8 @@ const CopyUserIdButton = ({ address, onClose }: { address: string; onClose: () =
onClose();
};
return (
<div
role='button'
<button
type='button'
tabIndex={0}
onClick={copyUserId}
onKeyDown={(e) => {
@@ -128,7 +128,7 @@ const CopyUserIdButton = ({ address, onClose }: { address: string; onClose: () =
}}
>
<div className={styles.postMenuItem}>{t('copy_user_id')}</div>
</div>
</button>
);
};
@@ -136,32 +136,20 @@ const ImageSearchButtons = ({ url, onClose }: { url: string; onClose: () => void
const { t } = useTranslation();
const encodedUrl = encodeURIComponent(url);
return (
<div
role='button'
tabIndex={0}
onClick={onClose}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
onClose();
}
}}
>
<a href={`https://lens.google.com/uploadbyurl?url=${encodedUrl}`} target='_blank' rel='noopener noreferrer'>
<div>
<a href={`https://lens.google.com/uploadbyurl?url=${encodedUrl}`} target='_blank' rel='noopener noreferrer' onClick={onClose}>
<div className={styles.postMenuItem}>{t('search_image_on_google')}</div>
</a>
<a href={`https://www.yandex.com/images/search?img_url=${encodedUrl}&rpt=imageview`} target='_blank' rel='noopener noreferrer'>
<a href={`https://www.yandex.com/images/search?url=${encodedUrl}&rpt=imageview`} target='_blank' rel='noopener noreferrer' onClick={onClose}>
<div className={styles.postMenuItem}>{t('search_image_on_yandex')}</div>
</a>
<a href={`https://saucenao.com/search.php?url=${encodedUrl}`} target='_blank' rel='noopener noreferrer'>
<a href={`https://saucenao.com/search.php?url=${encodedUrl}`} target='_blank' rel='noopener noreferrer' onClick={onClose}>
<div className={styles.postMenuItem}>{t('search_image_on_saucenao')}</div>
</a>
</div>
);
};
const { addChallenge } = useChallengesStore.getState();
const ReportPostButton = ({ onClose }: { onClose: () => void }) => {
const { t } = useTranslation();
const reportPost = () => {
@@ -169,8 +157,8 @@ const ReportPostButton = ({ onClose }: { onClose: () => void }) => {
onClose();
};
return (
<div
role='button'
<button
type='button'
tabIndex={0}
onClick={reportPost}
onKeyDown={(e) => {
@@ -181,7 +169,7 @@ const ReportPostButton = ({ onClose }: { onClose: () => void }) => {
}}
>
<div className={styles.postMenuItem}>{t('report_post')}</div>
</div>
</button>
);
};
@@ -208,7 +196,7 @@ const DeletePostButton = ({ post, onClose }: DeletePostButtonProps) => {
latestPostRef.current = resolvedPost;
}, [resolvedPost]);
const onChallenge = useCallback(async (...args: unknown[]) => {
addChallenge([...args, latestPostRef.current]);
useChallengesStore.getState().addChallenge([...args, latestPostRef.current]);
}, []);
const deleteOptions = useMemo(
@@ -252,8 +240,8 @@ const DeletePostButton = ({ post, onClose }: DeletePostButtonProps) => {
};
return (
<div
role='button'
<button
type='button'
tabIndex={0}
onClick={deletePost}
onKeyDown={(e) => {
@@ -264,7 +252,7 @@ const DeletePostButton = ({ post, onClose }: DeletePostButtonProps) => {
}}
>
<div className={styles.postMenuItem}>{t('delete_post')}</div>
</div>
</button>
);
};
@@ -283,8 +271,8 @@ const HidePostButton = ({ cid, comment, isReply, onClose, postCid }: HideButtonP
};
return (
(!isInPostView || isReply) && (
<div
role='button'
<button
type='button'
tabIndex={0}
onClick={togglePostHidden}
onKeyDown={(e) => {
@@ -297,7 +285,7 @@ const HidePostButton = ({ cid, comment, isReply, onClose, postCid }: HideButtonP
<div className={styles.postMenuItem}>
{hidden ? (postCid === cid ? t('unhide_thread') : t('unhide_post')) : postCid === cid ? t('hide_thread') : t('hide_post')}
</div>
</div>
</button>
)
);
};
@@ -343,10 +331,10 @@ const PostMenuMobile = ({ postMenu, editMenuPost }: PostMenuMobileProps) => {
<>
{!(deleted || removed) && (
<>
<span
<button
type='button'
className={styles.postMenuBtn}
title='Post menu'
role='button'
tabIndex={0}
onClick={handleMenuClick}
onKeyDown={(e) => {
@@ -359,7 +347,7 @@ const PostMenuMobile = ({ postMenu, editMenuPost }: PostMenuMobileProps) => {
{...getReferenceProps()}
>
...
</span>
</button>
{isMenuOpen &&
cid &&
createPortal(