🔥(frontend) remove useless update title codes
We can now update the title directly in the header, so we don't need the update title modal anymore. We remove the buttons to trigger the modal and the modal itself.
This commit is contained in:
@@ -212,26 +212,6 @@ test.describe('Documents Grid', () => {
|
||||
).toHaveText(/.*/);
|
||||
});
|
||||
|
||||
test('it updates document', async ({ page }) => {
|
||||
const datagrid = page
|
||||
.getByLabel('Datagrid of the documents page 1')
|
||||
.getByRole('table');
|
||||
|
||||
const docRow = datagrid.getByRole('row').nth(1).getByRole('cell');
|
||||
|
||||
const docName = await docRow.nth(1).textContent();
|
||||
|
||||
await docRow.getByLabel('Open the document options').click();
|
||||
|
||||
await page.getByText('Update document').click();
|
||||
|
||||
await page.getByLabel('Document name').fill(`${docName} updated`);
|
||||
|
||||
await page.getByText('Validate the modification').click();
|
||||
|
||||
await expect(datagrid.getByText(`${docName} updated`)).toBeVisible();
|
||||
});
|
||||
|
||||
test('it deletes the document', async ({ page }) => {
|
||||
const datagrid = page
|
||||
.getByLabel('Datagrid of the documents page 1')
|
||||
@@ -241,11 +221,9 @@ test.describe('Documents Grid', () => {
|
||||
|
||||
const docName = await docRow.nth(1).textContent();
|
||||
|
||||
await docRow.getByLabel('Open the document options').click();
|
||||
|
||||
await page
|
||||
await docRow
|
||||
.getByRole('button', {
|
||||
name: 'Delete document',
|
||||
name: 'Delete the document',
|
||||
})
|
||||
.click();
|
||||
|
||||
|
||||
@@ -65,49 +65,15 @@ test.describe('Doc Header', () => {
|
||||
await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('it updates the doc', async ({ page, browserName }) => {
|
||||
test('it updates the title doc', async ({ page, browserName }) => {
|
||||
const [randomDoc] = await createDoc(page, 'doc-update', browserName, 1);
|
||||
await expect(page.locator('h2').getByText(randomDoc)).toBeVisible();
|
||||
|
||||
await page.getByLabel('Open the document options').click();
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Update document',
|
||||
})
|
||||
.click();
|
||||
await page.getByRole('heading', { name: randomDoc }).fill(' ');
|
||||
await page.getByText('Created at ').click();
|
||||
|
||||
await expect(
|
||||
page.locator('h2').getByText(`Update document "${randomDoc}"`),
|
||||
page.getByRole('heading', { name: 'Untitled document' }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByText('Document name').fill(`${randomDoc}-updated`);
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Validate the modification',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.getByText('The document has been updated.'),
|
||||
).toBeVisible();
|
||||
|
||||
const docTitle = await goToGridDoc(page, {
|
||||
title: `${randomDoc}-updated`,
|
||||
});
|
||||
|
||||
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
|
||||
|
||||
await page.getByLabel('Open the document options').click();
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Update document',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('textbox', { name: 'Document name' }),
|
||||
).toHaveValue(`${randomDoc}-updated`);
|
||||
});
|
||||
|
||||
test('it deletes the doc', async ({ page, browserName }) => {
|
||||
@@ -167,16 +133,15 @@ test.describe('Doc Header', () => {
|
||||
|
||||
await goToGridDoc(page);
|
||||
|
||||
await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
|
||||
await expect(
|
||||
page.locator('h2').getByText('Mocked document'),
|
||||
).toHaveAttribute('contenteditable');
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
|
||||
|
||||
await page.getByLabel('Open the document options').click();
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Export' })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Update document' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Delete document' }),
|
||||
).toBeHidden();
|
||||
@@ -199,16 +164,15 @@ test.describe('Doc Header', () => {
|
||||
|
||||
await goToGridDoc(page);
|
||||
|
||||
await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
|
||||
await expect(
|
||||
page.locator('h2').getByText('Mocked document'),
|
||||
).toHaveAttribute('contenteditable');
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Share' })).toBeHidden();
|
||||
|
||||
await page.getByLabel('Open the document options').click();
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Export' })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Update document' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Delete document' }),
|
||||
).toBeHidden();
|
||||
@@ -231,7 +195,9 @@ test.describe('Doc Header', () => {
|
||||
|
||||
await goToGridDoc(page);
|
||||
|
||||
await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
|
||||
await expect(
|
||||
page.locator('h2').getByText('Mocked document'),
|
||||
).not.toHaveAttribute('contenteditable');
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Share' })).toBeHidden();
|
||||
|
||||
@@ -239,9 +205,6 @@ test.describe('Doc Header', () => {
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Share' })).toBeHidden();
|
||||
await expect(page.getByRole('button', { name: 'Export' })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Update document' }),
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Delete document' }),
|
||||
).toBeHidden();
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
Doc,
|
||||
ModalRemoveDoc,
|
||||
ModalShare,
|
||||
ModalUpdateDoc,
|
||||
} from '@/features/docs/doc-management';
|
||||
|
||||
import { ModalPDF } from './ModalExport';
|
||||
@@ -20,7 +19,6 @@ interface DocToolBoxProps {
|
||||
export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [isModalShareOpen, setIsModalShareOpen] = useState(false);
|
||||
const [isModalUpdateOpen, setIsModalUpdateOpen] = useState(false);
|
||||
const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
|
||||
const [isModalPDFOpen, setIsModalPDFOpen] = useState(false);
|
||||
const [isDropOpen, setIsDropOpen] = useState(false);
|
||||
@@ -53,32 +51,6 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
isOpen={isDropOpen}
|
||||
>
|
||||
<Box>
|
||||
{doc.abilities.partial_update && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsModalUpdateOpen(true);
|
||||
setIsDropOpen(false);
|
||||
}}
|
||||
color="primary-text"
|
||||
icon={<span className="material-icons">edit</span>}
|
||||
size="small"
|
||||
>
|
||||
<Text $theme="primary">{t('Update document')}</Text>
|
||||
</Button>
|
||||
)}
|
||||
{doc.abilities.destroy && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsModalRemoveOpen(true);
|
||||
setIsDropOpen(false);
|
||||
}}
|
||||
color="primary-text"
|
||||
icon={<span className="material-icons">delete</span>}
|
||||
size="small"
|
||||
>
|
||||
<Text $theme="primary">{t('Delete document')}</Text>
|
||||
</Button>
|
||||
)}
|
||||
{doc.abilities.versions_list && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
@@ -116,6 +88,19 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
>
|
||||
<Text $theme="primary">{t('Export')}</Text>
|
||||
</Button>
|
||||
{doc.abilities.destroy && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsModalRemoveOpen(true);
|
||||
setIsDropOpen(false);
|
||||
}}
|
||||
color="primary-text"
|
||||
icon={<span className="material-icons">delete</span>}
|
||||
size="small"
|
||||
>
|
||||
<Text $theme="primary">{t('Delete document')}</Text>
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</DropButton>
|
||||
{isModalShareOpen && (
|
||||
@@ -124,9 +109,6 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
{isModalPDFOpen && (
|
||||
<ModalPDF onClose={() => setIsModalPDFOpen(false)} doc={doc} />
|
||||
)}
|
||||
{isModalUpdateOpen && (
|
||||
<ModalUpdateDoc onClose={() => setIsModalUpdateOpen(false)} doc={doc} />
|
||||
)}
|
||||
{isModalRemoveOpen && (
|
||||
<ModalRemoveDoc onClose={() => setIsModalRemoveOpen(false)} doc={doc} />
|
||||
)}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import { Input, Loader } from '@openfun/cunningham-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { APIError } from '@/api';
|
||||
import { Box, TextErrors } from '@/components';
|
||||
|
||||
interface InputDocNameProps {
|
||||
error: APIError | null;
|
||||
isError: boolean;
|
||||
isPending: boolean;
|
||||
label: string;
|
||||
setDocName: (newDocName: string) => void;
|
||||
defaultValue?: string;
|
||||
}
|
||||
|
||||
export const InputDocName = ({
|
||||
defaultValue,
|
||||
error,
|
||||
isError,
|
||||
isPending,
|
||||
label,
|
||||
setDocName,
|
||||
}: InputDocNameProps) => {
|
||||
const [isInputError, setIsInputError] = useState(isError);
|
||||
|
||||
useEffect(() => {
|
||||
if (isError) {
|
||||
setIsInputError(true);
|
||||
}
|
||||
}, [isError]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
fullWidth
|
||||
type="text"
|
||||
label={label}
|
||||
defaultValue={defaultValue}
|
||||
onChange={(e) => {
|
||||
setDocName(e.target.value);
|
||||
setIsInputError(false);
|
||||
}}
|
||||
rightIcon={<span className="material-icons">edit</span>}
|
||||
state={isInputError ? 'error' : 'default'}
|
||||
/>
|
||||
{isError && error && <TextErrors causes={error.cause} />}
|
||||
{isPending && (
|
||||
<Box $align="center">
|
||||
<Loader />
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,176 +0,0 @@
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Modal,
|
||||
ModalSize,
|
||||
VariantType,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
import { UseMutationResult } from '@tanstack/react-query';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { APIError } from '@/api';
|
||||
import { Box, Text } from '@/components';
|
||||
import useCunninghamTheme from '@/cunningham/useCunninghamTheme';
|
||||
|
||||
import { KEY_DOC, KEY_LIST_DOC } from '../api';
|
||||
import { useCreateDoc } from '../api/useCreateDoc';
|
||||
import { useUpdateDoc } from '../api/useUpdateDoc';
|
||||
import IconEdit from '../assets/icon-edit.svg';
|
||||
import { Doc } from '../types';
|
||||
|
||||
import { InputDocName } from './InputDocName';
|
||||
|
||||
interface ModalCreateDocProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const ModalCreateDoc = ({ onClose }: ModalCreateDocProps) => {
|
||||
const router = useRouter();
|
||||
const api = useCreateDoc({
|
||||
onSuccess: (doc) => {
|
||||
router.push(`/docs/${doc.id}`);
|
||||
},
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<ModalDoc
|
||||
{...{
|
||||
buttonText: t('Create the document'),
|
||||
onClose,
|
||||
isPublic: false,
|
||||
titleModal: t('Create a new document'),
|
||||
validate: (title) =>
|
||||
api.mutate({
|
||||
title,
|
||||
}),
|
||||
...api,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
interface ModalUpdateDocProps {
|
||||
onClose: () => void;
|
||||
doc: Doc;
|
||||
}
|
||||
|
||||
export const ModalUpdateDoc = ({ onClose, doc }: ModalUpdateDocProps) => {
|
||||
const { toast } = useToastProvider();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const api = useUpdateDoc({
|
||||
onSuccess: () => {
|
||||
toast(t('The document has been updated.'), VariantType.SUCCESS, {
|
||||
duration: 4000,
|
||||
});
|
||||
onClose();
|
||||
},
|
||||
listInvalideQueries: [KEY_DOC, KEY_LIST_DOC],
|
||||
});
|
||||
|
||||
return (
|
||||
<ModalDoc
|
||||
{...{
|
||||
buttonText: t('Validate the modification'),
|
||||
onClose,
|
||||
initialTitle: doc.title,
|
||||
infoText: t('Enter the new name of the selected document.'),
|
||||
titleModal: t('Update document "{{documentTitle}}"', {
|
||||
documentTitle: doc.title,
|
||||
}),
|
||||
validate: (title) =>
|
||||
api.mutate({
|
||||
title,
|
||||
id: doc.id,
|
||||
}),
|
||||
...api,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type ModalDoc<T> = {
|
||||
buttonText: string;
|
||||
onClose: () => void;
|
||||
titleModal: string;
|
||||
validate: (title: string) => void;
|
||||
initialTitle?: string;
|
||||
infoText?: string;
|
||||
} & UseMutationResult<Doc, APIError<unknown>, T, unknown>;
|
||||
|
||||
const ModalDoc = <T,>({
|
||||
buttonText,
|
||||
infoText,
|
||||
initialTitle,
|
||||
onClose,
|
||||
titleModal,
|
||||
validate,
|
||||
...api
|
||||
}: ModalDoc<T>) => {
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
const { t } = useTranslation();
|
||||
const [title, setTitle] = useState(initialTitle || '');
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen
|
||||
closeOnClickOutside
|
||||
hideCloseButton
|
||||
leftActions={
|
||||
<Button
|
||||
aria-label={t('Close the modal')}
|
||||
color="secondary"
|
||||
fullWidth
|
||||
onClick={() => onClose()}
|
||||
>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
}
|
||||
onClose={() => onClose()}
|
||||
rightActions={
|
||||
<Button
|
||||
aria-label={buttonText}
|
||||
color="primary"
|
||||
fullWidth
|
||||
onClick={() => validate(title)}
|
||||
>
|
||||
{buttonText}
|
||||
</Button>
|
||||
}
|
||||
size={ModalSize.MEDIUM}
|
||||
title={
|
||||
<Box $align="center" $gap="1rem" $margin={{ bottom: '2.5rem' }}>
|
||||
<IconEdit width={48} color={colorsTokens()['primary-text']} />
|
||||
<Text as="h2" $size="h3" $margin="none">
|
||||
{titleModal}
|
||||
</Text>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<Box $margin={{ bottom: 'xl' }} $gap="1rem">
|
||||
{infoText && (
|
||||
<Alert canClose={false} type={VariantType.INFO}>
|
||||
<Text>{infoText}</Text>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Box $gap="1rem">
|
||||
<InputDocName
|
||||
label={t('Document name')}
|
||||
defaultValue={title}
|
||||
{...{
|
||||
error: api.error,
|
||||
isError: api.isError,
|
||||
isPending: api.isPending,
|
||||
setDocName: setTitle,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from './ModalCreateUpdateDoc';
|
||||
export * from './ModalRemoveDoc';
|
||||
export * from './ModalShare';
|
||||
|
||||
@@ -2,12 +2,7 @@ import { Button } from '@openfun/cunningham-react';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, DropButton, IconOptions, Text } from '@/components';
|
||||
import {
|
||||
Doc,
|
||||
ModalRemoveDoc,
|
||||
ModalUpdateDoc,
|
||||
} from '@/features/docs/doc-management';
|
||||
import { Doc, ModalRemoveDoc } from '@/features/docs/doc-management';
|
||||
|
||||
interface DocsGridActionsProps {
|
||||
doc: Doc;
|
||||
@@ -15,58 +10,24 @@ interface DocsGridActionsProps {
|
||||
|
||||
export const DocsGridActions = ({ doc }: DocsGridActionsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [isModalUpdateOpen, setIsModalUpdateOpen] = useState(false);
|
||||
const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
|
||||
const [isDropOpen, setIsDropOpen] = useState(false);
|
||||
|
||||
if (!doc.abilities.partial_update && !doc.abilities.destroy) {
|
||||
if (!doc.abilities.destroy) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropButton
|
||||
button={
|
||||
<IconOptions
|
||||
isOpen={isDropOpen}
|
||||
aria-label={t('Open the document options')}
|
||||
/>
|
||||
}
|
||||
onOpenChange={(isOpen) => setIsDropOpen(isOpen)}
|
||||
isOpen={isDropOpen}
|
||||
>
|
||||
<Box>
|
||||
{doc.abilities.partial_update && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsModalUpdateOpen(true);
|
||||
setIsDropOpen(false);
|
||||
}}
|
||||
color="primary-text"
|
||||
icon={<span className="material-icons">edit</span>}
|
||||
size="small"
|
||||
>
|
||||
<Text $theme="primary">{t('Update document')}</Text>
|
||||
</Button>
|
||||
)}
|
||||
{doc.abilities.destroy && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsModalRemoveOpen(true);
|
||||
setIsDropOpen(false);
|
||||
}}
|
||||
color="primary-text"
|
||||
icon={<span className="material-icons">delete</span>}
|
||||
size="small"
|
||||
>
|
||||
<Text $theme="primary">{t('Delete document')}</Text>
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</DropButton>
|
||||
{isModalUpdateOpen && (
|
||||
<ModalUpdateDoc onClose={() => setIsModalUpdateOpen(false)} doc={doc} />
|
||||
)}
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsModalRemoveOpen(true);
|
||||
}}
|
||||
color="tertiary-text"
|
||||
icon={<span className="material-icons">delete</span>}
|
||||
size="small"
|
||||
style={{ padding: '0rem' }}
|
||||
aria-label={t('Delete the document')}
|
||||
/>
|
||||
{isModalRemoveOpen && (
|
||||
<ModalRemoveDoc onClose={() => setIsModalRemoveOpen(false)} doc={doc} />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user