✨(frontend) add "Add Emoji" button to doc options menu
- Add "Add Emoji" button to doc options menu - Remove default emoji when none selected - Improve doc options styling
This commit is contained in:
@@ -68,9 +68,18 @@ test.describe('Doc Header', () => {
|
|||||||
await createDoc(page, 'doc-update-emoji', browserName, 1);
|
await createDoc(page, 'doc-update-emoji', browserName, 1);
|
||||||
|
|
||||||
const emojiPicker = page.locator('.--docs--doc-title').getByRole('button');
|
const emojiPicker = page.locator('.--docs--doc-title').getByRole('button');
|
||||||
|
const optionMenu = page.getByLabel('Open the document options');
|
||||||
|
const addEmojiMenuItem = page.getByRole('menuitem', { name: 'Add emoji' });
|
||||||
|
const removeEmojiMenuItem = page.getByRole('menuitem', {
|
||||||
|
name: 'Remove emoji',
|
||||||
|
});
|
||||||
|
|
||||||
// Top parent should not have emoji picker
|
// Top parent should not have emoji picker
|
||||||
await expect(emojiPicker).toBeHidden();
|
await expect(emojiPicker).toBeHidden();
|
||||||
|
await optionMenu.click();
|
||||||
|
await expect(addEmojiMenuItem).toBeHidden();
|
||||||
|
await expect(removeEmojiMenuItem).toBeHidden();
|
||||||
|
await page.keyboard.press('Escape');
|
||||||
|
|
||||||
const { name: docChild } = await createRootSubPage(
|
const { name: docChild } = await createRootSubPage(
|
||||||
page,
|
page,
|
||||||
@@ -80,13 +89,23 @@ test.describe('Doc Header', () => {
|
|||||||
|
|
||||||
await verifyDocName(page, docChild);
|
await verifyDocName(page, docChild);
|
||||||
|
|
||||||
await expect(emojiPicker).toBeVisible();
|
// Emoji picker should be hidden initially
|
||||||
|
await expect(emojiPicker).toBeHidden();
|
||||||
|
|
||||||
|
// Add emoji
|
||||||
|
await optionMenu.click();
|
||||||
|
await expect(removeEmojiMenuItem).toBeHidden();
|
||||||
|
await addEmojiMenuItem.click();
|
||||||
|
await expect(emojiPicker).toHaveText('📄');
|
||||||
|
|
||||||
|
// Change emoji
|
||||||
await emojiPicker.click({
|
await emojiPicker.click({
|
||||||
delay: 100,
|
delay: 100,
|
||||||
});
|
});
|
||||||
await page.getByRole('button', { name: '😀' }).first().click();
|
await page.getByRole('button', { name: '😀' }).first().click();
|
||||||
await expect(emojiPicker).toHaveText('😀');
|
await expect(emojiPicker).toHaveText('😀');
|
||||||
|
|
||||||
|
// Update title
|
||||||
const docTitle = page.getByRole('textbox', { name: 'Document title' });
|
const docTitle = page.getByRole('textbox', { name: 'Document title' });
|
||||||
await docTitle.fill('Hello Emoji World');
|
await docTitle.fill('Hello Emoji World');
|
||||||
await docTitle.blur();
|
await docTitle.blur();
|
||||||
@@ -95,6 +114,12 @@ test.describe('Doc Header', () => {
|
|||||||
// Check the tree
|
// Check the tree
|
||||||
const row = await getTreeRow(page, 'Hello Emoji World');
|
const row = await getTreeRow(page, 'Hello Emoji World');
|
||||||
await expect(row.getByText('😀')).toBeVisible();
|
await expect(row.getByText('😀')).toBeVisible();
|
||||||
|
|
||||||
|
// Remove emoji
|
||||||
|
await optionMenu.click();
|
||||||
|
await expect(addEmojiMenuItem).toBeHidden();
|
||||||
|
await removeEmojiMenuItem.click();
|
||||||
|
await expect(emojiPicker).toBeHidden();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it deletes the doc', async ({ page, browserName }) => {
|
test('it deletes the doc', async ({ page, browserName }) => {
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ test.describe('Doc Tree', () => {
|
|||||||
await page.getByRole('menuitem', { name: 'Remove emoji' }).click();
|
await page.getByRole('menuitem', { name: 'Remove emoji' }).click();
|
||||||
|
|
||||||
await expect(row.getByText('😀')).toBeHidden();
|
await expect(row.getByText('😀')).toBeHidden();
|
||||||
await expect(titleEmojiPicker).not.toHaveText('😀');
|
await expect(titleEmojiPicker).toBeHidden();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,16 @@ const DocTitleEmojiPicker = ({ doc }: DocTitleProps) => {
|
|||||||
const { colorsTokens } = useCunninghamTheme();
|
const { colorsTokens } = useCunninghamTheme();
|
||||||
const { emoji } = getEmojiAndTitle(doc.title ?? '');
|
const { emoji } = getEmojiAndTitle(doc.title ?? '');
|
||||||
|
|
||||||
|
if (!emoji) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip content={t('Document emoji')} aria-hidden={true} placement="top">
|
<Tooltip
|
||||||
|
content={t('Edit document emoji')}
|
||||||
|
aria-hidden={true}
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
$css={css`
|
$css={css`
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
@@ -70,11 +78,17 @@ const DocTitleEmojiPicker = ({ doc }: DocTitleProps) => {
|
|||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<DocIcon
|
<DocIcon
|
||||||
|
buttonProps={{
|
||||||
|
$width: '32px',
|
||||||
|
$height: '32px',
|
||||||
|
$justify: 'space-between',
|
||||||
|
$align: 'center',
|
||||||
|
}}
|
||||||
withEmojiPicker={doc.abilities.partial_update}
|
withEmojiPicker={doc.abilities.partial_update}
|
||||||
docId={doc.id}
|
docId={doc.id}
|
||||||
title={doc.title}
|
title={doc.title}
|
||||||
emoji={emoji}
|
emoji={emoji}
|
||||||
$size="25px"
|
$size="23px"
|
||||||
defaultIcon={
|
defaultIcon={
|
||||||
<SimpleFileIcon
|
<SimpleFileIcon
|
||||||
width="25px"
|
width="25px"
|
||||||
@@ -94,7 +108,6 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
|
|||||||
const { isDesktop } = useResponsiveStore();
|
const { isDesktop } = useResponsiveStore();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { colorsTokens } = useCunninghamTheme();
|
const { colorsTokens } = useCunninghamTheme();
|
||||||
const { spacingsTokens } = useCunninghamTheme();
|
|
||||||
const { isTopRoot } = useDocUtils(doc);
|
const { isTopRoot } = useDocUtils(doc);
|
||||||
const { untitledDocument } = useTrans();
|
const { untitledDocument } = useTrans();
|
||||||
const { emoji, titleWithoutEmoji } = getEmojiAndTitle(doc.title ?? '');
|
const { emoji, titleWithoutEmoji } = getEmojiAndTitle(doc.title ?? '');
|
||||||
@@ -139,19 +152,9 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
|
|||||||
className="--docs--doc-title"
|
className="--docs--doc-title"
|
||||||
$direction="row"
|
$direction="row"
|
||||||
$align="center"
|
$align="center"
|
||||||
$gap={spacingsTokens['xs']}
|
$gap="4px"
|
||||||
$minHeight="40px"
|
$minHeight="40px"
|
||||||
>
|
>
|
||||||
{isTopRoot && (
|
|
||||||
<SimpleFileIcon
|
|
||||||
width="25px"
|
|
||||||
height="25px"
|
|
||||||
aria-hidden="true"
|
|
||||||
aria-label={t('Simple document icon')}
|
|
||||||
color={colorsTokens['primary-500']}
|
|
||||||
style={{ flexShrink: '0' }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!isTopRoot && <DocTitleEmojiPicker doc={doc} />}
|
{!isTopRoot && <DocTitleEmojiPicker doc={doc} />}
|
||||||
|
|
||||||
<Tooltip content={t('Rename')} aria-hidden={true} placement="top">
|
<Tooltip content={t('Rename')} aria-hidden={true} placement="top">
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||||||
const selectHistoryModal = useModal();
|
const selectHistoryModal = useModal();
|
||||||
const modalShare = useModal();
|
const modalShare = useModal();
|
||||||
|
|
||||||
const { isSmallMobile, isDesktop } = useResponsiveStore();
|
const { isSmallMobile, isMobile } = useResponsiveStore();
|
||||||
const copyDocLink = useCopyDocLink(doc.id);
|
const copyDocLink = useCopyDocLink(doc.id);
|
||||||
const { mutate: duplicateDoc } = useDuplicateDoc({
|
const { mutate: duplicateDoc } = useDuplicateDoc({
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
@@ -90,28 +90,20 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||||||
const { updateDocEmoji } = useDocTitleUpdate();
|
const { updateDocEmoji } = useDocTitleUpdate();
|
||||||
|
|
||||||
const options: DropdownMenuOption[] = [
|
const options: DropdownMenuOption[] = [
|
||||||
...(isSmallMobile
|
{
|
||||||
? [
|
label: t('Share'),
|
||||||
{
|
icon: 'group',
|
||||||
label: t('Share'),
|
callback: modalShare.open,
|
||||||
icon: 'group',
|
show: isSmallMobile,
|
||||||
callback: modalShare.open,
|
},
|
||||||
},
|
{
|
||||||
{
|
label: t('Export'),
|
||||||
label: t('Export'),
|
icon: 'download',
|
||||||
icon: 'download',
|
callback: () => {
|
||||||
callback: () => {
|
setIsModalExportOpen(true);
|
||||||
setIsModalExportOpen(true);
|
},
|
||||||
},
|
show: !!ModalExport && isSmallMobile,
|
||||||
show: !!ModalExport,
|
},
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('Copy link'),
|
|
||||||
icon: 'add_link',
|
|
||||||
callback: copyDocLink,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
{
|
{
|
||||||
label: doc.is_favorite ? t('Unpin') : t('Pin'),
|
label: doc.is_favorite ? t('Unpin') : t('Pin'),
|
||||||
icon: 'push_pin',
|
icon: 'push_pin',
|
||||||
@@ -124,17 +116,6 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||||||
},
|
},
|
||||||
testId: `docs-actions-${doc.is_favorite ? 'unpin' : 'pin'}-${doc.id}`,
|
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'),
|
label: t('Version history'),
|
||||||
icon: 'history',
|
icon: 'history',
|
||||||
@@ -142,7 +123,31 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||||||
callback: () => {
|
callback: () => {
|
||||||
selectHistoryModal.open();
|
selectHistoryModal.open();
|
||||||
},
|
},
|
||||||
show: isDesktop,
|
show: !isMobile,
|
||||||
|
showSeparator: isTopRoot ? true : false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('Remove emoji'),
|
||||||
|
icon: 'emoji_emotions',
|
||||||
|
callback: () => {
|
||||||
|
updateDocEmoji(doc.id, doc.title ?? '', '');
|
||||||
|
},
|
||||||
|
showSeparator: true,
|
||||||
|
show: !!emoji && doc.abilities.partial_update && !isTopRoot,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('Add emoji'),
|
||||||
|
icon: 'emoji_emotions',
|
||||||
|
callback: () => {
|
||||||
|
updateDocEmoji(doc.id, doc.title ?? '', '📄');
|
||||||
|
},
|
||||||
|
showSeparator: true,
|
||||||
|
show: !emoji && doc.abilities.partial_update && !isTopRoot,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('Copy link'),
|
||||||
|
icon: 'add_link',
|
||||||
|
callback: copyDocLink,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('Copy as {{format}}', { format: 'Markdown' }),
|
label: t('Copy as {{format}}', { format: 'Markdown' }),
|
||||||
@@ -158,6 +163,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||||||
void copyCurrentEditorToClipboard('html');
|
void copyCurrentEditorToClipboard('html');
|
||||||
},
|
},
|
||||||
show: isFeatureFlagActivated('CopyAsHTML'),
|
show: isFeatureFlagActivated('CopyAsHTML'),
|
||||||
|
showSeparator: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('Duplicate'),
|
label: t('Duplicate'),
|
||||||
@@ -170,6 +176,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||||||
canSave: doc.abilities.partial_update,
|
canSave: doc.abilities.partial_update,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
showSeparator: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: isChild ? t('Delete sub-document') : t('Delete document'),
|
label: isChild ? t('Delete sub-document') : t('Delete document'),
|
||||||
@@ -224,25 +231,22 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
|||||||
aria-label={t('Export the document')}
|
aria-label={t('Export the document')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<DropdownMenu options={options} label={t('Open the document options')}>
|
<DropdownMenu
|
||||||
<IconOptions
|
options={options}
|
||||||
aria-hidden="true"
|
label={t('Open the document options')}
|
||||||
isHorizontal
|
buttonCss={css`
|
||||||
$theme="primary"
|
padding: ${spacingsTokens['xs']};
|
||||||
$padding={{ all: 'xs' }}
|
&:hover {
|
||||||
$css={css`
|
background-color: ${colorsTokens['greyscale-100']};
|
||||||
border-radius: 4px;
|
}
|
||||||
&:hover {
|
${isSmallMobile
|
||||||
background-color: ${colorsTokens['greyscale-100']};
|
? css`
|
||||||
}
|
border: 1px solid ${colorsTokens['greyscale-300']};
|
||||||
${isSmallMobile
|
`
|
||||||
? css`
|
: ''}
|
||||||
padding: 10px;
|
`}
|
||||||
border: 1px solid ${colorsTokens['greyscale-300']};
|
>
|
||||||
`
|
<IconOptions aria-hidden="true" isHorizontal $theme="primary" />
|
||||||
: ''}
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user