Skip to content

Commit 7ed46ab

Browse files
committed
🛂(frontend) block editing title when not allowed
We had a case where the title input was editable even when the user did not have the right to edit it because of websocket problem during collaboration. We fixed this issue by checking the collaboration status before allowing the edition of the title.
1 parent 18f4ab8 commit 7ed46ab

File tree

7 files changed

+66
-54
lines changed

7 files changed

+66
-54
lines changed

CHANGELOG.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ and this project adheres to
1414

1515
- ♻️(frontend) replace Arial font-family with token font #1411
1616
- ♿(frontend) improve accessibility:
17-
- #1354
18-
- #1349
17+
- ♿(frontend) enable enter key to open documentss #1354
18+
- ♿(frontend) improve modal a11y: structure, labels, title #1349
19+
- ♿improve NVDA navigation in DocShareModal #1396
1920
- ♿ improve accessibility by adding landmark roles to layout #1394
2021
- ♿ add document visible in list and openable via enter key #1365
2122
- ♿ add pdf outline property to enable bookmarks display #1368
@@ -29,20 +30,15 @@ and this project adheres to
2930
- ♿ add h1 for SR on 40X pages and remove alt texts #1438
3031
- ♿ update labels and shared document icon accessibility #1442
3132

32-
3333
### Fixed
3434

3535
- 🐛(backend) duplicate sub docs as root for reader users
3636
- ⚗️(service-worker) remove index from cache first strategy #1395
3737
- 🐛(frontend) fix 404 page when reload 403 page #1402
3838
- 🐛(frontend) fix legacy role computation #1376
39+
- 🛂(frontend) block editing title when not allowed #1412
3940
- 🐛(frontend) scroll back to top when navigate to a document #1406
4041

41-
### Changed
42-
43-
- ♿(frontend) improve accessibility:
44-
- ♿improve NVDA navigation in DocShareModal #1396
45-
4642
## [3.7.0] - 2025-09-12
4743

4844
### Added

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

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable playwright/no-conditional-expect */
22
import path from 'path';
33

4-
import { chromium, expect, test } from '@playwright/test';
4+
import { expect, test } from '@playwright/test';
55
import cs from 'convert-stream';
66

77
import {
@@ -12,6 +12,7 @@ import {
1212
verifyDocName,
1313
} from './utils-common';
1414
import { getEditor, openSuggestionMenu, writeInEditor } from './utils-editor';
15+
import { connectOtherUserToDoc, updateShareLink } from './utils-share';
1516
import { createRootSubPage, navigateToPageFromTree } from './utils-sub-pages';
1617

1718
test.beforeEach(async ({ page }) => {
@@ -560,20 +561,7 @@ test.describe('Doc Editor', () => {
560561

561562
await page.getByRole('button', { name: 'Share' }).click();
562563

563-
await page.getByTestId('doc-visibility').click();
564-
565-
await page
566-
.getByRole('menuitem', {
567-
name: 'Public',
568-
})
569-
.click();
570-
571-
await expect(
572-
page.getByText('The document visibility has been updated.'),
573-
).toBeVisible();
574-
575-
await page.getByTestId('doc-access-mode').click();
576-
await page.getByRole('menuitem', { name: 'Editing' }).click();
564+
await updateShareLink(page, 'Public', 'Editing');
577565

578566
// Close the modal
579567
await page.getByRole('button', { name: 'close' }).first().click();
@@ -597,17 +585,12 @@ test.describe('Doc Editor', () => {
597585
* We open another browser that will connect to the collaborative server
598586
* and will block the current browser to edit the doc.
599587
*/
600-
const otherBrowser = await chromium.launch({ headless: true });
601-
const otherContext = await otherBrowser.newContext({
602-
locale: 'en-US',
603-
timezoneId: 'Europe/Paris',
604-
permissions: [],
605-
storageState: {
606-
cookies: [],
607-
origins: [],
608-
},
588+
const { otherPage } = await connectOtherUserToDoc({
589+
browserName,
590+
docUrl: urlChildDoc,
591+
docTitle: childTitle,
592+
withoutSignIn: true,
609593
});
610-
const otherPage = await otherContext.newPage();
611594

612595
const webSocketPromise = otherPage.waitForEvent(
613596
'websocket',
@@ -648,6 +631,11 @@ test.describe('Doc Editor', () => {
648631

649632
await expect(editor).toHaveAttribute('contenteditable', 'false');
650633

634+
await expect(
635+
page.getByRole('textbox', { name: 'Document title' }),
636+
).toBeHidden();
637+
await expect(page.getByRole('heading', { name: childTitle })).toBeVisible();
638+
651639
await page.goto(urlParentDoc);
652640

653641
await verifyDocName(page, parentTitle);
@@ -664,6 +652,11 @@ test.describe('Doc Editor', () => {
664652

665653
await expect(editor).toHaveAttribute('contenteditable', 'true');
666654

655+
await expect(
656+
page.getByRole('textbox', { name: 'Document title' }),
657+
).toContainText(childTitle);
658+
await expect(page.getByRole('heading', { name: childTitle })).toBeHidden();
659+
667660
await expect(
668661
card.getByText('Others are editing. Your network prevent changes.'),
669662
).toBeHidden();

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ test.describe('Doc Header', () => {
142142

143143
await goToGridDoc(page);
144144

145+
await expect(
146+
page.getByRole('textbox', { name: 'Document title' }),
147+
).toContainText('Mocked document');
148+
145149
await expect(
146150
page.getByRole('button', { name: 'Export the document' }),
147151
).toBeVisible();
@@ -218,6 +222,10 @@ test.describe('Doc Header', () => {
218222

219223
await goToGridDoc(page);
220224

225+
await expect(
226+
page.getByRole('textbox', { name: 'Document title' }),
227+
).toContainText('Mocked document');
228+
221229
await expect(
222230
page.getByRole('button', { name: 'Export the document' }),
223231
).toBeVisible();
@@ -286,6 +294,10 @@ test.describe('Doc Header', () => {
286294

287295
await goToGridDoc(page);
288296

297+
await expect(
298+
page.getByRole('heading', { name: 'Mocked document' }),
299+
).toBeVisible();
300+
289301
await expect(
290302
page.getByRole('button', { name: 'Export the document' }),
291303
).toBeVisible();

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,11 @@ test.describe('Document create member', () => {
229229
.last()
230230
.fill('Hello World');
231231

232-
const urlDoc = page.url();
232+
const docUrl = page.url();
233233

234234
// Other user will request access
235235
const { otherPage, otherBrowserName, cleanup } =
236-
await connectOtherUserToDoc(browserName, urlDoc);
236+
await connectOtherUserToDoc({ browserName, docUrl });
237237

238238
await expect(
239239
otherPage.getByText('Insufficient access rights to view the document.'),

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,12 @@ test.describe('Doc Visibility: Restricted', () => {
157157
.last()
158158
.fill('Hello World');
159159

160-
const urlDoc = page.url();
160+
const docUrl = page.url();
161161

162-
const { otherBrowserName, otherPage } = await connectOtherUserToDoc(
162+
const { otherBrowserName, otherPage } = await connectOtherUserToDoc({
163163
browserName,
164-
urlDoc,
165-
);
164+
docUrl,
165+
});
166166

167167
await expect(
168168
otherPage.getByText('Insufficient access rights to view the document.'),

src/frontend/apps/e2e/__tests__/app-impress/utils-share.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99

1010
export type Role = 'Administrator' | 'Owner' | 'Member' | 'Editor' | 'Reader';
1111
export type LinkReach = 'Private' | 'Connected' | 'Public';
12-
export type LinkRole = 'Reading' | 'Edition';
12+
export type LinkRole = 'Reading' | 'Editing';
1313

1414
export const addNewMember = async (
1515
page: Page,
@@ -88,11 +88,17 @@ export const updateRoleUser = async (
8888
* @param docTitle The title of the document (optional).
8989
* @returns An object containing the other browser, context, and page.
9090
*/
91-
export const connectOtherUserToDoc = async (
92-
browserName: BrowserName,
93-
docUrl: string,
94-
docTitle?: string,
95-
) => {
91+
export const connectOtherUserToDoc = async ({
92+
browserName,
93+
docUrl,
94+
docTitle,
95+
withoutSignIn,
96+
}: {
97+
browserName: BrowserName;
98+
docUrl: string;
99+
docTitle?: string;
100+
withoutSignIn?: boolean;
101+
}) => {
96102
const otherBrowserName = BROWSERS.find((b) => b !== browserName);
97103
if (!otherBrowserName) {
98104
throw new Error('No alternative browser found');
@@ -111,15 +117,16 @@ export const connectOtherUserToDoc = async (
111117
const otherPage = await otherContext.newPage();
112118
await otherPage.goto(docUrl);
113119

114-
await otherPage
115-
.getByRole('main', { name: 'Main content' })
116-
.getByLabel('Login')
117-
.click({
118-
timeout: 15000,
119-
});
120-
121-
await keyCloakSignIn(otherPage, otherBrowserName, false);
120+
if (!withoutSignIn) {
121+
await otherPage
122+
.getByRole('main', { name: 'Main content' })
123+
.getByLabel('Login')
124+
.click({
125+
timeout: 15000,
126+
});
122127

128+
await keyCloakSignIn(otherPage, otherBrowserName, false);
129+
}
123130
if (docTitle) {
124131
await verifyDocName(otherPage, docTitle);
125132
}

src/frontend/apps/impress/src/features/docs/doc-header/components/DocTitle.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
KEY_DOC,
1212
KEY_LIST_DOC,
1313
useDocStore,
14+
useIsCollaborativeEditable,
1415
useTrans,
1516
useUpdateDoc,
1617
} from '@/docs/doc-management';
@@ -21,7 +22,10 @@ interface DocTitleProps {
2122
}
2223

2324
export const DocTitle = ({ doc }: DocTitleProps) => {
24-
if (!doc.abilities.partial_update) {
25+
const { isEditable, isLoading } = useIsCollaborativeEditable(doc);
26+
const readOnly = !doc.abilities.partial_update || !isEditable || isLoading;
27+
28+
if (readOnly) {
2529
return <DocTitleText />;
2630
}
2731

0 commit comments

Comments
 (0)