Skip to content

Commit c690300

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 2b5a9e1 commit c690300

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
@@ -14,6 +14,7 @@ and this project adheres to
1414

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

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

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
@@ -786,6 +786,29 @@ test.describe('Doc Editor', () => {
786786
).toBeVisible();
787787
});
788788

789+
test('it keeps @ when pressing Escape', async ({ page, browserName }) => {
790+
const [randomDoc] = await createDoc(
791+
page,
792+
'doc-interlink-esc',
793+
browserName,
794+
1,
795+
);
796+
797+
await verifyDocName(page, randomDoc);
798+
799+
const editor = await getEditor({ page });
800+
await page.keyboard.press('@');
801+
802+
const searchInput = page.locator(
803+
"span[data-inline-content-type='interlinkingSearchInline'] input",
804+
);
805+
await expect(searchInput).toBeVisible();
806+
807+
await page.keyboard.press('Escape');
808+
809+
await expect(editor.getByText('@')).toBeVisible();
810+
});
811+
789812
test('it checks multiple big doc scroll to the top', async ({
790813
page,
791814
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
@@ -96,6 +96,63 @@ export const SearchPage = ({
9696
}, 100);
9797
}, [inputRef]);
9898

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

0 commit comments

Comments
 (0)