diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 3bfe7c9f..5e2e5135 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -880,5 +880,24 @@ "Try a different search term.": "Try a different search term.", "Try again": "Try again", "Untitled chat": "Untitled chat", - "What can I help you with?": "What can I help you with?" + "What can I help you with?": "What can I help you with?", + "Page menu": "Page menu", + "Expand": "Expand", + "Collapse": "Collapse", + "Comment menu": "Comment menu", + "Group menu": "Group menu", + "Show hidden breadcrumbs": "Show hidden breadcrumbs", + "Breadcrumbs": "Breadcrumbs", + "Page actions": "Page actions", + "Pick emoji": "Pick emoji", + "Template menu": "Template menu", + "Chat menu": "Chat menu", + "API key menu": "API key menu", + "Jump to comment selection": "Jump to comment selection", + "Slash commands": "Slash commands", + "Mention suggestions": "Mention suggestions", + "Link suggestions": "Link suggestions", + "Diagram editor": "Diagram editor", + "Add comment": "Add comment", + "Find and replace": "Find and replace" } diff --git a/apps/client/src/components/common/copy.tsx b/apps/client/src/components/common/copy.tsx index 745fc4ba..2144417b 100644 --- a/apps/client/src/components/common/copy.tsx +++ b/apps/client/src/components/common/copy.tsx @@ -25,6 +25,7 @@ export default function CopyTextButton({ text, size }: CopyProps) { variant="subtle" onClick={copy} size={size} + aria-label={copied ? t("Copied") : t("Copy")} > {copied ? : } diff --git a/apps/client/src/components/ui/destination-picker/page-children.tsx b/apps/client/src/components/ui/destination-picker/page-children.tsx index 9db5fd71..c3e84718 100644 --- a/apps/client/src/components/ui/destination-picker/page-children.tsx +++ b/apps/client/src/components/ui/destination-picker/page-children.tsx @@ -74,7 +74,18 @@ export function PageChildren({ /> ))} {hasNextPage && ( -
fetchNextPage()}> +
fetchNextPage()} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + fetchNextPage(); + } + }} + role="button" + tabIndex={0} + > {t("Load more")}
)} diff --git a/apps/client/src/components/ui/emoji-picker.tsx b/apps/client/src/components/ui/emoji-picker.tsx index 112c2d9c..804d1b0f 100644 --- a/apps/client/src/components/ui/emoji-picker.tsx +++ b/apps/client/src/components/ui/emoji-picker.tsx @@ -70,11 +70,14 @@ function EmojiPicker({ closeOnEscape={true} > - {icon} diff --git a/apps/client/src/ee/ai-chat/components/ai-chat-sidebar-item.tsx b/apps/client/src/ee/ai-chat/components/ai-chat-sidebar-item.tsx index abaf4277..e2bd553c 100644 --- a/apps/client/src/ee/ai-chat/components/ai-chat-sidebar-item.tsx +++ b/apps/client/src/ee/ai-chat/components/ai-chat-sidebar-item.tsx @@ -132,6 +132,7 @@ export default function AiChatSidebarItem({ size="xs" color="gray" onClick={(e) => e.preventDefault()} + aria-label={t("Chat menu")} > diff --git a/apps/client/src/ee/api-key/components/api-key-table.tsx b/apps/client/src/ee/api-key/components/api-key-table.tsx index 48757acc..2c55b36f 100644 --- a/apps/client/src/ee/api-key/components/api-key-table.tsx +++ b/apps/client/src/ee/api-key/components/api-key-table.tsx @@ -106,7 +106,11 @@ export function ApiKeyTable({ - + diff --git a/apps/client/src/ee/page-verification/components/page-verification-modal.tsx b/apps/client/src/ee/page-verification/components/page-verification-modal.tsx index df3f75ee..2a6253fa 100644 --- a/apps/client/src/ee/page-verification/components/page-verification-modal.tsx +++ b/apps/client/src/ee/page-verification/components/page-verification-modal.tsx @@ -38,6 +38,7 @@ export function PageVerificationModal({ e.stopPropagation()} + aria-label={t("Template menu")} > diff --git a/apps/client/src/ee/template/components/template-preview-modal.tsx b/apps/client/src/ee/template/components/template-preview-modal.tsx index d51b95ca..f9eed6a9 100644 --- a/apps/client/src/ee/template/components/template-preview-modal.tsx +++ b/apps/client/src/ee/template/components/template-preview-modal.tsx @@ -24,7 +24,7 @@ export default function TemplatePreviewModal({ const title = template?.title || t("Untitled"); return ( - + diff --git a/apps/client/src/features/comment/components/comment-dialog.tsx b/apps/client/src/features/comment/components/comment-dialog.tsx index 24781a94..ac7107f9 100644 --- a/apps/client/src/features/comment/components/comment-dialog.tsx +++ b/apps/client/src/features/comment/components/comment-dialog.tsx @@ -144,6 +144,7 @@ function CommentDialog({ editor, pageId, readOnly }: CommentDialogProps) { withCloseButton withBorder data-comment-dialog + aria-label={t("Add comment")} > diff --git a/apps/client/src/features/comment/components/comment-list-item.tsx b/apps/client/src/features/comment/components/comment-list-item.tsx index cfb69a0e..8af21d28 100644 --- a/apps/client/src/features/comment/components/comment-list-item.tsx +++ b/apps/client/src/features/comment/components/comment-list-item.tsx @@ -173,6 +173,15 @@ function CommentListItem({ handleCommentClick(comment)} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + handleCommentClick(comment); + } + }} + role="button" + tabIndex={0} + aria-label={t("Jump to comment selection")} > {comment?.selection} diff --git a/apps/client/src/features/comment/components/comment-menu.tsx b/apps/client/src/features/comment/components/comment-menu.tsx index fe047232..690815e6 100644 --- a/apps/client/src/features/comment/components/comment-menu.tsx +++ b/apps/client/src/features/comment/components/comment-menu.tsx @@ -46,7 +46,11 @@ function CommentMenu({ return ( - + diff --git a/apps/client/src/features/editor/components/bubble-menu/color-selector.tsx b/apps/client/src/features/editor/components/bubble-menu/color-selector.tsx index 0d4d80c0..1363858a 100644 --- a/apps/client/src/features/editor/components/bubble-menu/color-selector.tsx +++ b/apps/client/src/features/editor/components/bubble-menu/color-selector.tsx @@ -172,6 +172,9 @@ export const ColorSelector: FC = ({ fontWeight: 500, fontSize: rem(16), }} + aria-label={t("Text color")} + aria-haspopup="dialog" + aria-expanded={isOpen} > A @@ -186,20 +189,32 @@ export const ColorSelector: FC = ({ {t("Text color")} - {TEXT_COLORS.map(({ name, color }, index) => ( + {TEXT_COLORS.map(({ name, color }, index) => { + const applyTextColor = () => { + if (name === "Default") { + editor.commands.unsetColor(); + } else { + editor + .chain() + .focus() + .setColor(color || "") + .run(); + } + setIsOpen(false); + }; + return ( { - if (name === "Default") { - editor.commands.unsetColor(); - } else { - editor - .chain() - .focus() - .setColor(color || "") - .run(); + role="button" + tabIndex={0} + aria-label={t(name)} + aria-pressed={!!editorState[`text_${color}`]} + onClick={applyTextColor} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + applyTextColor(); } - setIsOpen(false); }} style={{ width: rem(28), @@ -221,7 +236,8 @@ export const ColorSelector: FC = ({ A - ))} + ); + })} @@ -230,23 +246,35 @@ export const ColorSelector: FC = ({ {t("Highlight color")} - {HIGHLIGHT_COLORS.map(({ name, color }, index) => ( + {HIGHLIGHT_COLORS.map(({ name, color }, index) => { + const applyHighlight = () => { + if (name === "Default") { + editor.commands.unsetHighlight(); + } else { + editor + .chain() + .focus() + .toggleMark("highlight", { + color: color || "", + colorName: name.toLowerCase() || "", + }) + .run(); + } + setIsOpen(false); + }; + return ( { - if (name === "Default") { - editor.commands.unsetHighlight(); - } else { - editor - .chain() - .focus() - .toggleMark("highlight", { - color: color || "", - colorName: name.toLowerCase() || "", - }) - .run(); + role="button" + tabIndex={0} + aria-label={t(name)} + aria-pressed={!!editorState[`highlight_${color}`]} + onClick={applyHighlight} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + applyHighlight(); } - setIsOpen(false); }} style={{ width: rem(28), @@ -274,7 +302,8 @@ export const ColorSelector: FC = ({ )} - ))} + ); + })} diff --git a/apps/client/src/features/editor/components/bubble-menu/node-selector.tsx b/apps/client/src/features/editor/components/bubble-menu/node-selector.tsx index 7fecff9e..55fe427e 100644 --- a/apps/client/src/features/editor/components/bubble-menu/node-selector.tsx +++ b/apps/client/src/features/editor/components/bubble-menu/node-selector.tsx @@ -157,6 +157,9 @@ export const NodeSelector: FC = ({ radius="0" rightSection={} onClick={() => setIsOpen(!isOpen)} + aria-label={t("Turn into")} + aria-haspopup="menu" + aria-expanded={isOpen} > {t(activeItem?.name)} diff --git a/apps/client/src/features/editor/components/bubble-menu/text-alignment-selector.tsx b/apps/client/src/features/editor/components/bubble-menu/text-alignment-selector.tsx index f8dba1c9..d4d9876e 100644 --- a/apps/client/src/features/editor/components/bubble-menu/text-alignment-selector.tsx +++ b/apps/client/src/features/editor/components/bubble-menu/text-alignment-selector.tsx @@ -92,6 +92,9 @@ export const TextAlignmentSelector: FC = ({ radius="0" rightSection={} onClick={() => setIsOpen(!isOpen)} + aria-label={t("Text align")} + aria-haspopup="menu" + aria-expanded={isOpen} > diff --git a/apps/client/src/features/editor/components/drawio/drawio-view.tsx b/apps/client/src/features/editor/components/drawio/drawio-view.tsx index 357057bf..fe10bf05 100644 --- a/apps/client/src/features/editor/components/drawio/drawio-view.tsx +++ b/apps/client/src/features/editor/components/drawio/drawio-view.tsx @@ -137,7 +137,13 @@ export default function DrawioView(props: NodeViewProps) { return ( - + diff --git a/apps/client/src/features/editor/components/emoji-menu/emoji-list.tsx b/apps/client/src/features/editor/components/emoji-menu/emoji-list.tsx index 15428bbe..cbe63479 100644 --- a/apps/client/src/features/editor/components/emoji-menu/emoji-list.tsx +++ b/apps/client/src/features/editor/components/emoji-menu/emoji-list.tsx @@ -107,7 +107,17 @@ const EmojiList = ({ }, [selectedIndex]); return items.length > 0 || isLoading ? ( - + 0 ? `emoji-command-option-${selectedIndex}` : undefined + } + > {isLoading && } {items.length > 0 && ( ( } classNames={{ input: classes.linkInput }} placeholder={t("Paste link or search pages")} + aria-label={t("Paste link or search pages")} + role="combobox" + aria-expanded={showDropdown} + aria-controls="link-editor-results" + aria-autocomplete="list" + aria-activedescendant={ + showDropdown ? `link-editor-option-${selectedIndex}` : undefined + } value={state.url} onChange={state.onChange} onKeyDown={handleKeyDown} @@ -125,10 +133,16 @@ export const LinkEditorPanel = ({ scrollbarSize={6} mt={state.url.length > 0 ? 8 : 0} styles={{ content: { minWidth: 0 } }} + id="link-editor-results" + role="listbox" + aria-label={t("Link suggestions")} > {showUrlItem && ( onSetLink(state.url, false)} className={clsx(classes.searchItem, { [classes.selectedSearchItem]: selectedIndex === 0, @@ -156,6 +170,9 @@ export const LinkEditorPanel = ({ return ( selectPage(page)} className={clsx(classes.searchItem, { diff --git a/apps/client/src/features/editor/components/mention/mention-list.tsx b/apps/client/src/features/editor/components/mention/mention-list.tsx index 3ddf3976..330bded9 100644 --- a/apps/client/src/features/editor/components/mention/mention-list.tsx +++ b/apps/client/src/features/editor/components/mention/mention-list.tsx @@ -287,7 +287,16 @@ const MentionList = forwardRef((props, ref) => { ); return ( - + ((props, ref) => { if (item.entityType === "header") { const isFirst = index === 0; return ( -
+
{!isFirst && } ((props, ref) => { selectItem(index)} className={clsx(classes.menuBtn, { [classes.selectedItem]: index === selectedIndex, @@ -348,6 +360,9 @@ const MentionList = forwardRef((props, ref) => { selectItem(index)} className={clsx(classes.menuBtn, { [classes.selectedItem]: index === selectedIndex, @@ -358,7 +373,7 @@ const MentionList = forwardRef((props, ref) => { diff --git a/apps/client/src/features/editor/components/pdf/pdf-view.tsx b/apps/client/src/features/editor/components/pdf/pdf-view.tsx index 4d06402b..693d3755 100644 --- a/apps/client/src/features/editor/components/pdf/pdf-view.tsx +++ b/apps/client/src/features/editor/components/pdf/pdf-view.tsx @@ -92,7 +92,20 @@ export default function PdfView(props: NodeViewProps) { if (hasError) { return ( -
+
{ + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + handleSelect(); + } + }} + role="button" + tabIndex={0} + aria-label={t("Failed to load PDF")} + > {t("Failed to load PDF")} diff --git a/apps/client/src/features/editor/components/search-and-replace/search-and-replace-dialog.tsx b/apps/client/src/features/editor/components/search-and-replace/search-and-replace-dialog.tsx index f5c17661..698666e0 100644 --- a/apps/client/src/features/editor/components/search-and-replace/search-and-replace-dialog.tsx +++ b/apps/client/src/features/editor/components/search-and-replace/search-and-replace-dialog.tsx @@ -187,6 +187,7 @@ function SearchAndReplaceDialog({ editor, editable = true }: PageFindDialogDialo position={{ top: 90, right: 50 }} withBorder transitionProps={{ transition: "slide-down" }} + aria-label={t("Find and replace")} > diff --git a/apps/client/src/features/editor/components/slash-menu/command-list.tsx b/apps/client/src/features/editor/components/slash-menu/command-list.tsx index 54d6cd17..a6132bcb 100644 --- a/apps/client/src/features/editor/components/slash-menu/command-list.tsx +++ b/apps/client/src/features/editor/components/slash-menu/command-list.tsx @@ -86,7 +86,15 @@ const CommandList = ({ }, [selectedIndex]); return flatItems.length > 0 ? ( - + {Object.entries(items).map(([category, categoryItems]) => ( -
+
{category} @@ -103,13 +111,16 @@ const CommandList = ({ selectItem(index)} className={clsx(classes.menuBtn, { [classes.selectedItem]: index === selectedIndex, })} > - + diff --git a/apps/client/src/features/editor/components/status/status-view.tsx b/apps/client/src/features/editor/components/status/status-view.tsx index d8f10d79..3cca4b59 100644 --- a/apps/client/src/features/editor/components/status/status-view.tsx +++ b/apps/client/src/features/editor/components/status/status-view.tsx @@ -92,8 +92,17 @@ export default function StatusView(props: NodeViewProps) { colorClassMap[color], )} onClick={() => isEditable && setOpened(true)} + onKeyDown={(e) => { + if (isEditable && (e.key === "Enter" || e.key === " ")) { + e.preventDefault(); + setOpened(true); + } + }} role="button" tabIndex={0} + aria-label={text || "SET STATUS"} + aria-haspopup="dialog" + aria-expanded={opened} > {text || "SET STATUS"} @@ -127,6 +136,16 @@ export default function StatusView(props: NodeViewProps) { )} style={{ backgroundColor: bg }} onClick={() => handleColorChange(name)} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + handleColorChange(name); + } + }} + role="button" + tabIndex={0} + aria-label={name} + aria-pressed={color === name} > {color === name && } diff --git a/apps/client/src/features/group/components/group-action-menu.tsx b/apps/client/src/features/group/components/group-action-menu.tsx index 331ea3de..8c3dccb0 100644 --- a/apps/client/src/features/group/components/group-action-menu.tsx +++ b/apps/client/src/features/group/components/group-action-menu.tsx @@ -53,7 +53,7 @@ export default function GroupActionMenu() { arrowPosition="center" > - + diff --git a/apps/client/src/features/page-history/components/history-modal.tsx b/apps/client/src/features/page-history/components/history-modal.tsx index dfd43cf1..08f05c9e 100644 --- a/apps/client/src/features/page-history/components/history-modal.tsx +++ b/apps/client/src/features/page-history/components/history-modal.tsx @@ -22,6 +22,7 @@ export default function HistoryModal({ pageId, pageTitle }: Props) { opened={isModalOpen} onClose={() => setModalOpen(false)} fullScreen + aria-label={t("Page history")} > @@ -49,6 +50,7 @@ export default function HistoryModal({ pageId, pageTitle }: Props) { size={1400} opened={isModalOpen} onClose={() => setModalOpen(false)} + aria-label={t("Page history")} > diff --git a/apps/client/src/features/page/components/breadcrumbs/breadcrumb.tsx b/apps/client/src/features/page/components/breadcrumbs/breadcrumb.tsx index 11507e40..ce872c36 100644 --- a/apps/client/src/features/page/components/breadcrumbs/breadcrumb.tsx +++ b/apps/client/src/features/page/components/breadcrumbs/breadcrumb.tsx @@ -19,6 +19,7 @@ import { buildPageUrl } from "@/features/page/page.utils.ts"; import { usePageQuery } from "@/features/page/queries/page-query.ts"; import { extractPageSlugId } from "@/lib"; import { useMediaQuery } from "@mantine/hooks"; +import { useTranslation } from "react-i18next"; function getTitle(name: string, icon: string) { if (icon) { @@ -28,6 +29,7 @@ function getTitle(name: string, icon: string) { } export default function Breadcrumb() { + const { t } = useTranslation(); const treeData = useAtomValue(treeDataAtom); const [breadcrumbNodes, setBreadcrumbNodes] = useState< SpaceTreeNode[] | null @@ -115,7 +117,11 @@ export default function Breadcrumb() { key="hidden-nodes" > - + @@ -144,8 +150,12 @@ export default function Breadcrumb() { key="mobile-hidden-nodes" > - - + + diff --git a/apps/client/src/features/page/components/header/page-header-menu.tsx b/apps/client/src/features/page/components/header/page-header-menu.tsx index 9e4b7209..311ed5c6 100644 --- a/apps/client/src/features/page/components/header/page-header-menu.tsx +++ b/apps/client/src/features/page/components/header/page-header-menu.tsx @@ -205,7 +205,11 @@ function PageActionMenu({ readOnly }: PageActionMenuProps) { arrowPosition="center" > - + diff --git a/apps/client/src/features/page/trash/components/trash-page-content-modal.tsx b/apps/client/src/features/page/trash/components/trash-page-content-modal.tsx index ef35b02d..c9aad622 100644 --- a/apps/client/src/features/page/trash/components/trash-page-content-modal.tsx +++ b/apps/client/src/features/page/trash/components/trash-page-content-modal.tsx @@ -19,7 +19,7 @@ export default function TrashPageContentModal({ const title = pageTitle || t("Untitled"); return ( - + diff --git a/apps/client/src/features/page/tree/components/space-tree.tsx b/apps/client/src/features/page/tree/components/space-tree.tsx index ee4d7745..df5b9a42 100644 --- a/apps/client/src/features/page/tree/components/space-tree.tsx +++ b/apps/client/src/features/page/tree/components/space-tree.tsx @@ -458,6 +458,8 @@ interface CreateNodeProps { } function CreateNode({ node, treeApi, onExpandTree }: CreateNodeProps) { + const { t } = useTranslation(); + function handleCreate() { if (node.data.hasChildren && node.children.length === 0) { node.toggle(); @@ -475,6 +477,7 @@ function CreateNode({ node, treeApi, onExpandTree }: CreateNodeProps) { { e.preventDefault(); e.stopPropagation(); @@ -591,6 +594,7 @@ function NodeMenu({ node, treeApi, spaceId }: NodeMenuProps) { { e.preventDefault(); e.stopPropagation(); @@ -725,6 +729,8 @@ interface PageArrowProps { } function PageArrow({ node, onExpandTree }: PageArrowProps) { + const { t } = useTranslation(); + useEffect(() => { if (node.isOpen) { onExpandTree(); @@ -736,6 +742,8 @@ function PageArrow({ node, onExpandTree }: PageArrowProps) { size={20} variant="subtle" c="gray" + aria-label={node.isOpen ? t("Collapse") : t("Expand")} + aria-expanded={node.isInternal ? node.isOpen : undefined} onClick={(e) => { e.preventDefault(); e.stopPropagation(); diff --git a/apps/client/src/features/share/components/share-action-menu.tsx b/apps/client/src/features/share/components/share-action-menu.tsx index 52dad0da..05995128 100644 --- a/apps/client/src/features/share/components/share-action-menu.tsx +++ b/apps/client/src/features/share/components/share-action-menu.tsx @@ -75,7 +75,7 @@ export default function ShareActionMenu({ share }: Props) { arrowPosition="center" > - + diff --git a/apps/client/src/features/share/components/share-shell.tsx b/apps/client/src/features/share/components/share-shell.tsx index 64881ca2..bba226fe 100644 --- a/apps/client/src/features/share/components/share-shell.tsx +++ b/apps/client/src/features/share/components/share-shell.tsx @@ -148,6 +148,7 @@ export default function ShareShell({ onClick={toggleTocMobile} hiddenFrom="sm" size="sm" + aria-label={t("Table of contents")} > @@ -157,6 +158,7 @@ export default function ShareShell({ - +