🩹(frontend) handle properly emojis in interlinking

Emoji in interlinking were not replacing
the default icon when present.
This commit is contained in:
Olivier Laurendeau
2025-09-15 15:54:17 +02:00
committed by Anthony LC
parent 192fa76b54
commit b1d033edc9
4 changed files with 106 additions and 58 deletions

View File

@@ -13,7 +13,11 @@ import {
} from './utils-common'; } from './utils-common';
import { getEditor, openSuggestionMenu, writeInEditor } from './utils-editor'; import { getEditor, openSuggestionMenu, writeInEditor } from './utils-editor';
import { connectOtherUserToDoc, updateShareLink } from './utils-share'; import { connectOtherUserToDoc, updateShareLink } from './utils-share';
import { createRootSubPage, navigateToPageFromTree } from './utils-sub-pages'; import {
createRootSubPage,
getTreeRow,
navigateToPageFromTree,
} from './utils-sub-pages';
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
await page.goto('/'); await page.goto('/');
@@ -728,7 +732,13 @@ test.describe('Doc Editor', () => {
await verifyDocName(page, docChild2); await verifyDocName(page, docChild2);
await page.locator('.bn-block-outer').last().fill('/'); const treeRow = await getTreeRow(page, docChild2);
await treeRow.locator('.--docs--doc-icon').click();
await page.getByRole('button', { name: '😀' }).first().click();
await navigateToPageFromTree({ page, title: docChild1 });
await openSuggestionMenu({ page });
await page.getByText('Link a doc').first().click(); await page.getByText('Link a doc').first().click();
const input = page.locator( const input = page.locator(
@@ -742,6 +752,16 @@ test.describe('Doc Editor', () => {
await expect(searchContainer.getByText(docChild1)).toBeVisible(); await expect(searchContainer.getByText(docChild1)).toBeVisible();
await expect(searchContainer.getByText(docChild2)).toBeVisible(); await expect(searchContainer.getByText(docChild2)).toBeVisible();
const searchContainerRow = searchContainer
.getByRole('option')
.filter({
hasText: docChild2,
})
.first();
await expect(searchContainerRow).toContainText('😀');
await expect(searchContainerRow.locator('svg').first()).toBeHidden();
await input.pressSequentially('-child'); await input.pressSequentially('-child');
await expect(searchContainer.getByText(docChild1)).toBeVisible(); await expect(searchContainer.getByText(docChild1)).toBeVisible();
@@ -756,32 +776,30 @@ test.describe('Doc Editor', () => {
await expect(searchContainer).toBeHidden(); await expect(searchContainer).toBeHidden();
// Wait for the interlink to be created and rendered // Wait for the interlink to be created and rendered
const editor = page.locator('.ProseMirror.bn-editor'); const editor = await getEditor({ page });
const interlink = editor.getByRole('button', { const interlinkChild2 = editor.getByRole('button', {
name: docChild2, name: docChild2,
}); });
await expect(interlink).toBeVisible({ timeout: 10000 }); await expect(interlinkChild2).toBeVisible({ timeout: 10000 });
await interlink.click(); await expect(interlinkChild2).toContainText('😀');
await expect(interlinkChild2.locator('svg').first()).toBeHidden();
await interlinkChild2.click();
await verifyDocName(page, docChild2); await verifyDocName(page, docChild2);
});
test('it checks interlink shortcut @', async ({ page, browserName }) => {
const [randomDoc] = await createDoc(page, 'doc-interlink', browserName, 1);
await verifyDocName(page, randomDoc);
const editor = page.locator('.bn-block-outer').last();
await editor.click(); await editor.click();
await page.keyboard.press('@');
await expect( await page.keyboard.press('@');
page.locator( await input.fill(docChild1);
"span[data-inline-content-type='interlinkingSearchInline'] input", await searchContainer.getByText(docChild1).click();
),
).toBeVisible(); const interlinkChild1 = editor.getByRole('button', {
name: docChild1,
});
await expect(interlinkChild1).toBeVisible({ timeout: 10000 });
await expect(interlinkChild1.locator('svg').first()).toBeVisible();
}); });
test('it checks multiple big doc scroll to the top', async ({ test('it checks multiple big doc scroll to the top', async ({

View File

@@ -1,10 +1,4 @@
<svg <svg viewBox="0 0 18 23" fill="none" xmlns="http://www.w3.org/2000/svg">
width="18"
height="23"
viewBox="0 0 18 23"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path <path
fill-rule="evenodd" fill-rule="evenodd"
clip-rule="evenodd" clip-rule="evenodd"

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -7,7 +7,7 @@ import { css } from 'styled-components';
import { BoxButton, Text } from '@/components'; import { BoxButton, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham'; import { useCunninghamTheme } from '@/cunningham';
import SelectedPageIcon from '@/docs/doc-editor/assets/doc-selected.svg'; import SelectedPageIcon from '@/docs/doc-editor/assets/doc-selected.svg';
import { useDoc } from '@/docs/doc-management'; import { getEmojiAndTitle, useDoc } from '@/docs/doc-management';
export const InterlinkingLinkInlineContent = createReactInlineContentSpec( export const InterlinkingLinkInlineContent = createReactInlineContentSpec(
{ {
@@ -52,6 +52,8 @@ interface LinkSelectedProps {
} }
const LinkSelected = ({ url, title }: LinkSelectedProps) => { const LinkSelected = ({ url, title }: LinkSelectedProps) => {
const { colorsTokens } = useCunninghamTheme(); const { colorsTokens } = useCunninghamTheme();
const { emoji, titleWithoutEmoji } = getEmojiAndTitle(title);
const router = useRouter(); const router = useRouter();
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => { const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
@@ -78,9 +80,21 @@ const LinkSelected = ({ url, title }: LinkSelectedProps) => {
transition: background-color 0.2s ease-in-out; transition: background-color 0.2s ease-in-out;
`} `}
> >
<SelectedPageIcon width={11.5} color={colorsTokens['primary-400']} /> {emoji ? (
<Text $weight="500" spellCheck="false" $size="16px" $display="inline"> <Text $size="16px">{emoji}</Text>
{title} ) : (
<SelectedPageIcon width={11.5} color={colorsTokens['primary-400']} />
)}
<Text
$weight="500"
spellCheck="false"
$size="16px"
$display="inline"
$css={css`
margin-left: 2px;
`}
>
{titleWithoutEmoji}
</Text> </Text>
</BoxButton> </BoxButton>
); );

View File

@@ -25,6 +25,7 @@ import {
import FoundPageIcon from '@/docs/doc-editor/assets/doc-found.svg'; import FoundPageIcon from '@/docs/doc-editor/assets/doc-found.svg';
import AddPageIcon from '@/docs/doc-editor/assets/doc-plus.svg'; import AddPageIcon from '@/docs/doc-editor/assets/doc-plus.svg';
import { import {
getEmojiAndTitle,
useCreateChildDocTree, useCreateChildDocTree,
useDocStore, useDocStore,
useTrans, useTrans,
@@ -236,35 +237,56 @@ export const SearchPage = ({
editor.focus(); editor.focus();
}} }}
renderElement={(doc) => ( renderElement={(doc) => {
<QuickSearchItemContent const { emoji, titleWithoutEmoji } = getEmojiAndTitle(
left={ doc.title || untitledDocument,
<Box );
$direction="row"
$gap="0.6rem" return (
$align="center" <QuickSearchItemContent
$padding={{ vertical: '0.5rem', horizontal: '0.2rem' }} left={
$width="100%" <Box
> $direction="row"
<FoundPageIcon style={{ flexShrink: 0 }} /> $gap="0.2rem"
<Text $align="center"
$size="14px" $padding={{ vertical: '0.5rem', horizontal: '0.2rem' }}
$color="var(--c--theme--colors--greyscale-1000)" $width="100%"
spellCheck="false"
> >
{doc.title} <Box
</Text> $css={css`
</Box> width: 24px;
} flex-shrink: 0;
right={ `}
<Icon >
iconName="keyboard_return" {emoji ? (
$variation="600" <Text $size="18px">{emoji}</Text>
spellCheck="false" ) : (
/> <FoundPageIcon
} width="100%"
/> style={{ maxHeight: '24px' }}
)} />
)}
</Box>
<Text
$size="14px"
$color="var(--c--theme--colors--greyscale-1000)"
spellCheck="false"
>
{titleWithoutEmoji}
</Text>
</Box>
}
right={
<Icon
iconName="keyboard_return"
$variation="600"
spellCheck="false"
/>
}
/>
);
}}
/> />
<QuickSearchGroup <QuickSearchGroup
group={{ group={{