️(frontend) make sub-doc interlinks keyboard accessible

A native anchor breaks Tiptap drag and drop, so we use role=link.
This commit is contained in:
Ovgodd
2026-08-25 15:43:14 +02:00
parent 5172794942
commit 2c2bbe91d6
2 changed files with 34 additions and 7 deletions
+4
View File
@@ -6,6 +6,10 @@ and this project adheres to
## [Unreleased]
### Changed
- ♿️(frontend) use anchor links for interlinking sub-documents #2391
## [v5.5.0] - 2026-08-24
### Added
@@ -2,9 +2,9 @@ import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { css } from 'styled-components';
import { Box, BoxButton, Text } from '@/components';
import { Box, Text } from '@/components';
import SelectedPageIcon from '@/docs/doc-editor/assets/doc-selected.svg';
import { getEmojiAndTitle, useDoc } from '@/docs/doc-management/';
import { getEmojiAndTitle, useDoc, useDocStore } from '@/docs/doc-management/';
interface LinkSelectedProps {
docId: string;
@@ -38,12 +38,18 @@ export const LinkSelected = ({
}, [doc?.title, docId, isEditable]);
const { emoji, titleWithoutEmoji } = getEmojiAndTitle(title);
const { currentDoc } = useDocStore();
const isDeletedDoc = !!currentDoc?.deleted_at;
const router = useRouter();
const href = `/docs/${docId}/`;
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
const handleClick = (e: React.MouseEvent<HTMLSpanElement>) => {
e.preventDefault();
if (isDeletedDoc) {
return;
}
// If ctrl or command is pressed, it opens a new tab. If shift is pressed, it opens a new window
if (e.metaKey || e.ctrlKey || e.shiftKey) {
window.open(href, '_blank');
@@ -53,8 +59,8 @@ export const LinkSelected = ({
};
// This triggers on middle-mouse click
const handleAuxClick = (e: React.MouseEvent<HTMLButtonElement>) => {
if (e.button !== 1) {
const handleAuxClick = (e: React.MouseEvent<HTMLSpanElement>) => {
if (e.button !== 1 || isDeletedDoc) {
return;
}
e.preventDefault();
@@ -62,19 +68,36 @@ export const LinkSelected = ({
window.open(href, '_blank');
};
/**
* A link is activated with Enter only, Space is a button behaviour and stays
* available to the editor.
*/
const handleKeyDown = (e: React.KeyboardEvent<HTMLSpanElement>) => {
if (e.key !== 'Enter' || isDeletedDoc) {
return;
}
e.preventDefault();
void router.push(href);
};
return (
<BoxButton
<Box
as="span"
role="link"
tabIndex={isDeletedDoc ? -1 : 0}
aria-disabled={isDeletedDoc || undefined}
className="--docs--interlinking-link-inline-content"
data-href={href}
onClick={handleClick}
onAuxClick={handleAuxClick}
onKeyDown={handleKeyDown}
draggable="false"
$height="28px"
$css={css`
display: inline;
padding: 0.1rem 0.4rem;
border-radius: 4px;
cursor: pointer;
& svg {
position: relative;
top: 2px;
@@ -129,6 +152,6 @@ export const LinkSelected = ({
{titleWithoutEmoji}
</Box>
</Text>
</BoxButton>
</Box>
);
};