🐛(frontend) fix toolbar not activated when reader
When user was a reader of the document, the toolbar of the BlockNote editor was not activated, making it impossible to download resources like images. We add the toolbar even in viewer mode. We block as well automatic document mutation from custom blocks when the editor is in viewer mode to avoid unwanted modifications.
This commit is contained in:
@@ -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 writeInEditor({ page, text: 'Hello World' });
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await updateShareLink(page, 'Public', 'Reading');
|
||||
|
||||
// Close the modal
|
||||
await page.getByRole('button', { name: 'close' }).first().click();
|
||||
|
||||
const { otherPage, cleanup } = await connectOtherUserToDoc({
|
||||
browserName,
|
||||
docUrl: page.url(),
|
||||
withoutSignIn: true,
|
||||
docTitle,
|
||||
});
|
||||
|
||||
await goToGridDoc(page);
|
||||
await expect(
|
||||
otherPage.getByLabel('It is the card information').getByText('Reader'),
|
||||
).toBeVisible();
|
||||
|
||||
const card = page.getByLabel('It is the card information');
|
||||
await expect(card).toBeVisible();
|
||||
|
||||
await expect(card.getByText('Reader')).toBeVisible();
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
// 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 }) => {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -59,9 +59,15 @@ const UploadLoaderBlockComponent = ({
|
||||
editor,
|
||||
}: UploadLoaderBlockComponentProps) => {
|
||||
const mediaUrl = useMediaUrl();
|
||||
const isEditable = editor.isEditable;
|
||||
|
||||
useEffect(() => {
|
||||
if (!block.props.blockUploadUrl || block.props.type !== 'loading') {
|
||||
const shouldCheckStatus =
|
||||
block.props.blockUploadUrl &&
|
||||
block.props.type === 'loading' &&
|
||||
isEditable;
|
||||
|
||||
if (!shouldCheckStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -108,7 +114,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">
|
||||
|
||||
@@ -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: {
|
||||
@@ -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} />;
|
||||
},
|
||||
|
||||
@@ -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
|
||||
@@ -101,6 +102,10 @@ export const SearchPage = ({
|
||||
}, [inputRef]);
|
||||
|
||||
const closeSearch = (insertContent: string) => {
|
||||
if (!isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateInlineContent({
|
||||
type: 'interlinkingSearchInline',
|
||||
props: {
|
||||
@@ -223,6 +228,10 @@ export const SearchPage = ({
|
||||
search={search}
|
||||
filters={{ target: DocSearchTarget.CURRENT }}
|
||||
onSelect={(doc) => {
|
||||
if (!isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateInlineContent({
|
||||
type: 'interlinkingSearchInline',
|
||||
props: {
|
||||
|
||||
Reference in New Issue
Block a user