Skip to content

Commit 65932c1

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 b9b25eb commit 65932c1

File tree

3 files changed

+77
-44
lines changed

3 files changed

+77
-44
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ and this project adheres to
2727
- 🐛(backend) fix s3 version_id validation
2828
- 🐛(frontend) retry check media status after page reload #1555
2929
- 🐛(frontend) fix Interlinking memory leak #1560
30+
- 🐛(frontend) preserve @ character when esc is pressed after typing it #1512
3031
- 🐛(frontend) button new doc UI fix #1557
3132
- 🐛(frontend) interlinking UI fix #1557
3233

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
@@ -808,6 +808,29 @@ test.describe('Doc Editor', () => {
808808
await expect(interlinkChild1.locator('svg').first()).toBeVisible();
809809
});
810810

811+
test('it keeps @ when pressing Escape', async ({ page, browserName }) => {
812+
const [randomDoc] = await createDoc(
813+
page,
814+
'doc-interlink-esc',
815+
browserName,
816+
1,
817+
);
818+
819+
await verifyDocName(page, randomDoc);
820+
821+
const editor = await getEditor({ page });
822+
await page.keyboard.press('@');
823+
824+
const searchInput = page.locator(
825+
"span[data-inline-content-type='interlinkingSearchInline'] input",
826+
);
827+
await expect(searchInput).toBeVisible();
828+
829+
await page.keyboard.press('Escape');
830+
831+
await expect(editor.getByText('@')).toBeVisible();
832+
});
833+
811834
test('it checks multiple big doc scroll to the top', async ({
812835
page,
813836
browserName,

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

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
StyleSchema,
44
} from '@blocknote/core';
55
import { useBlockNoteEditor } from '@blocknote/react';
6+
import type { KeyboardEvent } from 'react';
67
import { useEffect, useRef, useState } from 'react';
78
import { useTranslation } from 'react-i18next';
89
import { css } from 'styled-components';
@@ -99,6 +100,55 @@ export const SearchPage = ({
99100
}, 100);
100101
}, [inputRef]);
101102

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

234+
contentRef(null);
235+
227236
editor.insertInlineContent([
228237
{
229238
type: 'interlinkingLinkInline',

0 commit comments

Comments
 (0)