(frontend) create page from slash menu

We are now able to create a new page from
the slash menu.
This commit is contained in:
Anthony LC
2025-04-23 17:40:27 +02:00
parent f12d30cffa
commit afa48b6675
7 changed files with 141 additions and 16 deletions

View File

@@ -29,6 +29,28 @@ test.describe('Doc Create', () => {
await expect(page.getByTestId('grid-loader')).toBeHidden();
await expect(docsGrid.getByText(docTitle)).toBeVisible();
});
test('it creates a sub doc from slash menu editor', async ({
page,
browserName,
}) => {
const [title] = await createDoc(page, 'my-new-slash-doc', browserName, 1);
await verifyDocName(page, title);
await page.locator('.bn-block-outer').last().fill('/');
await page
.getByText('New sub-doc', {
exact: true,
})
.click();
const input = page.getByRole('textbox', { name: 'doc title input' });
await expect(input).toHaveText('');
await expect(
page.locator('.c__tree-view--row-content').getByText('Untitled document'),
).toBeVisible();
});
});
test.describe('Doc Create: Not logged', () => {

View File

@@ -16,18 +16,11 @@ test.describe('Doc Routing', () => {
await page.goto('/');
});
test('Check the presence of the meta tag noindex', async ({ page }) => {
const buttonCreateHomepage = page.getByRole('button', {
name: 'New doc',
});
await expect(buttonCreateHomepage).toBeVisible();
await buttonCreateHomepage.click();
await expect(
page.getByRole('button', {
name: 'Share',
}),
).toBeVisible();
test('Check the presence of the meta tag noindex', async ({
page,
browserName,
}) => {
await createDoc(page, 'doc-routing-test', browserName, 1);
const metaDescription = page.locator('meta[name="robots"]');
await expect(metaDescription).toHaveAttribute('content', 'noindex');
});

View File

@@ -0,0 +1,30 @@
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M18.4661 8.93913C17.8524 8.93913 17.2752 8.82368 16.7344 8.59277C16.1997 8.35579 15.7257 8.03375 15.3125 7.62663C14.9054 7.21343 14.5833 6.73644 14.3464 6.19564C14.1155 5.65484 14 5.08062 14 4.47298C14 3.65267 14.2005 2.90527 14.6016 2.23079C15.0087 1.55632 15.5495 1.01855 16.224 0.617513C16.8984 0.210395 17.6458 0.00683594 18.4661 0.00683594C19.2804 0.00683594 20.0247 0.210395 20.6992 0.617513C21.3798 1.01855 21.9206 1.55632 22.3216 2.23079C22.7227 2.90527 22.9232 3.65267 22.9232 4.47298C22.9232 5.08062 22.8047 5.65484 22.5677 6.19564C22.3368 6.73036 22.0148 7.20432 21.6016 7.61751C21.1944 8.03071 20.7205 8.35579 20.1797 8.59277C19.6389 8.82368 19.0677 8.93913 18.4661 8.93913ZM18.4753 7.31673C18.6576 7.31673 18.8034 7.26204 18.9128 7.15267C19.0282 7.03722 19.0859 6.88835 19.0859 6.70606V5.08366H20.6992C20.8815 5.08366 21.0273 5.02897 21.1367 4.9196C21.2522 4.80415 21.3099 4.65527 21.3099 4.47298C21.3099 4.28461 21.2522 4.13574 21.1367 4.02637C21.0273 3.91699 20.8815 3.8623 20.6992 3.8623H19.0859V2.24902C19.0859 2.06673 19.0282 1.9209 18.9128 1.81152C18.8034 1.69607 18.6576 1.63835 18.4753 1.63835C18.2869 1.63835 18.135 1.69607 18.0195 1.81152C17.9102 1.9209 17.8555 2.06673 17.8555 2.24902V3.8623H16.2422C16.0599 3.8623 15.911 3.91699 15.7956 4.02637C15.6862 4.13574 15.6315 4.28461 15.6315 4.47298C15.6315 4.65527 15.6862 4.80415 15.7956 4.9196C15.911 5.02897 16.0599 5.08366 16.2422 5.08366H17.8555V6.70606C17.8555 6.88835 17.9102 7.03722 18.0195 7.15267C18.135 7.26204 18.2869 7.31673 18.4753 7.31673Z"
fill="#3A3A3A"
/>
<path
d="M20.7234 19.8862V9.53888C20.1803 9.77921 19.591 9.93419 18.9726 9.98682V19.8555C18.9726 20.3682 18.8358 20.7612 18.5624 21.0347C18.2958 21.3081 17.8959 21.4448 17.3627 21.4448H6.63711C6.10391 21.4448 5.70059 21.3081 5.42715 21.0347L5.49875 20.9649L5.49786 20.964L5.42715 21.0347C5.16055 20.7612 5.02725 20.3682 5.02725 19.8555V4.36181C5.02725 3.84911 5.16055 3.45605 5.42715 3.18261C5.70059 2.90234 6.10391 2.7622 6.63711 2.7622H13.2825C13.4981 2.11701 13.8301 1.52509 14.2535 1.01132H6.55508C5.47955 1.01132 4.65317 1.28555 4.09476 1.85112C3.5438 2.41639 3.27637 3.24944 3.27637 4.33105V19.8862C3.27637 20.9743 3.54365 21.8079 4.09521 22.3666C4.65367 22.9251 5.47988 23.1957 6.55508 23.1957H17.4447C18.5196 23.1957 19.3427 22.9252 19.8945 22.3664C20.4531 21.8078 20.7234 20.9746 20.7234 19.8862Z"
fill="#3A3A3A"
/>
<path
d="M13.6747 7.14833C13.4266 6.69614 13.2402 6.20528 13.1269 5.6871H8.11367C7.90437 5.6871 7.72456 5.75794 7.58154 5.90096C7.44495 6.03755 7.37793 6.21072 7.37793 6.41259C7.37793 6.61445 7.44486 6.78981 7.57954 6.93243L7.58154 6.93447C7.72456 7.0775 7.90437 7.14833 8.11367 7.14833H13.6747Z"
fill="#3A3A3A"
/>
<path
d="M16.5378 9.64649C16.2607 9.54063 15.9943 9.41301 15.7408 9.26572H8.11367C7.90437 9.26572 7.72456 9.33655 7.58154 9.47958C7.44495 9.61616 7.37793 9.78933 7.37793 9.99121C7.37793 10.1931 7.44486 10.3684 7.57954 10.511L7.58154 10.5131C7.72456 10.6561 7.90437 10.7269 8.11367 10.7269H15.8964C16.0996 10.7269 16.273 10.6555 16.409 10.5121C16.5506 10.37 16.6219 10.1944 16.6219 9.99121C16.6219 9.86401 16.5939 9.74847 16.5378 9.64649Z"
fill="#3A3A3A"
/>
<path
d="M7.57954 14.0999C7.44561 13.9581 7.37793 13.7865 7.37793 13.5903C7.37793 13.3827 7.44414 13.2036 7.57954 13.0602L7.58151 13.0582C7.72453 12.9151 7.90437 12.8443 8.11367 12.8443H11.7948C12.0036 12.8443 12.1809 12.9149 12.3177 13.0592C12.4601 13.2021 12.5306 13.3815 12.5306 13.5903C12.5306 13.7883 12.4581 13.9605 12.3167 14.102C12.1794 14.2393 12.0024 14.3056 11.7948 14.3056H8.11367C7.90609 14.3056 7.72695 14.2393 7.58358 14.1039L7.57954 14.0999Z"
fill="#3A3A3A"
/>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -9,28 +9,49 @@ import {
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { DocsBlockSchema } from '../types';
import {
DocsBlockSchema,
DocsInlineContentSchema,
DocsStyleSchema,
} from '../types';
import {
getCalloutReactSlashMenuItems,
getDividerReactSlashMenuItems,
} from './custom-blocks';
import { useGetInterlinkingMenuItems } from './custom-inline-content';
import XLMultiColumn from './xl-multi-column';
const getMultiColumnSlashMenuItems =
XLMultiColumn?.getMultiColumnSlashMenuItems;
export const BlockNoteSuggestionMenu = () => {
const editor = useBlockNoteEditor<DocsBlockSchema>();
const editor = useBlockNoteEditor<
DocsBlockSchema,
DocsInlineContentSchema,
DocsStyleSchema
>();
const { t } = useTranslation();
const basicBlocksName = useDictionary().slash_menu.page_break.group;
const getInterlinkingMenuItems = useGetInterlinkingMenuItems();
const getSlashMenuItems = useMemo(() => {
// We insert it after the "Code Block" item to have the interlinking block displayed after the basic blocks
const defaultMenu = getDefaultReactSlashMenuItems(editor);
const index = defaultMenu.findIndex(
(item) => item.aliases?.includes('code') && item.aliases?.includes('pre'),
);
const newSlashMenuItems = [
...defaultMenu.slice(0, index + 1),
...getInterlinkingMenuItems(t),
...defaultMenu.slice(index + 1),
];
return async (query: string) =>
Promise.resolve(
filterSuggestionItems(
combineByGroup(
getDefaultReactSlashMenuItems(editor),
newSlashMenuItems,
getCalloutReactSlashMenuItems(editor, t, basicBlocksName),
getMultiColumnSlashMenuItems?.(editor) || [],
getPageBreakReactSlashMenuItems(editor),
@@ -39,7 +60,7 @@ export const BlockNoteSuggestionMenu = () => {
query,
),
);
}, [basicBlocksName, editor, t]);
}, [basicBlocksName, editor, getInterlinkingMenuItems, t]);
return (
<SuggestionMenuController

View File

@@ -0,0 +1,57 @@
import { useTreeContext } from '@gouvfr-lasuite/ui-kit';
import { TFunction } from 'i18next';
import { useRouter } from 'next/navigation';
import { useCallback } from 'react';
import AddPageIcon from '@/docs/doc-editor/assets/doc-plus.svg';
import { Doc, useCreateChildDoc, useDocStore } from '@/docs/doc-management';
export const getInterlinkingMenuItems = (
t: TFunction<'translation', undefined>,
group: string,
createPage: () => void,
) => [
{
title: t('New sub-doc'),
onItemClick: createPage,
aliases: ['new sub-doc'],
group,
icon: <AddPageIcon />,
subtext: t('Create a new sub-doc'),
},
];
export const useGetInterlinkingMenuItems = () => {
const treeContext = useTreeContext<Doc>();
const router = useRouter();
const { currentDoc } = useDocStore();
const { mutate: createChildDoc } = useCreateChildDoc({
onSuccess: (createdDoc) => {
const newDoc = {
...createdDoc,
children: [],
childrenCount: 0,
parentId: currentDoc?.id ?? undefined,
};
treeContext?.treeData.addChild(currentDoc?.id || null, newDoc);
router.push(`/docs/${newDoc.id}`);
treeContext?.treeData.setSelectedNode(createdDoc);
},
});
return useCallback(
(t: TFunction<'translation', undefined>) =>
getInterlinkingMenuItems(
t,
t('Links'),
() =>
currentDoc?.id &&
createChildDoc({
parentId: currentDoc.id,
}),
),
[createChildDoc, currentDoc?.id],
);
};

View File

@@ -0,0 +1 @@
export * from './InterlinkingSearchInlineContent';

View File

@@ -0,0 +1 @@
export * from './Interlinking';