Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@ and this project adheres to

## [Unreleased]

### Added

- ✨ Add comments feature to the editor #1330
- ✨(backend) Comments on text editor #1330

### Fixed

- ♿(frontend) improve accessibility:
- ♿(frontend) improve share modal button accessibility #1626
- 🐛(frontend) fix toolbar not activated when reader #1640
- 🐛(frontend) preserve left panel width on window resize #1588

## [3.10.0] - 2025-11-18

Expand Down Expand Up @@ -40,7 +47,6 @@ and this project adheres to
### Security

- mitigate role escalation in the ask_for_access viewset #1580
- 🐛(frontend) preserve left panel width on window resize #1588

### Removed

Expand All @@ -54,7 +60,6 @@ and this project adheres to
- ✨(frontend) create skeleton component for DocEditor #1491
- ✨(frontend) add an EmojiPicker in the document tree and title #1381
- ✨(frontend) ajustable left panel #1456
- ✨ Add comments feature to the editor #1330

### Changed

Expand Down Expand Up @@ -183,7 +188,6 @@ and this project adheres to

### Added

- ✨(backend) Comments on text editor #1309
- 👷(CI) add bundle size check job #1268
- ✨(frontend) use title first emoji as doc icon in tree #1289

Expand Down
64 changes: 55 additions & 9 deletions src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,20 +241,66 @@ test.describe('Doc Editor', () => {
await expect(editor.getByText('Hello World Doc persisted 2')).toBeVisible();
});

test('it cannot edit if viewer', async ({ page }) => {
await mockedDocument(page, {
user_role: 'reader',
});
test('it cannot edit if viewer but see and can get resources', async ({
page,
browserName,
}) => {
const [docTitle] = await createDoc(page, 'doc-viewer', browserName, 1);
await verifyDocName(page, docTitle);

await goToGridDoc(page);
await writeInEditor({ page, text: 'Hello World' });

const card = page.getByLabel('It is the card information');
await expect(card).toBeVisible();
await page.getByRole('button', { name: 'Share' }).click();
await updateShareLink(page, 'Public', 'Reading');

// Close the modal
await page.getByRole('button', { name: 'close' }).first().click();

await expect(card.getByText('Reader')).toBeVisible();
const { otherPage, cleanup } = await connectOtherUserToDoc({
browserName,
docUrl: page.url(),
withoutSignIn: true,
docTitle,
});

const editor = page.locator('.ProseMirror');
await expect(
otherPage.getByLabel('It is the card information').getByText('Reader'),
).toBeVisible();

// Cannot edit
const editor = otherPage.locator('.ProseMirror');
await expect(editor).toHaveAttribute('contenteditable', 'false');

// Owner add a image
const fileChooserPromise = page.waitForEvent('filechooser');
await page.locator('.bn-block-outer').last().fill('/');
await page.getByText('Resizable image with caption').click();
await page.getByText('Upload image').click();

const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(
path.join(__dirname, 'assets/logo-suite-numerique.png'),
);

// Owner see the image
await expect(
page.locator('.--docs--editor-container img.bn-visual-media').first(),
).toBeVisible();

// Viewser see the image
const viewerImg = otherPage
.locator('.--docs--editor-container img.bn-visual-media')
.first();
await expect(viewerImg).toBeVisible();

// Viewer can download the image
await viewerImg.click();
const downloadPromise = otherPage.waitForEvent('download');
await otherPage.getByRole('button', { name: 'Download image' }).click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toBe('logo-suite-numerique.png');

await cleanup();
});

test('it adds an image to the doc editor', async ({ page, browserName }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,13 @@ export const BlockNoteReader = ({
editor={editor}
editable={false}
theme="light"
aria-label={t('Document version viewer')}
aria-label={t('Document viewer')}
formattingToolbar={false}
slashMenu={false}
comments={false}
/>
>
<BlockNoteToolbar />
</BlockNoteView>
</Box>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,14 @@ const UploadLoaderBlockComponent = ({
editor,
}: UploadLoaderBlockComponentProps) => {
const mediaUrl = useMediaUrl();
const isEditable = editor.isEditable;

useEffect(() => {
if (!block.props.blockUploadUrl || block.props.type !== 'loading') {
if (
!block.props.blockUploadUrl ||
block.props.type !== 'loading' ||
!isEditable
) {
return;
}

Expand Down Expand Up @@ -108,7 +113,7 @@ const UploadLoaderBlockComponent = ({
/* During collaboration, another user might have updated the block */
}
});
}, [block, editor, mediaUrl]);
}, [block, editor, mediaUrl, isEditable]);

return (
<Box className="bn-visual-media-wrapper" $direction="row" $gap="0.5rem">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@ export const InterlinkingLinkInlineContent = createReactInlineContentSpec(
content: 'none',
},
{
render: ({ inlineContent, updateInlineContent }) => {
render: ({ editor, inlineContent, updateInlineContent }) => {
const { data: doc } = useDoc({ id: inlineContent.props.docId });
const isEditable = editor.isEditable;

/**
* Update the content title if the referenced doc title changes
*/
useEffect(() => {
if (doc?.title && doc.title !== inlineContent.props.title) {
if (
isEditable &&
doc?.title &&
doc.title !== inlineContent.props.title
) {
updateInlineContent({
type: 'interlinkingLinkInline',
props: {
Expand All @@ -50,7 +55,7 @@ export const InterlinkingLinkInlineContent = createReactInlineContentSpec(
* not when inlineContent.props.title changes.
*/
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [doc?.title]);
}, [doc?.title, isEditable]);

return <LinkSelected {...inlineContent.props} />;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const SearchPage = ({
const [search, setSearch] = useState('');
const { isDesktop } = useResponsiveStore();
const { untitledDocument } = useTrans();
const isEditable = editor.isEditable;

/**
* createReactInlineContentSpec add automatically the focus after
Expand All @@ -101,6 +102,10 @@ export const SearchPage = ({
}, [inputRef]);

const closeSearch = (insertContent: string) => {
if (!isEditable) {
return;
}

updateInlineContent({
type: 'interlinkingSearchInline',
props: {
Expand Down Expand Up @@ -223,6 +228,10 @@ export const SearchPage = ({
search={search}
filters={{ target: DocSearchTarget.CURRENT }}
onSelect={(doc) => {
if (!isEditable) {
return;
}

updateInlineContent({
type: 'interlinkingSearchInline',
props: {
Expand Down
Loading