Skip to content

Commit 045daa3

Browse files
committed
⚡️(frontend) improve Comments feature
Improve the comments feature to reduce annoyance: - gives focus on input when opening comment threads - hide comment button when mobile view - improve contrast of overline commented text - remove thread if last comment deleted - scroll to bottom thread when adding new comment
1 parent 8036f16 commit 045daa3

File tree

6 files changed

+103
-8
lines changed

6 files changed

+103
-8
lines changed

CHANGELOG.md

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

1111
- ♿(frontend) improve accessibility:
1212
- ♿(frontend) add skip to content button for keyboard accessibility #1624
13+
- ⚡️(frontend) improve Comments feature #1687
1314

1415
### Fixed
1516

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

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,7 @@ test.describe('Doc Comments', () => {
116116
await createDoc(page, 'comment-interaction', browserName, 1);
117117

118118
// Checks add react reaction
119-
const editor = page.locator('.ProseMirror');
120-
await editor.locator('.bn-block-outer').last().fill('Hello World');
119+
const editor = await writeInEditor({ page, text: 'Hello' });
121120
await editor.getByText('Hello').selectText();
122121
await page.getByRole('button', { name: 'Comment' }).click();
123122

@@ -181,6 +180,28 @@ test.describe('Doc Comments', () => {
181180
'background-color',
182181
'rgba(0, 0, 0, 0)',
183182
);
183+
184+
/* Delete the last comment remove the thread */
185+
await editor.getByText('Hello').selectText();
186+
await page.getByRole('button', { name: 'Comment' }).click();
187+
188+
await thread.getByRole('paragraph').first().fill('This is a new comment');
189+
await thread.locator('[data-test="save"]').click();
190+
191+
await expect(editor.getByText('Hello')).toHaveCSS(
192+
'background-color',
193+
'rgba(237, 180, 0, 0.4)',
194+
);
195+
await editor.getByText('Hello').click();
196+
197+
await thread.getByText('This is a new comment').first().hover();
198+
await thread.locator('[data-test="moreactions"]').first().click();
199+
await thread.getByRole('menuitem', { name: 'Delete comment' }).click();
200+
201+
await expect(editor.getByText('Hello')).toHaveCSS(
202+
'background-color',
203+
'rgba(0, 0, 0, 0)',
204+
);
184205
});
185206

186207
test('it checks the comments abilities', async ({ page, browserName }) => {
@@ -293,3 +314,27 @@ test.describe('Doc Comments', () => {
293314
await cleanup();
294315
});
295316
});
317+
318+
test.describe('Doc Comments mobile', () => {
319+
test.use({ viewport: { width: 500, height: 1200 } });
320+
321+
test('Comments are not visible on mobile', async ({ page, browserName }) => {
322+
await page
323+
.locator('header')
324+
.first()
325+
.getByLabel('Open the header menu')
326+
.click();
327+
await createDoc(page, 'comment-mobile', browserName, 1);
328+
await page
329+
.locator('header')
330+
.first()
331+
.getByLabel('Open the header menu')
332+
.click();
333+
334+
// Checks add react reaction
335+
const editor = await writeInEditor({ page, text: 'Hello' });
336+
await editor.getByText('Hello').selectText();
337+
await expect(page.getByRole('button', { name: 'Comment' })).toBeHidden();
338+
await expect(page.getByRole('button', { name: 'Paragraph' })).toBeVisible();
339+
});
340+
});

src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/CommentToolbarButton.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/**
2+
* This file is adapted from BlockNote's AddCommentButton component
3+
* https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/FormattingToolbar/DefaultButtons/AddCommentButton.tsx
4+
*/
5+
16
import {
27
useBlockNoteEditor,
38
useComponentsContext,
@@ -10,6 +15,7 @@ import { css } from 'styled-components';
1015
import { Box, Icon } from '@/components';
1116
import { useCunninghamTheme } from '@/cunningham';
1217
import { useDocStore } from '@/features/docs/doc-management';
18+
import { useResponsiveStore } from '@/stores';
1319

1420
import {
1521
DocsBlockSchema,
@@ -22,6 +28,7 @@ export const CommentToolbarButton = () => {
2228
const { currentDoc } = useDocStore();
2329
const { t } = useTranslation();
2430
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
31+
const { isDesktop } = useResponsiveStore();
2532

2633
const editor = useBlockNoteEditor<
2734
DocsBlockSchema,
@@ -35,7 +42,18 @@ export const CommentToolbarButton = () => {
3542
return !!selectedBlocks.find((block) => block.content !== undefined);
3643
}, [selectedBlocks]);
3744

45+
const focusOnInputThread = () => {
46+
// Use setTimeout to ensure the DOM has been updated with the new comment
47+
setTimeout(() => {
48+
const threadElement = document.querySelector<HTMLElement>(
49+
'.bn-thread .bn-editor',
50+
);
51+
threadElement?.focus();
52+
}, 400);
53+
};
54+
3855
if (
56+
!isDesktop ||
3957
!show ||
4058
!editor.isEditable ||
4159
!Components ||
@@ -51,6 +69,7 @@ export const CommentToolbarButton = () => {
5169
onClick={() => {
5270
editor.comments?.startPendingComment();
5371
editor.formattingToolbar.closeMenu();
72+
focusOnInputThread();
5473
}}
5574
aria-haspopup="dialog"
5675
data-test="comment-toolbar-button"

src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/DocsThreadStore.tsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,21 @@ export class DocsThreadStore extends ThreadStore {
117117
});
118118
}
119119

120+
/**
121+
* Scrolls to the bottom of a thread modal
122+
* @param threadId
123+
*/
124+
private scrollToBottomOfThread() {
125+
// Use setTimeout to ensure the DOM has been updated with the new comment
126+
setTimeout(() => {
127+
const threadElement = document.querySelector('.bn-thread');
128+
threadElement?.scrollBy({
129+
top: threadElement.scrollHeight,
130+
behavior: 'smooth',
131+
});
132+
}, 200);
133+
}
134+
120135
/**
121136
* Notifies all subscribers about the current thread state
122137
*/
@@ -345,6 +360,10 @@ export class DocsThreadStore extends ThreadStore {
345360
await this.refreshThread(threadId);
346361
}
347362
this.ping(threadId);
363+
364+
// Auto-scroll to bottom of thread after adding comment
365+
this.scrollToBottomOfThread();
366+
348367
return serverCommentToClientComment(comment);
349368
};
350369

@@ -405,10 +424,20 @@ export class DocsThreadStore extends ThreadStore {
405424
// Optimistically remove the comment locally if we have the thread
406425
const existing = this.threads.get(threadId);
407426
if (existing) {
427+
const updatedComments = existing.comments.filter(
428+
(c) => c.id !== commentId,
429+
);
430+
431+
// If this was the last comment, delete the thread
432+
if (updatedComments.length === 0) {
433+
await this.deleteThread({ threadId });
434+
return;
435+
}
436+
408437
const updated: ClientThreadData = {
409438
...existing,
410439
updatedAt: new Date(),
411-
comments: existing.comments.filter((c) => c.id !== commentId),
440+
comments: updatedComments,
412441
};
413442
this.upsertClientThreadData(updated);
414443
this.notifySubscribers();
@@ -419,10 +448,6 @@ export class DocsThreadStore extends ThreadStore {
419448
this.ping(threadId);
420449
};
421450

422-
/**
423-
* UI not implemented
424-
* @param _options
425-
*/
426451
public deleteThread = async (_options: { threadId: string }) => {
427452
const response = await fetchAPI(
428453
`documents/${this.docId}/threads/${_options.threadId}/`,

src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/styles.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ export const cssComments = (
1313
background: ${canSeeComment ? '#EDB40066' : 'transparent'};
1414
color: var(--c--globals--colors--gray-700);
1515
}
16+
17+
[data-show-selection] {
18+
color: HighlightText;
19+
}
1620
}
1721
1822
em-emoji-picker {

src/frontend/apps/impress/src/features/docs/doc-management/components/SimpleDocItem.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import { css } from 'styled-components';
44

55
import { Box, Text } from '@/components';
66
import { useCunninghamTheme } from '@/cunningham';
7-
import { Doc, useDocUtils, useTrans } from '@/docs/doc-management';
87
import { useResponsiveStore } from '@/stores';
98

109
import ChildDocument from '../assets/child-document.svg';
1110
import PinnedDocumentIcon from '../assets/pinned-document.svg';
1211
import SimpleFileIcon from '../assets/simple-document.svg';
12+
import { useDocUtils, useTrans } from '../hooks';
13+
import { Doc } from '../types';
1314

1415
const ItemTextCss = css`
1516
overflow: hidden;

0 commit comments

Comments
 (0)