Skip to content

Commit a6b472a

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 9fcc221 commit a6b472a

File tree

6 files changed

+113
-10
lines changed

6 files changed

+113
-10
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ and this project adheres to
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- ⚡️(frontend) export html #1669
12+
913
### Changed
1014

1115
- ♿(frontend) improve accessibility:
1216
- ♿(frontend) add skip to content button for keyboard accessibility #1624
1317
- ♿(frontend) fix toggle panel button a11y labels #1634
14-
- ⚡️(frontend) Enhance/html copy to download #1669
18+
- ⚡️(frontend) improve Comments feature #1687
1519

1620
### Fixed
1721

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

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { expect, test } from '@playwright/test';
22

3-
import { createDoc, getOtherBrowserName, verifyDocName } from './utils-common';
3+
import {
4+
closeHeaderMenu,
5+
createDoc,
6+
getOtherBrowserName,
7+
verifyDocName,
8+
} from './utils-common';
49
import { writeInEditor } from './utils-editor';
510
import {
611
addNewMember,
@@ -116,8 +121,7 @@ test.describe('Doc Comments', () => {
116121
await createDoc(page, 'comment-interaction', browserName, 1);
117122

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

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

186212
test('it checks the comments abilities', async ({ page, browserName }) => {
@@ -293,3 +319,27 @@ test.describe('Doc Comments', () => {
293319
await cleanup();
294320
});
295321
});
322+
323+
test.describe('Doc Comments mobile', () => {
324+
test.use({ viewport: { width: 500, height: 1200 } });
325+
326+
test('Comments are not visible on mobile', async ({ page, browserName }) => {
327+
const [title] = await createDoc(
328+
page,
329+
'comment-mobile',
330+
browserName,
331+
1,
332+
true,
333+
);
334+
335+
await closeHeaderMenu(page);
336+
337+
await verifyDocName(page, title);
338+
339+
// Checks add react reaction
340+
const editor = await writeInEditor({ page, text: 'Hello' });
341+
await editor.getByText('Hello').selectText();
342+
await expect(page.getByRole('button', { name: 'Comment' })).toBeHidden();
343+
await expect(page.getByRole('button', { name: 'Paragraph' })).toBeVisible();
344+
});
345+
});

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)