Skip to content

Commit 0b15ebb

Browse files
committed
🛂(frontend) readers and editors can access share modal
Readers and editors of a document can access the share modal and see the list of members and their roles.
1 parent eee2003 commit 0b15ebb

File tree

16 files changed

+360
-104
lines changed

16 files changed

+360
-104
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ and this project adheres to
2121

2222
- 💄(frontend) error alert closeable on editor #284
2323
- ♻️(backend) Change email content #283
24+
- 🛂(frontend) viewers and editors can access share modal #302
2425

2526
## Fixed
2627

src/frontend/apps/e2e/__tests__/app-impress/common.ts

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,13 @@ export const goToGridDoc = async (
140140
export const mockedDocument = async (page: Page, json: object) => {
141141
await page.route('**/documents/**/', async (route) => {
142142
const request = route.request();
143-
if (request.method().includes('GET') && !request.url().includes('page=')) {
143+
if (
144+
request.method().includes('GET') &&
145+
!request.url().includes('page=') &&
146+
!request.url().includes('versions') &&
147+
!request.url().includes('accesses') &&
148+
!request.url().includes('invitations')
149+
) {
144150
await route.fulfill({
145151
json: {
146152
id: 'mocked-document-id',
@@ -168,3 +174,82 @@ export const mockedDocument = async (page: Page, json: object) => {
168174
}
169175
});
170176
};
177+
178+
export const mockedInvitations = async (page: Page, json?: object) => {
179+
await page.route('**/invitations/**/', async (route) => {
180+
const request = route.request();
181+
if (
182+
request.method().includes('GET') &&
183+
request.url().includes('invitations') &&
184+
request.url().includes('page=')
185+
) {
186+
await route.fulfill({
187+
json: {
188+
count: 1,
189+
next: null,
190+
previous: null,
191+
results: [
192+
{
193+
id: '120ec765-43af-4602-83eb-7f4e1224548a',
194+
abilities: {
195+
destroy: true,
196+
update: true,
197+
partial_update: true,
198+
retrieve: true,
199+
},
200+
created_at: '2024-10-03T12:19:26.107687Z',
201+
202+
document: '4888c328-8406-4412-9b0b-c0ba5b9e5fb6',
203+
role: 'editor',
204+
issuer: '7380f42f-02eb-4ad5-b8f0-037a0e66066d',
205+
is_expired: false,
206+
...json,
207+
},
208+
],
209+
},
210+
});
211+
} else {
212+
await route.continue();
213+
}
214+
});
215+
};
216+
217+
export const mockedAccesses = async (page: Page, json?: object) => {
218+
await page.route('**/accesses/**/', async (route) => {
219+
const request = route.request();
220+
if (
221+
request.method().includes('GET') &&
222+
request.url().includes('accesses') &&
223+
request.url().includes('page=')
224+
) {
225+
await route.fulfill({
226+
json: {
227+
count: 1,
228+
next: null,
229+
previous: null,
230+
results: [
231+
{
232+
id: 'bc8bbbc5-a635-4f65-9817-fd1e9ec8ef87',
233+
user: {
234+
id: 'b4a21bb3-722e-426c-9f78-9d190eda641c',
235+
236+
},
237+
team: '',
238+
role: 'reader',
239+
abilities: {
240+
destroy: true,
241+
update: true,
242+
partial_update: true,
243+
retrieve: true,
244+
set_role_to: ['administrator', 'editor'],
245+
},
246+
...json,
247+
},
248+
],
249+
},
250+
});
251+
} else {
252+
await route.continue();
253+
}
254+
});
255+
};

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

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

3-
import { createDoc, goToGridDoc, mockedDocument } from './common';
3+
import {
4+
createDoc,
5+
goToGridDoc,
6+
mockedAccesses,
7+
mockedDocument,
8+
mockedInvitations,
9+
} from './common';
410

511
test.beforeEach(async ({ page }) => {
612
await page.goto('/');
@@ -182,20 +188,55 @@ test.describe('Doc Header', () => {
182188
},
183189
});
184190

191+
await mockedInvitations(page);
192+
await mockedAccesses(page);
193+
185194
await goToGridDoc(page);
186195

187196
await expect(
188197
page.locator('h2').getByText('Mocked document'),
189198
).toHaveAttribute('contenteditable');
190199

191-
await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
192-
193200
await page.getByLabel('Open the document options').click();
194201

195202
await expect(page.getByRole('button', { name: 'Export' })).toBeVisible();
196203
await expect(
197204
page.getByRole('button', { name: 'Delete document' }),
198205
).toBeHidden();
206+
207+
// Click somewhere else to close the options
208+
await page.click('body', { position: { x: 0, y: 0 } });
209+
210+
await page.getByRole('button', { name: 'Share' }).click();
211+
212+
const shareModal = page.getByLabel('Share modal');
213+
214+
await expect(shareModal.getByLabel('Doc private')).toBeEnabled();
215+
await expect(shareModal.getByText('Search by email')).toBeVisible();
216+
217+
const invitationCard = shareModal.getByLabel('List invitation card');
218+
await expect(
219+
invitationCard.getByText('[email protected]'),
220+
).toBeVisible();
221+
await expect(
222+
invitationCard.getByRole('combobox', { name: 'Role' }),
223+
).toBeEnabled();
224+
await expect(
225+
invitationCard.getByRole('button', {
226+
name: 'delete',
227+
}),
228+
).toBeEnabled();
229+
230+
const memberCard = shareModal.getByLabel('List members card');
231+
await expect(memberCard.getByText('[email protected]')).toBeVisible();
232+
await expect(
233+
memberCard.getByRole('combobox', { name: 'Role' }),
234+
).toBeEnabled();
235+
await expect(
236+
memberCard.getByRole('button', {
237+
name: 'delete',
238+
}),
239+
).toBeEnabled();
199240
});
200241

201242
test('it checks the options available if editor', async ({ page }) => {
@@ -213,20 +254,62 @@ test.describe('Doc Header', () => {
213254
},
214255
});
215256

257+
await mockedInvitations(page, {
258+
abilities: {
259+
destroy: false,
260+
update: false,
261+
partial_update: false,
262+
retrieve: true,
263+
},
264+
});
265+
await mockedAccesses(page);
266+
216267
await goToGridDoc(page);
217268

218269
await expect(
219270
page.locator('h2').getByText('Mocked document'),
220271
).toHaveAttribute('contenteditable');
221272

222-
await expect(page.getByRole('button', { name: 'Share' })).toBeHidden();
223-
224273
await page.getByLabel('Open the document options').click();
225274

226275
await expect(page.getByRole('button', { name: 'Export' })).toBeVisible();
227276
await expect(
228277
page.getByRole('button', { name: 'Delete document' }),
229278
).toBeHidden();
279+
280+
// Click somewhere else to close the options
281+
await page.click('body', { position: { x: 0, y: 0 } });
282+
283+
await page.getByRole('button', { name: 'Share' }).click();
284+
285+
const shareModal = page.getByLabel('Share modal');
286+
287+
await expect(shareModal.getByLabel('Doc private')).toBeDisabled();
288+
await expect(shareModal.getByText('Search by email')).toBeHidden();
289+
290+
const invitationCard = shareModal.getByLabel('List invitation card');
291+
await expect(
292+
invitationCard.getByText('[email protected]'),
293+
).toBeVisible();
294+
await expect(
295+
invitationCard.getByRole('combobox', { name: 'Role' }),
296+
).toHaveAttribute('disabled');
297+
await expect(
298+
invitationCard.getByRole('button', {
299+
name: 'delete',
300+
}),
301+
).toBeHidden();
302+
303+
const memberCard = shareModal.getByLabel('List members card');
304+
await expect(memberCard.getByText('[email protected]')).toBeVisible();
305+
await expect(
306+
memberCard.getByRole('combobox', { name: 'Role' }),
307+
).toHaveAttribute('disabled');
308+
await expect(
309+
memberCard.getByRole('button', {
310+
name: 'delete',
311+
}),
312+
).toBeHidden();
230313
});
231314

232315
test('it checks the options available if reader', async ({ page }) => {
@@ -244,20 +327,61 @@ test.describe('Doc Header', () => {
244327
},
245328
});
246329

330+
await mockedInvitations(page, {
331+
abilities: {
332+
destroy: false,
333+
update: false,
334+
partial_update: false,
335+
retrieve: true,
336+
},
337+
});
338+
await mockedAccesses(page);
339+
247340
await goToGridDoc(page);
248341

249342
await expect(
250343
page.locator('h2').getByText('Mocked document'),
251344
).not.toHaveAttribute('contenteditable');
252345

253-
await expect(page.getByRole('button', { name: 'Share' })).toBeHidden();
254-
255346
await page.getByLabel('Open the document options').click();
256347

257-
await expect(page.getByRole('button', { name: 'Share' })).toBeHidden();
258348
await expect(page.getByRole('button', { name: 'Export' })).toBeVisible();
259349
await expect(
260350
page.getByRole('button', { name: 'Delete document' }),
261351
).toBeHidden();
352+
353+
// Click somewhere else to close the options
354+
await page.click('body', { position: { x: 0, y: 0 } });
355+
356+
await page.getByRole('button', { name: 'Share' }).click();
357+
358+
const shareModal = page.getByLabel('Share modal');
359+
360+
await expect(shareModal.getByLabel('Doc private')).toBeDisabled();
361+
await expect(shareModal.getByText('Search by email')).toBeHidden();
362+
363+
const invitationCard = shareModal.getByLabel('List invitation card');
364+
await expect(
365+
invitationCard.getByText('[email protected]'),
366+
).toBeVisible();
367+
await expect(
368+
invitationCard.getByRole('combobox', { name: 'Role' }),
369+
).toHaveAttribute('disabled');
370+
await expect(
371+
invitationCard.getByRole('button', {
372+
name: 'delete',
373+
}),
374+
).toBeHidden();
375+
376+
const memberCard = shareModal.getByLabel('List members card');
377+
await expect(memberCard.getByText('[email protected]')).toBeVisible();
378+
await expect(
379+
memberCard.getByRole('combobox', { name: 'Role' }),
380+
).toHaveAttribute('disabled');
381+
await expect(
382+
memberCard.getByRole('button', {
383+
name: 'delete',
384+
}),
385+
).toBeHidden();
262386
});
263387
});

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,17 @@ test.describe('Document list members', () => {
106106
await page.getByRole('option', { name: 'Administrator' }).click();
107107
await expect(page.getByText('The role has been updated')).toBeVisible();
108108

109+
const shareModal = page.getByLabel('Share modal');
110+
109111
// Admin still have the right to share
110-
await expect(page.locator('h3').getByText('Share')).toBeVisible();
112+
await expect(shareModal.getByLabel('Doc private')).toBeEnabled();
111113

112114
await SelectRoleCurrentUser.click();
113115
await page.getByRole('option', { name: 'Reader' }).click();
114116
await expect(page.getByText('The role has been updated')).toBeVisible();
115117

116118
// Reader does not have the right to share
117-
await expect(page.locator('h3').getByText('Share')).toBeHidden();
119+
await expect(shareModal.getByLabel('Doc private')).toBeDisabled();
118120
});
119121

120122
test('it checks the delete members', async ({ page, browserName }) => {

src/frontend/apps/impress/src/components/Box.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export interface BoxProps {
3333
$padding?: MarginPadding;
3434
$position?: CSSProperties['position'];
3535
$radius?: CSSProperties['borderRadius'];
36+
$shrink?: CSSProperties['flexShrink'];
3637
$transition?: CSSProperties['transition'];
3738
$width?: CSSProperties['width'];
3839
$wrap?: CSSProperties['flexWrap'];
@@ -68,6 +69,7 @@ export const Box = styled('div')<BoxProps>`
6869
${({ $padding }) => $padding && stylesPadding($padding)}
6970
${({ $position }) => $position && `position: ${$position};`}
7071
${({ $radius }) => $radius && `border-radius: ${$radius};`}
72+
${({ $shrink }) => $shrink && `flex-shrink: ${$shrink};`}
7173
${({ $transition }) => $transition && `transition: ${$transition};`}
7274
${({ $width }) => $width && `width: ${$width};`}
7375
${({ $wrap }) => $wrap && `flex-wrap: ${$wrap};`}

src/frontend/apps/impress/src/cunningham/cunningham-style.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ input:-webkit-autofill:focus {
150150
border-color: var(--c--components--forms-select--border-color-disabled-hover);
151151
}
152152

153+
.c__select--disabled .c__select__wrapper label,
154+
.c__select--disabled .c__select__wrapper input,
155+
.c__select--disabled .c__select__wrapper {
156+
cursor: not-allowed;
157+
}
158+
153159
.c__select__menu__item {
154160
transition: all var(--c--theme--transitions--duration)
155161
var(--c--theme--transitions--ease-out);
@@ -313,6 +319,14 @@ input:-webkit-autofill:focus {
313319
font-size: var(--c--components--forms-checkbox--text--size);
314320
}
315321

322+
.c__checkbox.c__checkbox--disabled .c__field__text {
323+
color: var(--c--theme--colors--greyscale-600);
324+
}
325+
326+
.c__switch.c__checkbox--disabled .c__switch__rail {
327+
cursor: not-allowed;
328+
}
329+
316330
/**
317331
* Button
318332
*/

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,13 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
3131
$align="center"
3232
$gap="1rem"
3333
>
34-
{doc.abilities.manage_accesses && (
35-
<Button
36-
onClick={() => {
37-
setIsModalShareOpen(true);
38-
}}
39-
>
40-
{t('Share')}
41-
</Button>
42-
)}
34+
<Button
35+
onClick={() => {
36+
setIsModalShareOpen(true);
37+
}}
38+
>
39+
{t('Share')}
40+
</Button>
4341
<DropButton
4442
button={
4543
<IconOptions

0 commit comments

Comments
 (0)