Skip to content

Commit 2c2bbe9

Browse files
committed
♿️(frontend) make sub-doc interlinks keyboard accessible
A native anchor breaks Tiptap drag and drop, so we use role=link.
1 parent 5172794 commit 2c2bbe9

2 files changed

Lines changed: 34 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ and this project adheres to
66

77
## [Unreleased]
88

9+
### Changed
10+
11+
- ♿️(frontend) use anchor links for interlinking sub-documents #2391
12+
913
## [v5.5.0] - 2026-08-24
1014

1115
### Added

src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-inline-content/Interlinking/LinkSelected.tsx

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { useRouter } from 'next/router';
22
import { useEffect } from 'react';
33
import { css } from 'styled-components';
44

5-
import { Box, BoxButton, Text } from '@/components';
5+
import { Box, Text } from '@/components';
66
import SelectedPageIcon from '@/docs/doc-editor/assets/doc-selected.svg';
7-
import { getEmojiAndTitle, useDoc } from '@/docs/doc-management/';
7+
import { getEmojiAndTitle, useDoc, useDocStore } from '@/docs/doc-management/';
88

99
interface LinkSelectedProps {
1010
docId: string;
@@ -38,12 +38,18 @@ export const LinkSelected = ({
3838
}, [doc?.title, docId, isEditable]);
3939

4040
const { emoji, titleWithoutEmoji } = getEmojiAndTitle(title);
41+
const { currentDoc } = useDocStore();
42+
const isDeletedDoc = !!currentDoc?.deleted_at;
4143
const router = useRouter();
4244
const href = `/docs/${docId}/`;
4345

44-
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
46+
const handleClick = (e: React.MouseEvent<HTMLSpanElement>) => {
4547
e.preventDefault();
4648

49+
if (isDeletedDoc) {
50+
return;
51+
}
52+
4753
// If ctrl or command is pressed, it opens a new tab. If shift is pressed, it opens a new window
4854
if (e.metaKey || e.ctrlKey || e.shiftKey) {
4955
window.open(href, '_blank');
@@ -53,28 +59,45 @@ export const LinkSelected = ({
5359
};
5460

5561
// This triggers on middle-mouse click
56-
const handleAuxClick = (e: React.MouseEvent<HTMLButtonElement>) => {
57-
if (e.button !== 1) {
62+
const handleAuxClick = (e: React.MouseEvent<HTMLSpanElement>) => {
63+
if (e.button !== 1 || isDeletedDoc) {
5864
return;
5965
}
6066
e.preventDefault();
6167
e.stopPropagation();
6268
window.open(href, '_blank');
6369
};
6470

71+
/**
72+
* A link is activated with Enter only, Space is a button behaviour and stays
73+
* available to the editor.
74+
*/
75+
const handleKeyDown = (e: React.KeyboardEvent<HTMLSpanElement>) => {
76+
if (e.key !== 'Enter' || isDeletedDoc) {
77+
return;
78+
}
79+
e.preventDefault();
80+
void router.push(href);
81+
};
82+
6583
return (
66-
<BoxButton
84+
<Box
6785
as="span"
86+
role="link"
87+
tabIndex={isDeletedDoc ? -1 : 0}
88+
aria-disabled={isDeletedDoc || undefined}
6889
className="--docs--interlinking-link-inline-content"
6990
data-href={href}
7091
onClick={handleClick}
7192
onAuxClick={handleAuxClick}
93+
onKeyDown={handleKeyDown}
7294
draggable="false"
7395
$height="28px"
7496
$css={css`
7597
display: inline;
7698
padding: 0.1rem 0.4rem;
7799
border-radius: 4px;
100+
cursor: pointer;
78101
& svg {
79102
position: relative;
80103
top: 2px;
@@ -129,6 +152,6 @@ export const LinkSelected = ({
129152
{titleWithoutEmoji}
130153
</Box>
131154
</Text>
132-
</BoxButton>
155+
</Box>
133156
);
134157
};

0 commit comments

Comments
 (0)