⚡️(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
This commit is contained in:
@@ -6,12 +6,16 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- ⚡️(frontend) export html #1669
|
||||
|
||||
### Changed
|
||||
|
||||
- ♿(frontend) improve accessibility:
|
||||
- ♿(frontend) add skip to content button for keyboard accessibility #1624
|
||||
- ♿(frontend) fix toggle panel button a11y labels #1634
|
||||
- ⚡️(frontend) Enhance/html copy to download #1669
|
||||
- ⚡️(frontend) improve Comments feature #1687
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { createDoc, getOtherBrowserName, verifyDocName } from './utils-common';
|
||||
import {
|
||||
closeHeaderMenu,
|
||||
createDoc,
|
||||
getOtherBrowserName,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
import { writeInEditor } from './utils-editor';
|
||||
import {
|
||||
addNewMember,
|
||||
@@ -116,8 +121,7 @@ test.describe('Doc Comments', () => {
|
||||
await createDoc(page, 'comment-interaction', browserName, 1);
|
||||
|
||||
// Checks add react reaction
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.locator('.bn-block-outer').last().fill('Hello World');
|
||||
const editor = await writeInEditor({ page, text: 'Hello' });
|
||||
await editor.getByText('Hello').selectText();
|
||||
await page.getByRole('button', { name: 'Comment' }).click();
|
||||
|
||||
@@ -181,6 +185,28 @@ test.describe('Doc Comments', () => {
|
||||
'background-color',
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
|
||||
/* Delete the last comment remove the thread */
|
||||
await editor.getByText('Hello').selectText();
|
||||
await page.getByRole('button', { name: 'Comment' }).click();
|
||||
|
||||
await thread.getByRole('paragraph').first().fill('This is a new comment');
|
||||
await thread.locator('[data-test="save"]').click();
|
||||
|
||||
await expect(editor.getByText('Hello')).toHaveCSS(
|
||||
'background-color',
|
||||
'rgba(237, 180, 0, 0.4)',
|
||||
);
|
||||
await editor.getByText('Hello').click();
|
||||
|
||||
await thread.getByText('This is a new comment').first().hover();
|
||||
await thread.locator('[data-test="moreactions"]').first().click();
|
||||
await thread.getByRole('menuitem', { name: 'Delete comment' }).click();
|
||||
|
||||
await expect(editor.getByText('Hello')).toHaveCSS(
|
||||
'background-color',
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
});
|
||||
|
||||
test('it checks the comments abilities', async ({ page, browserName }) => {
|
||||
@@ -293,3 +319,27 @@ test.describe('Doc Comments', () => {
|
||||
await cleanup();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Doc Comments mobile', () => {
|
||||
test.use({ viewport: { width: 500, height: 1200 } });
|
||||
|
||||
test('Comments are not visible on mobile', async ({ page, browserName }) => {
|
||||
const [title] = await createDoc(
|
||||
page,
|
||||
'comment-mobile',
|
||||
browserName,
|
||||
1,
|
||||
true,
|
||||
);
|
||||
|
||||
await closeHeaderMenu(page);
|
||||
|
||||
await verifyDocName(page, title);
|
||||
|
||||
// Checks add react reaction
|
||||
const editor = await writeInEditor({ page, text: 'Hello' });
|
||||
await editor.getByText('Hello').selectText();
|
||||
await expect(page.getByRole('button', { name: 'Comment' })).toBeHidden();
|
||||
await expect(page.getByRole('button', { name: 'Paragraph' })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* This file is adapted from BlockNote's AddCommentButton component
|
||||
* https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/FormattingToolbar/DefaultButtons/AddCommentButton.tsx
|
||||
*/
|
||||
|
||||
import {
|
||||
useBlockNoteEditor,
|
||||
useComponentsContext,
|
||||
@@ -10,6 +15,7 @@ import { css } from 'styled-components';
|
||||
import { Box, Icon } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { useDocStore } from '@/features/docs/doc-management';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import {
|
||||
DocsBlockSchema,
|
||||
@@ -22,6 +28,7 @@ export const CommentToolbarButton = () => {
|
||||
const { currentDoc } = useDocStore();
|
||||
const { t } = useTranslation();
|
||||
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
|
||||
const editor = useBlockNoteEditor<
|
||||
DocsBlockSchema,
|
||||
@@ -35,7 +42,18 @@ export const CommentToolbarButton = () => {
|
||||
return !!selectedBlocks.find((block) => block.content !== undefined);
|
||||
}, [selectedBlocks]);
|
||||
|
||||
const focusOnInputThread = () => {
|
||||
// Use setTimeout to ensure the DOM has been updated with the new comment
|
||||
setTimeout(() => {
|
||||
const threadElement = document.querySelector<HTMLElement>(
|
||||
'.bn-thread .bn-editor',
|
||||
);
|
||||
threadElement?.focus();
|
||||
}, 400);
|
||||
};
|
||||
|
||||
if (
|
||||
!isDesktop ||
|
||||
!show ||
|
||||
!editor.isEditable ||
|
||||
!Components ||
|
||||
@@ -51,6 +69,7 @@ export const CommentToolbarButton = () => {
|
||||
onClick={() => {
|
||||
editor.comments?.startPendingComment();
|
||||
editor.formattingToolbar.closeMenu();
|
||||
focusOnInputThread();
|
||||
}}
|
||||
aria-haspopup="dialog"
|
||||
data-test="comment-toolbar-button"
|
||||
|
||||
@@ -117,6 +117,21 @@ export class DocsThreadStore extends ThreadStore {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls to the bottom of a thread modal
|
||||
* @param threadId
|
||||
*/
|
||||
private scrollToBottomOfThread() {
|
||||
// Use setTimeout to ensure the DOM has been updated with the new comment
|
||||
setTimeout(() => {
|
||||
const threadElement = document.querySelector('.bn-thread');
|
||||
threadElement?.scrollBy({
|
||||
top: threadElement.scrollHeight,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all subscribers about the current thread state
|
||||
*/
|
||||
@@ -345,6 +360,10 @@ export class DocsThreadStore extends ThreadStore {
|
||||
await this.refreshThread(threadId);
|
||||
}
|
||||
this.ping(threadId);
|
||||
|
||||
// Auto-scroll to bottom of thread after adding comment
|
||||
this.scrollToBottomOfThread();
|
||||
|
||||
return serverCommentToClientComment(comment);
|
||||
};
|
||||
|
||||
@@ -405,10 +424,20 @@ export class DocsThreadStore extends ThreadStore {
|
||||
// Optimistically remove the comment locally if we have the thread
|
||||
const existing = this.threads.get(threadId);
|
||||
if (existing) {
|
||||
const updatedComments = existing.comments.filter(
|
||||
(c) => c.id !== commentId,
|
||||
);
|
||||
|
||||
// If this was the last comment, delete the thread
|
||||
if (updatedComments.length === 0) {
|
||||
await this.deleteThread({ threadId });
|
||||
return;
|
||||
}
|
||||
|
||||
const updated: ClientThreadData = {
|
||||
...existing,
|
||||
updatedAt: new Date(),
|
||||
comments: existing.comments.filter((c) => c.id !== commentId),
|
||||
comments: updatedComments,
|
||||
};
|
||||
this.upsertClientThreadData(updated);
|
||||
this.notifySubscribers();
|
||||
@@ -419,10 +448,6 @@ export class DocsThreadStore extends ThreadStore {
|
||||
this.ping(threadId);
|
||||
};
|
||||
|
||||
/**
|
||||
* UI not implemented
|
||||
* @param _options
|
||||
*/
|
||||
public deleteThread = async (_options: { threadId: string }) => {
|
||||
const response = await fetchAPI(
|
||||
`documents/${this.docId}/threads/${_options.threadId}/`,
|
||||
|
||||
@@ -13,6 +13,10 @@ export const cssComments = (
|
||||
background: ${canSeeComment ? '#EDB40066' : 'transparent'};
|
||||
color: var(--c--globals--colors--gray-700);
|
||||
}
|
||||
|
||||
[data-show-selection] {
|
||||
color: HighlightText;
|
||||
}
|
||||
}
|
||||
|
||||
em-emoji-picker {
|
||||
|
||||
@@ -4,12 +4,13 @@ import { css } from 'styled-components';
|
||||
|
||||
import { Box, Text } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { Doc, useDocUtils, useTrans } from '@/docs/doc-management';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import ChildDocument from '../assets/child-document.svg';
|
||||
import PinnedDocumentIcon from '../assets/pinned-document.svg';
|
||||
import SimpleFileIcon from '../assets/simple-document.svg';
|
||||
import { useDocUtils, useTrans } from '../hooks';
|
||||
import { Doc } from '../types';
|
||||
|
||||
const ItemTextCss = css`
|
||||
overflow: hidden;
|
||||
|
||||
Reference in New Issue
Block a user