🚚(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:
Anthony LC
2024-09-06 18:03:19 +02:00
committed by Anthony LC
parent 459cb5e2e2
commit 4321511631
14 changed files with 184 additions and 88 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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');
}); });
}); });

View File

@@ -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 }) => {

View File

@@ -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();
});
}); });

View File

@@ -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();
});
});

View File

@@ -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',

View File

@@ -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
*/ */

View File

@@ -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);

View File

@@ -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' },

View File

@@ -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,
}), }),
}); });

View File

@@ -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>
);
};

View File

@@ -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>

View File

@@ -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} />