diff --git a/CHANGELOG.md b/CHANGELOG.md index 23a0631b..858f4b40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to ### Added - ✨(frontend) create skeleton component for DocEditor #1491 +- ✨(frontend) add an EmojiPicker in the document tree and title #1381 ### Changed @@ -99,6 +100,9 @@ and this project adheres to ### Added - ✨(api) add API route to fetch document content #1206 +- ✨(frontend) doc emojis improvements #1381 + - add an EmojiPicker in the document tree and document title + - remove emoji buttons in menus ### Changed @@ -112,6 +116,8 @@ and this project adheres to - ✨unify tab focus style for better visual consistency #1341 - ♿hide decorative icons, label menus, avoid accessible name… #1362 - ♻️(tilt) use helm dev-backend chart +- 🩹(frontend) on main pages do not display leading emoji as page icon #1381 +- 🩹(frontend) handle properly emojis in interlinking #1381 ### Removed diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts index 2e5a187e..89f05286 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts @@ -65,16 +65,36 @@ test.describe('Doc Header', () => { page, browserName, }) => { - await createDoc(page, 'doc-update', browserName, 1); + await createDoc(page, 'doc-update-emoji', browserName, 1); + + const emojiPicker = page.locator('.--docs--doc-title').getByRole('button'); + + // Top parent should not have emoji picker + await expect(emojiPicker).toBeHidden(); + + const { name: docChild } = await createRootSubPage( + page, + browserName, + 'doc-update-emoji-child', + ); + + await verifyDocName(page, docChild); + + await expect(emojiPicker).toBeVisible(); + await emojiPicker.click({ + delay: 100, + }); + await page.getByRole('button', { name: '😀' }).first().click(); + await expect(emojiPicker).toHaveText('😀'); + const docTitle = page.getByRole('textbox', { name: 'Document title' }); - await expect(docTitle).toBeVisible(); - await docTitle.fill('👍 Hello Emoji World'); + await docTitle.fill('Hello Emoji World'); await docTitle.blur(); - await verifyDocName(page, '👍 Hello Emoji World'); + await verifyDocName(page, 'Hello Emoji World'); // Check the tree const row = await getTreeRow(page, 'Hello Emoji World'); - await expect(row.getByText('👍')).toBeVisible(); + await expect(row.getByText('😀')).toBeVisible(); }); test('it deletes the doc', async ({ page, browserName }) => { diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-tree.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-tree.spec.ts index 8b9ce931..422231f7 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-tree.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-tree.spec.ts @@ -340,9 +340,11 @@ test.describe('Doc Tree', () => { // Verify the emoji is updated in the tree and in the document title await expect(row.getByText('😀')).toBeVisible(); - await expect( - page.getByRole('textbox', { name: 'Document title' }), - ).toContainText('😀'); + + const titleEmojiPicker = page + .locator('.--docs--doc-title') + .getByRole('button'); + await expect(titleEmojiPicker).toHaveText('😀'); // Now remove the emoji using the new action await row.hover(); @@ -350,9 +352,7 @@ test.describe('Doc Tree', () => { await page.getByRole('menuitem', { name: 'Remove emoji' }).click(); await expect(row.getByText('😀')).toBeHidden(); - await expect( - page.getByRole('textbox', { name: 'Document title' }), - ).not.toContainText('😀'); + await expect(titleEmojiPicker).not.toHaveText('😀'); }); }); diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/EmojiPicker.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/EmojiPicker.tsx index 7469b2a0..de4a5c90 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/EmojiPicker.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/EmojiPicker.tsx @@ -19,7 +19,7 @@ export const EmojiPicker = ({ const { i18n } = useTranslation(); return ( - + { ); }; +const DocTitleEmojiPicker = ({ doc }: DocTitleProps) => { + const { t } = useTranslation(); + const { colorsTokens } = useCunninghamTheme(); + const { emoji } = getEmojiAndTitle(doc.title ?? ''); + + return ( + + + + + ); +}; + const DocTitleInput = ({ doc }: DocTitleProps) => { const { isDesktop } = useResponsiveStore(); const { t } = useTranslation(); const { colorsTokens } = useCunninghamTheme(); - const [titleDisplay, setTitleDisplay] = useState(doc.title); - + const { spacingsTokens } = useCunninghamTheme(); + const { isTopRoot } = useDocUtils(doc); const { untitledDocument } = useTrans(); + const { emoji, titleWithoutEmoji } = getEmojiAndTitle(doc.title ?? ''); + const [titleDisplay, setTitleDisplay] = useState( + isTopRoot ? doc.title : titleWithoutEmoji, + ); const { updateDocTitle } = useDocTitleUpdate(); const handleTitleSubmit = useCallback( (inputText: string) => { - const sanitizedTitle = updateDocTitle(doc, inputText.trim()); - setTitleDisplay(sanitizedTitle); + if (isTopRoot) { + const sanitizedTitle = updateDocTitle(doc, inputText); + setTitleDisplay(sanitizedTitle); + } else { + const sanitizedTitle = updateDocTitle( + doc, + emoji ? `${emoji} ${inputText}` : inputText, + ); + const { titleWithoutEmoji: sanitizedTitleWithoutEmoji } = + getEmojiAndTitle(sanitizedTitle); + + setTitleDisplay(sanitizedTitleWithoutEmoji); + } }, - [doc, updateDocTitle], + [updateDocTitle, doc, emoji, isTopRoot], ); const handleKeyDown = (e: React.KeyboardEvent) => { @@ -72,43 +131,62 @@ const DocTitleInput = ({ doc }: DocTitleProps) => { }; useEffect(() => { - setTitleDisplay(doc.title); - }, [doc]); + setTitleDisplay(isTopRoot ? doc.title : titleWithoutEmoji); + }, [doc.title, isTopRoot, titleWithoutEmoji]); return ( - - - handleTitleSubmit(event.target.textContent || '') - } - $color={colorsTokens['greyscale-1000']} - $minHeight="40px" - $padding={{ right: 'big' }} - $css={css` - &[contenteditable='true']:empty:not(:focus):before { - content: '${untitledDocument}'; - color: grey; - pointer-events: none; - font-style: italic; + + {isTopRoot && ( + + + ); }; diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx index 95a42441..bc9708d3 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx @@ -20,9 +20,11 @@ import { KEY_DOC, KEY_LIST_DOC, ModalRemoveDoc, + getEmojiAndTitle, useCopyDocLink, useCreateFavoriteDoc, useDeleteFavoriteDoc, + useDocTitleUpdate, useDocUtils, useDuplicateDoc, } from '@/docs/doc-management'; @@ -49,7 +51,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => { const treeContext = useTreeContext(); const queryClient = useQueryClient(); const router = useRouter(); - const { isChild } = useDocUtils(doc); + const { isChild, isTopRoot } = useDocUtils(doc); const { spacingsTokens, colorsTokens } = useCunninghamTheme(); @@ -83,6 +85,10 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => { }); }, [selectHistoryModal.isOpen, queryClient]); + // Emoji Management + const { emoji } = getEmojiAndTitle(doc.title ?? ''); + const { updateDocEmoji } = useDocTitleUpdate(); + const options: DropdownMenuOption[] = [ ...(isSmallMobile ? [ @@ -118,6 +124,17 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => { }, testId: `docs-actions-${doc.is_favorite ? 'unpin' : 'pin'}-${doc.id}`, }, + ...(emoji && doc.abilities.partial_update && !isTopRoot + ? [ + { + label: t('Remove emoji'), + icon: 'emoji_emotions', + callback: () => { + updateDocEmoji(doc.id, doc.title ?? '', ''); + }, + }, + ] + : []), { label: t('Version history'), icon: 'history', diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/assets/simple-document.svg b/src/frontend/apps/impress/src/features/docs/doc-management/assets/simple-document.svg index ee656f0d..bddcff8a 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/assets/simple-document.svg +++ b/src/frontend/apps/impress/src/features/docs/doc-management/assets/simple-document.svg @@ -1,6 +1,4 @@ ) : (