🚚(frontend) change visibility in share modal
We stop to propose to make the document public from the doc creation modal. We now propose to change the visibility of the document from the share modal.
This commit is contained in:
@@ -13,6 +13,10 @@ and this project adheres to
|
|||||||
|
|
||||||
- 🛂(frontend) access public docs without being logged #235
|
- 🛂(frontend) access public docs without being logged #235
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- 🚚(frontend) change visibility in share modal #235
|
||||||
|
|
||||||
|
|
||||||
## [1.3.0] - 2024-09-05
|
## [1.3.0] - 2024-09-05
|
||||||
|
|
||||||
|
|||||||
@@ -55,14 +55,19 @@ export const createDoc = async (
|
|||||||
})
|
})
|
||||||
.fill(randomDocs[i]);
|
.fill(randomDocs[i]);
|
||||||
|
|
||||||
if (isPublic) {
|
|
||||||
await page.getByText('Is it public ?').click();
|
|
||||||
}
|
|
||||||
|
|
||||||
await expect(buttonCreate).toBeEnabled();
|
await expect(buttonCreate).toBeEnabled();
|
||||||
await buttonCreate.click();
|
await buttonCreate.click();
|
||||||
|
|
||||||
await expect(page.locator('h2').getByText(randomDocs[i])).toBeVisible();
|
await expect(page.locator('h2').getByText(randomDocs[i])).toBeVisible();
|
||||||
|
|
||||||
|
if (isPublic) {
|
||||||
|
await page.getByRole('button', { name: 'Share' }).click();
|
||||||
|
await page.getByText('Doc private').click();
|
||||||
|
|
||||||
|
await page.locator('.c__modal__backdrop').click({
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomDocs;
|
return randomDocs;
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ test.describe('Doc Create', () => {
|
|||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await expect(card.getByLabel('Document name')).toBeVisible();
|
await expect(card.getByLabel('Document name')).toBeVisible();
|
||||||
|
|
||||||
await expect(card.getByText('Is it public ?')).toBeVisible();
|
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
card.getByRole('button', {
|
card.getByRole('button', {
|
||||||
name: 'Create the document',
|
name: 'Create the document',
|
||||||
@@ -46,14 +44,8 @@ test.describe('Doc Create', () => {
|
|||||||
await expect(buttonCreateHomepage).toBeVisible();
|
await expect(buttonCreateHomepage).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('create a new public doc', async ({ page, browserName }) => {
|
test('it creates a doc', async ({ page, browserName }) => {
|
||||||
const [docTitle] = await createDoc(
|
const [docTitle] = await createDoc(page, 'My new doc', browserName, 1);
|
||||||
page,
|
|
||||||
'My new doc',
|
|
||||||
browserName,
|
|
||||||
1,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(await page.locator('title').textContent()).toMatch(
|
expect(await page.locator('title').textContent()).toMatch(
|
||||||
/My new doc - Docs/,
|
/My new doc - Docs/,
|
||||||
@@ -69,11 +61,5 @@ test.describe('Doc Create', () => {
|
|||||||
await expect(datagrid.getByLabel('Loading data')).toBeHidden();
|
await expect(datagrid.getByLabel('Loading data')).toBeHidden();
|
||||||
|
|
||||||
await expect(datagrid.getByText(docTitle)).toBeVisible();
|
await expect(datagrid.getByText(docTitle)).toBeVisible();
|
||||||
|
|
||||||
const row = datagrid.getByRole('row').filter({
|
|
||||||
hasText: docTitle,
|
|
||||||
});
|
|
||||||
|
|
||||||
await expect(row.getByRole('cell').nth(0)).toHaveText('Public');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,13 +65,7 @@ test.describe('Doc Header', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it updates the doc', async ({ page, browserName }) => {
|
test('it updates the doc', async ({ page, browserName }) => {
|
||||||
const [randomDoc] = await createDoc(
|
const [randomDoc] = await createDoc(page, 'doc-update', browserName, 1);
|
||||||
page,
|
|
||||||
'doc-update',
|
|
||||||
browserName,
|
|
||||||
1,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
await expect(page.locator('h2').getByText(randomDoc)).toBeVisible();
|
await expect(page.locator('h2').getByText(randomDoc)).toBeVisible();
|
||||||
|
|
||||||
await page.getByLabel('Open the document options').click();
|
await page.getByLabel('Open the document options').click();
|
||||||
@@ -85,12 +79,7 @@ test.describe('Doc Header', () => {
|
|||||||
page.locator('h2').getByText(`Update document "${randomDoc}"`),
|
page.locator('h2').getByText(`Update document "${randomDoc}"`),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.getByRole('checkbox', { name: 'Is it public ?' }),
|
|
||||||
).toBeChecked();
|
|
||||||
|
|
||||||
await page.getByText('Document name').fill(`${randomDoc}-updated`);
|
await page.getByText('Document name').fill(`${randomDoc}-updated`);
|
||||||
await page.getByText('Is it public ?').click();
|
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByRole('button', {
|
.getByRole('button', {
|
||||||
@@ -116,8 +105,8 @@ test.describe('Doc Header', () => {
|
|||||||
.click();
|
.click();
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('checkbox', { name: 'Is it public ?' }),
|
page.getByRole('textbox', { name: 'Document name' }),
|
||||||
).not.toBeChecked();
|
).toHaveValue(`${randomDoc}-updated`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it deletes the doc', async ({ page, browserName }) => {
|
test('it deletes the doc', async ({ page, browserName }) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect, test } from '@playwright/test';
|
import { expect, test } from '@playwright/test';
|
||||||
|
|
||||||
import { createDoc, keyCloakSignIn } from './common';
|
import { keyCloakSignIn } from './common';
|
||||||
|
|
||||||
test.describe('Doc Routing', () => {
|
test.describe('Doc Routing', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
@@ -56,34 +56,4 @@ test.describe('Doc Routing: Not loggued', () => {
|
|||||||
}),
|
}),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('A public doc is accessible even when not authentified.', async ({
|
|
||||||
page,
|
|
||||||
browserName,
|
|
||||||
}) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await keyCloakSignIn(page, browserName);
|
|
||||||
|
|
||||||
const [docTitle] = await createDoc(
|
|
||||||
page,
|
|
||||||
'My new doc',
|
|
||||||
browserName,
|
|
||||||
1,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
const urlDoc = page.url();
|
|
||||||
|
|
||||||
await page
|
|
||||||
.getByRole('button', {
|
|
||||||
name: 'Logout',
|
|
||||||
})
|
|
||||||
.click();
|
|
||||||
|
|
||||||
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
|
|
||||||
|
|
||||||
await page.goto(urlDoc);
|
|
||||||
|
|
||||||
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
|
||||||
|
import { createDoc, keyCloakSignIn } from './common';
|
||||||
|
|
||||||
|
test.describe('Doc Visibility', () => {
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.goto('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Make a public doc', async ({ page, browserName }) => {
|
||||||
|
const [docTitle] = await createDoc(
|
||||||
|
page,
|
||||||
|
'My new doc',
|
||||||
|
browserName,
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
const header = page.locator('header').first();
|
||||||
|
await header.locator('h2').getByText('Docs').click();
|
||||||
|
|
||||||
|
const datagrid = page
|
||||||
|
.getByLabel('Datagrid of the documents page 1')
|
||||||
|
.getByRole('table');
|
||||||
|
|
||||||
|
await expect(datagrid.getByLabel('Loading data')).toBeHidden();
|
||||||
|
|
||||||
|
await expect(datagrid.getByText(docTitle)).toBeVisible();
|
||||||
|
|
||||||
|
const row = datagrid.getByRole('row').filter({
|
||||||
|
hasText: docTitle,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(row.getByRole('cell').nth(0)).toHaveText('Public');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Doc Visibility: Not loggued', () => {
|
||||||
|
test.use({ storageState: { cookies: [], origins: [] } });
|
||||||
|
|
||||||
|
test('A public doc is accessible even when not authentified.', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
await page.goto('/');
|
||||||
|
await keyCloakSignIn(page, browserName);
|
||||||
|
|
||||||
|
const [docTitle] = await createDoc(
|
||||||
|
page,
|
||||||
|
'My new doc',
|
||||||
|
browserName,
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByText('The document visiblitity has been updated.'),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
const urlDoc = page.url();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByRole('button', {
|
||||||
|
name: 'Logout',
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
|
||||||
|
|
||||||
|
await page.goto(urlDoc);
|
||||||
|
|
||||||
|
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -340,6 +340,10 @@ const config = {
|
|||||||
'forms-checkbox': {
|
'forms-checkbox': {
|
||||||
'border-radius': '0',
|
'border-radius': '0',
|
||||||
color: 'var(--c--theme--colors--primary-text)',
|
color: 'var(--c--theme--colors--primary-text)',
|
||||||
|
text: {
|
||||||
|
color: 'var(--c--theme--colors--greyscale-text)',
|
||||||
|
size: 'var(--c--theme--font--sizes--t)',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'forms-datepicker': {
|
'forms-datepicker': {
|
||||||
'border-radius': '0',
|
'border-radius': '0',
|
||||||
|
|||||||
@@ -308,6 +308,11 @@ input:-webkit-autofill:focus {
|
|||||||
transition: all 0.8s ease-in-out;
|
transition: all 0.8s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c__checkbox .c__field__text {
|
||||||
|
color: var(--c--components--forms-checkbox--text--color);
|
||||||
|
font-size: var(--c--components--forms-checkbox--text--size);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Button
|
* Button
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -463,6 +463,10 @@
|
|||||||
);
|
);
|
||||||
--c--components--forms-checkbox--border-radius: 0;
|
--c--components--forms-checkbox--border-radius: 0;
|
||||||
--c--components--forms-checkbox--color: var(--c--theme--colors--primary-text);
|
--c--components--forms-checkbox--color: var(--c--theme--colors--primary-text);
|
||||||
|
--c--components--forms-checkbox--text--color: var(
|
||||||
|
--c--theme--colors--greyscale-text
|
||||||
|
);
|
||||||
|
--c--components--forms-checkbox--text--size: var(--c--theme--font--sizes--t);
|
||||||
--c--components--forms-datepicker--border-radius: 0;
|
--c--components--forms-datepicker--border-radius: 0;
|
||||||
--c--components--forms-fileuploader--border-radius: 0;
|
--c--components--forms-fileuploader--border-radius: 0;
|
||||||
--c--components--forms-field--color: var(--c--theme--colors--primary-text);
|
--c--components--forms-field--color: var(--c--theme--colors--primary-text);
|
||||||
|
|||||||
@@ -469,6 +469,10 @@ export const tokens = {
|
|||||||
'forms-checkbox': {
|
'forms-checkbox': {
|
||||||
'border-radius': '0',
|
'border-radius': '0',
|
||||||
color: 'var(--c--theme--colors--primary-text)',
|
color: 'var(--c--theme--colors--primary-text)',
|
||||||
|
text: {
|
||||||
|
color: 'var(--c--theme--colors--greyscale-text)',
|
||||||
|
size: 'var(--c--theme--font--sizes--t)',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'forms-datepicker': { 'border-radius': '0' },
|
'forms-datepicker': { 'border-radius': '0' },
|
||||||
'forms-fileuploader': { 'border-radius': '0' },
|
'forms-fileuploader': { 'border-radius': '0' },
|
||||||
|
|||||||
@@ -6,17 +6,13 @@ import { Doc } from '../types';
|
|||||||
|
|
||||||
import { KEY_LIST_DOC } from './useDocs';
|
import { KEY_LIST_DOC } from './useDocs';
|
||||||
|
|
||||||
export type CreateDocParam = Pick<Doc, 'title' | 'is_public'>;
|
export type CreateDocParam = Pick<Doc, 'title'>;
|
||||||
|
|
||||||
export const createDoc = async ({
|
export const createDoc = async ({ title }: CreateDocParam): Promise<Doc> => {
|
||||||
title,
|
|
||||||
is_public,
|
|
||||||
}: CreateDocParam): Promise<Doc> => {
|
|
||||||
const response = await fetchAPI(`documents/`, {
|
const response = await fetchAPI(`documents/`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
title,
|
title,
|
||||||
is_public,
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import {
|
||||||
|
Switch,
|
||||||
|
VariantType,
|
||||||
|
useToastProvider,
|
||||||
|
} from '@openfun/cunningham-react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { Box, Card, IconBG } from '@/components';
|
||||||
|
|
||||||
|
import { KEY_DOC, KEY_LIST_DOC, useUpdateDoc } from '../api';
|
||||||
|
import { Doc } from '../types';
|
||||||
|
|
||||||
|
interface DocVisibilityProps {
|
||||||
|
doc: Doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DocVisibility = ({ doc }: DocVisibilityProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [docPublic, setDocPublic] = useState(doc.is_public);
|
||||||
|
const { toast } = useToastProvider();
|
||||||
|
const api = useUpdateDoc({
|
||||||
|
onSuccess: () => {
|
||||||
|
toast(
|
||||||
|
t('The document visiblitity has been updated.'),
|
||||||
|
VariantType.SUCCESS,
|
||||||
|
{
|
||||||
|
duration: 4000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
listInvalideQueries: [KEY_LIST_DOC],
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
$margin="tiny"
|
||||||
|
$padding="small"
|
||||||
|
aria-label={t('Doc visibility card')}
|
||||||
|
$direction="row"
|
||||||
|
$align="center"
|
||||||
|
$justify="space-between"
|
||||||
|
>
|
||||||
|
<Box $direction="row" $gap="1rem">
|
||||||
|
<IconBG iconName="public" $margin="none" />
|
||||||
|
<Switch
|
||||||
|
label={t(docPublic ? 'Doc public' : 'Doc private')}
|
||||||
|
defaultChecked={docPublic}
|
||||||
|
onChange={() => {
|
||||||
|
api.mutate({
|
||||||
|
id: doc.id,
|
||||||
|
is_public: !docPublic,
|
||||||
|
});
|
||||||
|
setDocPublic(!docPublic);
|
||||||
|
}}
|
||||||
|
text={t(
|
||||||
|
docPublic
|
||||||
|
? 'Anyone on the internet with the link can view'
|
||||||
|
: 'Only for people with access',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Modal,
|
Modal,
|
||||||
ModalSize,
|
ModalSize,
|
||||||
Switch,
|
|
||||||
VariantType,
|
VariantType,
|
||||||
useToastProvider,
|
useToastProvider,
|
||||||
} from '@openfun/cunningham-react';
|
} from '@openfun/cunningham-react';
|
||||||
@@ -44,9 +43,8 @@ export const ModalCreateDoc = ({ onClose }: ModalCreateDocProps) => {
|
|||||||
onClose,
|
onClose,
|
||||||
isPublic: false,
|
isPublic: false,
|
||||||
titleModal: t('Create a new document'),
|
titleModal: t('Create a new document'),
|
||||||
validate: (title, is_public) =>
|
validate: (title) =>
|
||||||
api.mutate({
|
api.mutate({
|
||||||
is_public,
|
|
||||||
title,
|
title,
|
||||||
}),
|
}),
|
||||||
...api,
|
...api,
|
||||||
@@ -85,9 +83,8 @@ export const ModalUpdateDoc = ({ onClose, doc }: ModalUpdateDocProps) => {
|
|||||||
titleModal: t('Update document "{{documentTitle}}"', {
|
titleModal: t('Update document "{{documentTitle}}"', {
|
||||||
documentTitle: doc.title,
|
documentTitle: doc.title,
|
||||||
}),
|
}),
|
||||||
validate: (title, is_public) =>
|
validate: (title) =>
|
||||||
api.mutate({
|
api.mutate({
|
||||||
is_public,
|
|
||||||
title,
|
title,
|
||||||
id: doc.id,
|
id: doc.id,
|
||||||
}),
|
}),
|
||||||
@@ -99,10 +96,9 @@ export const ModalUpdateDoc = ({ onClose, doc }: ModalUpdateDocProps) => {
|
|||||||
|
|
||||||
type ModalDoc<T> = {
|
type ModalDoc<T> = {
|
||||||
buttonText: string;
|
buttonText: string;
|
||||||
isPublic: boolean;
|
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
titleModal: string;
|
titleModal: string;
|
||||||
validate: (title: string, is_public: boolean) => void;
|
validate: (title: string) => void;
|
||||||
initialTitle?: string;
|
initialTitle?: string;
|
||||||
infoText?: string;
|
infoText?: string;
|
||||||
} & UseMutationResult<Doc, APIError<unknown>, T, unknown>;
|
} & UseMutationResult<Doc, APIError<unknown>, T, unknown>;
|
||||||
@@ -111,7 +107,6 @@ const ModalDoc = <T,>({
|
|||||||
buttonText,
|
buttonText,
|
||||||
infoText,
|
infoText,
|
||||||
initialTitle,
|
initialTitle,
|
||||||
isPublic,
|
|
||||||
onClose,
|
onClose,
|
||||||
titleModal,
|
titleModal,
|
||||||
validate,
|
validate,
|
||||||
@@ -121,8 +116,6 @@ const ModalDoc = <T,>({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [title, setTitle] = useState(initialTitle || '');
|
const [title, setTitle] = useState(initialTitle || '');
|
||||||
|
|
||||||
const [docPublic, setDocPublic] = useState(isPublic);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen
|
isOpen
|
||||||
@@ -144,7 +137,7 @@ const ModalDoc = <T,>({
|
|||||||
aria-label={buttonText}
|
aria-label={buttonText}
|
||||||
color="primary"
|
color="primary"
|
||||||
fullWidth
|
fullWidth
|
||||||
onClick={() => validate(title, docPublic)}
|
onClick={() => validate(title)}
|
||||||
>
|
>
|
||||||
{buttonText}
|
{buttonText}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -177,12 +170,6 @@ const ModalDoc = <T,>({
|
|||||||
setDocName: setTitle,
|
setDocName: setTitle,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Switch
|
|
||||||
label={t('Is it public ?')}
|
|
||||||
labelSide="right"
|
|
||||||
defaultChecked={docPublic}
|
|
||||||
onChange={() => setDocPublic(!docPublic)}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { MemberList } from '@/features/docs/members/members-list';
|
|||||||
import { Doc } from '../types';
|
import { Doc } from '../types';
|
||||||
import { currentDocRole } from '../utils';
|
import { currentDocRole } from '../utils';
|
||||||
|
|
||||||
|
import { DocVisibility } from './DocVisibility';
|
||||||
|
|
||||||
const ModalShareStyle = createGlobalStyle`
|
const ModalShareStyle = createGlobalStyle`
|
||||||
& .c__modal__scroller{
|
& .c__modal__scroller{
|
||||||
background: #FAFAFA;
|
background: #FAFAFA;
|
||||||
@@ -66,6 +68,7 @@ export const ModalShare = ({ onClose, doc }: ModalShareProps) => {
|
|||||||
</Card>
|
</Card>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
<DocVisibility doc={doc} />
|
||||||
<AddMembers doc={doc} currentRole={currentDocRole(doc.abilities)} />
|
<AddMembers doc={doc} currentRole={currentDocRole(doc.abilities)} />
|
||||||
<InvitationList doc={doc} />
|
<InvitationList doc={doc} />
|
||||||
<MemberList doc={doc} />
|
<MemberList doc={doc} />
|
||||||
|
|||||||
Reference in New Issue
Block a user