(frontend) create page from dropdown search

We are now able to create a new page from
the dropdown search.
This commit is contained in:
Anthony LC
2025-04-25 12:28:40 +02:00
parent 155e7dfe22
commit 2a7c0ef800
7 changed files with 127 additions and 39 deletions

View File

@@ -51,6 +51,28 @@ test.describe('Doc Create', () => {
page.locator('.c__tree-view--row-content').getByText('Untitled document'),
).toBeVisible();
});
test('it creates a sub doc from interlinking dropdown', 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('Link a doc').first().click();
await page
.locator('.quick-search-container')
.getByText('New sub-doc')
.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

@@ -31,11 +31,11 @@ const BoxButton = forwardRef<HTMLDivElement, BoxButtonType>(
$background="none"
$margin="none"
$padding="none"
$hasTransition
$css={css`
cursor: ${props.disabled ? 'not-allowed' : 'pointer'};
border: none;
outline: none;
transition: all 0.2s ease-in-out;
font-family: inherit;
color: ${props.disabled

View File

@@ -0,0 +1,3 @@
<svg width="18" height="23" viewBox="0 0 18 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.57954 5.93282C4.44486 5.79021 4.37793 5.61484 4.37793 5.41299C4.37793 5.21112 4.44495 5.03795 4.58154 4.90136C4.72456 4.75833 4.90437 4.6875 5.11367 4.6875H12.8964C13.0991 4.6875 13.2722 4.75857 13.408 4.90133C13.5508 5.03718 13.6219 5.21027 13.6219 5.41299C13.6219 5.61613 13.5506 5.7918 13.409 5.93387C13.273 6.07732 13.0996 6.14873 12.8964 6.14873H5.11367C4.90437 6.14873 4.72456 6.0779 4.58154 5.93487L4.57954 5.93282ZM4.57954 9.51144C4.44486 9.36882 4.37793 9.19346 4.37793 8.9916C4.37793 8.78973 4.44495 8.61656 4.58154 8.47997C4.72456 8.33695 4.90437 8.26611 5.11367 8.26611H12.8964C13.0991 8.26611 13.2722 8.33719 13.408 8.47995C13.5508 8.61579 13.6219 8.78888 13.6219 8.9916C13.6219 9.19475 13.5506 9.37042 13.409 9.51249C13.273 9.65593 13.0996 9.72734 12.8964 9.72734H5.11367C4.90437 9.72734 4.72456 9.65651 4.58154 9.51348L4.57954 9.51144ZM4.57954 13.1003C4.44561 12.9585 4.37793 12.7869 4.37793 12.5907C4.37793 12.3831 4.44414 12.204 4.57954 12.0606L4.58151 12.0586C4.72453 11.9155 4.90437 11.8447 5.11367 11.8447H8.79482C9.00363 11.8447 9.18092 11.9153 9.3177 12.0596C9.46006 12.2024 9.53057 12.3819 9.53057 12.5907C9.53057 12.7887 9.45812 12.9609 9.31671 13.1024C9.17936 13.2397 9.00235 13.306 8.79482 13.306H5.11367C4.90609 13.306 4.72695 13.2397 4.58358 13.1043L4.57954 13.1003ZM1.09476 0.851519C1.65317 0.285946 2.47955 0.0117188 3.55508 0.0117188H14.4447C15.52 0.0117188 16.3433 0.28583 16.895 0.851748C17.4529 1.41698 17.7234 2.24966 17.7234 3.33145V18.8866C17.7234 19.975 17.4531 20.8082 16.8945 21.3668C16.3427 21.9256 15.5196 22.1961 14.4447 22.1961H3.55508C2.47988 22.1961 1.65367 21.9255 1.09521 21.367C0.543652 20.8083 0.276367 19.9747 0.276367 18.8866V3.33145C0.276367 2.24984 0.543796 1.41679 1.09476 0.851519ZM15.5624 20.0351C15.2958 20.3085 14.8959 20.4452 14.3627 20.4452H3.63711C3.10391 20.4452 2.70059 20.3085 2.42715 20.0351L2.49875 19.9652L2.49786 19.9643L2.42715 20.0351C2.16055 19.7616 2.02725 19.3686 2.02725 18.8559V3.36221C2.02725 2.84951 2.16055 2.45645 2.42715 2.18301C2.70059 1.90273 3.10391 1.7626 3.63711 1.7626H14.3627C14.8959 1.7626 15.2958 1.90273 15.5624 2.18301C15.8358 2.45645 15.9726 2.84951 15.9726 3.36221V18.8559C15.9726 19.3686 15.8358 19.7616 15.5624 20.0351Z" fill="#3A3A3A"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1,14 +1,11 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { createReactInlineContentSpec } from '@blocknote/react';
import { useTreeContext } from '@gouvfr-lasuite/ui-kit';
import { TFunction } from 'i18next';
import { useRouter } from 'next/navigation';
import { useCallback } from 'react';
import { DocsBlockNoteEditor } from '@/docs/doc-editor';
import LinkPageIcon from '@/docs/doc-editor/assets/doc-link.svg';
import AddPageIcon from '@/docs/doc-editor/assets/doc-plus.svg';
import { Doc, useCreateChildDoc, useDocStore } from '@/docs/doc-management';
import { useCreateChildDocTree, useDocStore } from '@/docs/doc-management';
import { SearchPage } from './SearchPage';
@@ -29,12 +26,12 @@ export const InterlinkingSearchInlineContent = createReactInlineContentSpec(
return null;
}
return <SearchPage {...props} />;
return <SearchPage {...props} contentRef={props.contentRef} />;
},
},
);
export const getInterlinkingMenuItems = (
export const getInterlinkinghMenuItems = (
editor: DocsBlockNoteEditor,
t: TFunction<'translation', undefined>,
group: string,
@@ -68,37 +65,11 @@ export const getInterlinkingMenuItems = (
];
export const useGetInterlinkingMenuItems = () => {
const treeContext = useTreeContext<Doc>();
const router = useRouter();
const { currentDoc } = useDocStore();
const createChildDoc = useCreateChildDocTree(currentDoc?.id);
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(
(editor: DocsBlockNoteEditor, t: TFunction<'translation', undefined>) =>
getInterlinkingMenuItems(
editor,
t,
t('Links'),
() =>
currentDoc?.id &&
createChildDoc({
parentId: currentDoc.id,
}),
),
[createChildDoc, currentDoc?.id],
);
return (
editor: DocsBlockNoteEditor,
t: TFunction<'translation', undefined>,
) => getInterlinkinghMenuItems(editor, t, t('Links'), createChildDoc);
};

View File

@@ -5,6 +5,7 @@ import {
} from '@blocknote/core';
import { useBlockNoteEditor } from '@blocknote/react';
import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import {
@@ -12,6 +13,7 @@ import {
Card,
Icon,
QuickSearch,
QuickSearchGroup,
QuickSearchItemContent,
Text,
} from '@/components';
@@ -22,7 +24,12 @@ import {
DocsStyleSchema,
} from '@/docs/doc-editor';
import FoundPageIcon from '@/docs/doc-editor/assets/doc-found.svg';
import { useTrans } from '@/docs/doc-management';
import AddPageIcon from '@/docs/doc-editor/assets/doc-plus.svg';
import {
useCreateChildDocTree,
useDocStore,
useTrans,
} from '@/docs/doc-management';
import { DocSearchSubPageContent, DocSearchTarget } from '@/docs/doc-search';
import { useResponsiveStore } from '@/stores';
@@ -64,6 +71,9 @@ export const SearchPage = ({
DocsInlineContentSchema,
DocsStyleSchema
>();
const { t } = useTranslation();
const { currentDoc } = useDocStore();
const createChildDoc = useCreateChildDocTree(currentDoc?.id);
const inputRef = useRef<HTMLInputElement>(null);
const [search, setSearch] = useState('');
const { isDesktop } = useResponsiveStore();
@@ -248,6 +258,52 @@ export const SearchPage = ({
/>
)}
/>
<QuickSearchGroup
group={{
groupName: '',
elements: [],
endActions: [
{
onSelect: createChildDoc,
content: (
<Box
$css={css`
border-top: 1px solid
var(--c--theme--colors--greyscale-200);
`}
$width="100%"
>
<Box
$direction="row"
$gap="0.4rem"
$align="center"
$padding={{
vertical: '0.5rem',
horizontal: '0.3rem',
}}
$css={css`
&:hover {
background-color: var(
--c--theme--colors--greyscale-100
);
}
`}
>
<AddPageIcon />
<Text
$size="14px"
$color="var(--c--theme--colors--greyscale-1000)"
contentEditable={false}
>
{t('New sub-doc')}
</Text>
</Box>
</Box>
),
},
],
}}
/>
</Card>
</QuickSearch>
</Box>

View File

@@ -1,5 +1,6 @@
export * from './useCollaboration';
export * from './useCopyDocLink';
export * from './useCreateChildDocTree';
export * from './useDocUtils';
export * from './useIsCollaborativeEditable';
export * from './useTrans';

View File

@@ -0,0 +1,35 @@
import { useTreeContext } from '@gouvfr-lasuite/ui-kit';
import { useRouter } from 'next/navigation';
import { useCreateChildDoc } from '../api';
import { Doc } from '../types';
export const useCreateChildDocTree = (parentId?: string) => {
const treeContext = useTreeContext<Doc>();
const router = useRouter();
const { mutate: createChildDoc } = useCreateChildDoc({
onSuccess: (createdDoc) => {
const newDoc = {
...createdDoc,
children: [],
childrenCount: 0,
parentId: parentId ?? undefined,
};
treeContext?.treeData.addChild(parentId || null, newDoc);
router.push(`/docs/${newDoc.id}`);
treeContext?.treeData.setSelectedNode(createdDoc);
},
});
return () => {
if (!parentId) {
return null;
}
createChildDoc({
parentId,
});
};
};