(frontend) edit title inline

We can now edit the title of the document inline.
This is a feature that is very useful for users
who want to change the title of the document
without having to go to the document
management page.
This commit is contained in:
Anthony LC
2024-10-01 15:55:19 +02:00
committed by Anthony LC
parent 61593bd807
commit 90027d3a5a
4 changed files with 156 additions and 27 deletions

View File

@@ -14,6 +14,7 @@ and this project adheres to
- ✨(ci) add security scan #291
- ✨(frontend) Activate versions feature #240
- ✨(frontend) one-click document creation #275
- ✨(frontend) edit title inline #275
## Changed

View File

@@ -29,32 +29,21 @@ export const createDoc = async (
length: number,
isPublic: boolean = false,
) => {
const buttonCreate = page.getByRole('button', {
name: 'Create the document',
});
const randomDocs = randomName(docName, browserName, length);
for (let i = 0; i < randomDocs.length; i++) {
const header = page.locator('header').first();
await header.locator('h2').getByText('Docs').click();
const buttonCreateHomepage = page.getByRole('button', {
name: 'Create a new document',
});
await buttonCreateHomepage.click();
// Fill input
await page
.getByRole('textbox', {
name: 'Document name',
.getByRole('button', {
name: 'Create a new document',
})
.fill(randomDocs[i]);
.click();
await expect(buttonCreate).toBeEnabled();
await buttonCreate.click();
await expect(page.locator('h2').getByText(randomDocs[i])).toBeVisible();
await page.getByRole('heading', { name: 'Untitled document' }).click();
await page.keyboard.type(randomDocs[i]);
await page.getByText('Created at ').click();
if (isPublic) {
await page.getByRole('button', { name: 'Share' }).click();

View File

@@ -8,12 +8,13 @@ import {
Doc,
Role,
currentDocRole,
useTransRole,
useTrans,
} from '@/features/docs/doc-management';
import { ModalVersion, Versions } from '@/features/docs/doc-versioning';
import { useDate } from '@/hook';
import { DocTagPublic } from './DocTagPublic';
import { DocTitle } from './DocTitle';
import { DocToolBox } from './DocToolBox';
interface DocHeaderProps {
@@ -25,7 +26,7 @@ export const DocHeader = ({ doc, versionId }: DocHeaderProps) => {
const { colorsTokens } = useCunninghamTheme();
const { t } = useTranslation();
const { formatDate } = useDate();
const transRole = useTransRole();
const { transRole } = useTrans();
const [isModalVersionOpen, setIsModalVersionOpen] = useState(false);
return (
@@ -54,14 +55,8 @@ export const DocHeader = ({ doc, versionId }: DocHeaderProps) => {
$background={colorsTokens()['greyscale-100']}
$margin={{ horizontal: 'small' }}
/>
<Box $gap="1rem" $direction="row">
<Text
as="h2"
$align="center"
$margin={{ all: 'none', left: 'tiny' }}
>
{doc.title}
</Text>
<Box $gap="1rem" $direction="row" $align="center">
<DocTitle doc={doc} />
{versionId && (
<Button
onClick={() => {

View File

@@ -0,0 +1,144 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
import {
Tooltip,
VariantType,
useToastProvider,
} from '@openfun/cunningham-react';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { createGlobalStyle } from 'styled-components';
import { Box, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import {
Doc,
KEY_DOC,
KEY_LIST_DOC,
useTrans,
useUpdateDoc,
} from '@/features/docs/doc-management';
import { isFirefox } from '@/utils/userAgent';
const DocTitleStyle = createGlobalStyle`
.c__tooltip {
padding: 4px 6px;
}
`;
interface DocTitleProps {
doc: Doc;
}
export const DocTitle = ({ doc }: DocTitleProps) => {
if (!doc.abilities.partial_update) {
return (
<Text as="h2" $align="center" $margin={{ all: 'none', left: 'tiny' }}>
{doc.title}
</Text>
);
}
return <DocTitleInput doc={doc} />;
};
const DocTitleInput = ({ doc }: DocTitleProps) => {
const { t } = useTranslation();
const { colorsTokens } = useCunninghamTheme();
const [titleDisplay, setTitleDisplay] = useState(doc.title);
const { toast } = useToastProvider();
const { untitledDocument } = useTrans();
const isUntitled = titleDisplay === untitledDocument;
const { mutate: updateDoc } = useUpdateDoc({
listInvalideQueries: [KEY_DOC, KEY_LIST_DOC],
});
const handleTitleSubmit = (inputText: string) => {
let sanitizedTitle = inputText.trim();
sanitizedTitle = sanitizedTitle.replace(/(\r\n|\n|\r)/gm, '');
// When blank we set to untitled
if (!sanitizedTitle) {
sanitizedTitle = untitledDocument;
setTitleDisplay(sanitizedTitle);
}
// If mutation we update
if (sanitizedTitle !== doc.title) {
updateDoc(
{ id: doc.id, title: sanitizedTitle },
{
onSuccess: () => {
if (sanitizedTitle !== untitledDocument) {
toast(
t('Document title updated successfully'),
VariantType.SUCCESS,
);
}
},
},
);
}
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
handleTitleSubmit(e.currentTarget.textContent || '');
}
};
const handleOnClick = () => {
if (isUntitled) {
setTitleDisplay('');
}
};
return (
<>
<DocTitleStyle />
<Tooltip content={t('Rename')} placement="top">
<Box
as="h2"
$radius="4px"
$padding={{ horizontal: 'tiny', vertical: '4px' }}
$align="center"
$margin="none"
contentEditable={isFirefox() ? 'true' : 'plaintext-only'}
onClick={handleOnClick}
onBlurCapture={(e) =>
handleTitleSubmit(e.currentTarget.textContent || '')
}
onKeyDownCapture={handleKeyDown}
suppressContentEditableWarning={true}
$color={
isUntitled
? colorsTokens()['greyscale-200']
: colorsTokens()['greyscale-text']
}
$css={`
${isUntitled && 'font-style: italic;'}
cursor: text;
font-size: 1.5rem;
transition: box-shadow 0.5s, border-color 0.5s;
border: 1px dashed transparent;
&:hover {
border-color: rgba(0, 123, 255, 0.25);
border-style: dashed;
}
&:focus {
outline: none;
border-color: transparent;
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}
`}
>
{titleDisplay}
</Box>
</Tooltip>
</>
);
};