Skip to content

Commit c8619b7

Browse files
committed
✨(frontend) preserve @ character when esc is pressed after typing it
improves user experience by keeping @ symbol after cancelling mention trigger Signed-off-by: Cyril <c.gromoff@gmail.com>
1 parent 145c688 commit c8619b7

File tree

3 files changed

+82
-44
lines changed

3 files changed

+82
-44
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to
1515

1616
- 🐛(frontend) fix duplicate document entries in grid #1479
1717
- 🐛(frontend) show full nested doc names with ajustable bar #1456
18+
- 🐛(frontend) preserve @ character when esc is pressed after typing it #1512
1819

1920
## [3.8.2] - 2025-10-17
2021

src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,29 @@ test.describe('Doc Editor', () => {
784784
).toBeVisible();
785785
});
786786

787+
test('it keeps @ when pressing Escape', async ({ page, browserName }) => {
788+
const [randomDoc] = await createDoc(
789+
page,
790+
'doc-interlink-esc',
791+
browserName,
792+
1,
793+
);
794+
795+
await verifyDocName(page, randomDoc);
796+
797+
const editor = await getEditor({ page });
798+
await page.keyboard.press('@');
799+
800+
const searchInput = page.locator(
801+
"span[data-inline-content-type='interlinkingSearchInline'] input",
802+
);
803+
await expect(searchInput).toBeVisible();
804+
805+
await page.keyboard.press('Escape');
806+
807+
await expect(editor.getByText('@')).toBeVisible();
808+
});
809+
787810
test('it checks multiple big doc scroll to the top', async ({
788811
page,
789812
browserName,

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

Lines changed: 58 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,63 @@ export const SearchPage = ({
9898
}, 100);
9999
}, [inputRef]);
100100

101+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
102+
if (e.key === 'Escape') {
103+
e.preventDefault();
104+
105+
updateInlineContent({
106+
type: 'interlinkingSearchInline',
107+
props: {
108+
disabled: true,
109+
trigger,
110+
},
111+
});
112+
113+
contentRef(null);
114+
editor.focus();
115+
// Keep the trigger character ('@' or '/') in the editor when closing with Escape
116+
editor.insertInlineContent([trigger]);
117+
} else if (e.key === 'Backspace' && search.length === 0) {
118+
e.preventDefault();
119+
120+
updateInlineContent({
121+
type: 'interlinkingSearchInline',
122+
props: {
123+
disabled: true,
124+
trigger,
125+
},
126+
});
127+
128+
contentRef(null);
129+
editor.focus();
130+
editor.insertInlineContent(['']);
131+
} else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
132+
// Allow arrow keys to be handled by the command menu for navigation
133+
const commandList = e.currentTarget
134+
.closest('.inline-content')
135+
?.nextElementSibling?.querySelector('[cmdk-list]');
136+
137+
// Create a synthetic keyboard event for the command menu
138+
const syntheticEvent = new KeyboardEvent('keydown', {
139+
key: e.key,
140+
bubbles: true,
141+
cancelable: true,
142+
});
143+
commandList?.dispatchEvent(syntheticEvent);
144+
e.preventDefault();
145+
} else if (e.key === 'Enter') {
146+
// Handle Enter key to select the currently highlighted item
147+
const selectedItem = e.currentTarget
148+
.closest('.inline-content')
149+
?.nextElementSibling?.querySelector(
150+
'[cmdk-item][data-selected="true"]',
151+
) as HTMLElement;
152+
153+
selectedItem?.click();
154+
e.preventDefault();
155+
}
156+
};
157+
101158
return (
102159
<Box as="span" $position="relative">
103160
<Box
@@ -123,50 +180,7 @@ export const SearchPage = ({
123180
const value = (e.target as HTMLInputElement).value;
124181
setSearch(value);
125182
}}
126-
onKeyDown={(e) => {
127-
if (
128-
(e.key === 'Backspace' && search.length === 0) ||
129-
e.key === 'Escape'
130-
) {
131-
e.preventDefault();
132-
133-
updateInlineContent({
134-
type: 'interlinkingSearchInline',
135-
props: {
136-
disabled: true,
137-
trigger,
138-
},
139-
});
140-
141-
contentRef(null);
142-
editor.focus();
143-
editor.insertInlineContent(['']);
144-
} else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
145-
// Allow arrow keys to be handled by the command menu for navigation
146-
const commandList = e.currentTarget
147-
.closest('.inline-content')
148-
?.nextElementSibling?.querySelector('[cmdk-list]');
149-
150-
// Create a synthetic keyboard event for the command menu
151-
const syntheticEvent = new KeyboardEvent('keydown', {
152-
key: e.key,
153-
bubbles: true,
154-
cancelable: true,
155-
});
156-
commandList?.dispatchEvent(syntheticEvent);
157-
e.preventDefault();
158-
} else if (e.key === 'Enter') {
159-
// Handle Enter key to select the currently highlighted item
160-
const selectedItem = e.currentTarget
161-
.closest('.inline-content')
162-
?.nextElementSibling?.querySelector(
163-
'[cmdk-item][data-selected="true"]',
164-
) as HTMLElement;
165-
166-
selectedItem?.click();
167-
e.preventDefault();
168-
}
169-
}}
183+
onKeyDown={handleKeyDown}
170184
/>
171185
</Box>
172186
<Box

0 commit comments

Comments
 (0)