mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-11 20:08:37 +00:00
test: add test case for blocksuite editor (#1528)
This commit is contained in:
@@ -7,6 +7,8 @@ const config = {
|
||||
),
|
||||
prefetchWorkspace: Boolean(process.env.PREFETCH_WORKSPACE ?? '1'),
|
||||
exposeInternal: Boolean(process.env.EXPOSE_INTERNAL ?? '1'),
|
||||
enableDebugPage: Boolean(process.env.ENABLE_DEBUG_PAGE ?? false),
|
||||
enableDebugPage: Boolean(
|
||||
process.env.ENABLE_DEBUG_PAGE ?? process.env.NODE_ENV === 'development'
|
||||
),
|
||||
};
|
||||
export default config;
|
||||
|
||||
71
apps/web/src/components/__debug__/client/Editor.tsx
Normal file
71
apps/web/src/components/__debug__/client/Editor.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
'use client';
|
||||
import { EditorContainer } from '@blocksuite/editor';
|
||||
import { assertEquals, assertExists, Generator, Page } from '@blocksuite/store';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { createEmptyBlockSuiteWorkspace } from '../../../utils';
|
||||
import { BlockSuiteEditor } from '../../blocksuite/block-suite-editor';
|
||||
|
||||
const blockSuiteWorkspace = createEmptyBlockSuiteWorkspace(
|
||||
'test',
|
||||
_ => undefined,
|
||||
Generator.AutoIncrement
|
||||
);
|
||||
|
||||
blockSuiteWorkspace.createPage('page0');
|
||||
|
||||
const Editor: React.FC<{
|
||||
onInit: (page: Page, editor: Readonly<EditorContainer>) => void;
|
||||
testType: 'empty' | 'importMarkdown';
|
||||
}> = ({ onInit, testType }) => {
|
||||
const page = blockSuiteWorkspace.getPage('page0');
|
||||
const [, rerender] = useState(false);
|
||||
const onceRef = useRef(false);
|
||||
if (!onceRef.current) {
|
||||
if (testType === 'importMarkdown') {
|
||||
if (page) {
|
||||
page.workspace.meta.setPageMeta(page.id, {
|
||||
init: true,
|
||||
});
|
||||
} else {
|
||||
blockSuiteWorkspace.slots.pageAdded.on(id => {
|
||||
const page = blockSuiteWorkspace.getPage(id);
|
||||
assertExists(page);
|
||||
assertEquals(id, 'page0');
|
||||
page.workspace.meta.setPageMeta(page.id, {
|
||||
init: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const cb = () => rerender(v => !v);
|
||||
const dispose = blockSuiteWorkspace.slots.pageAdded.on(cb);
|
||||
return () => {
|
||||
dispose.dispose();
|
||||
};
|
||||
}, []);
|
||||
const onLoad = useCallback((page: Page, editor: EditorContainer) => {
|
||||
// @ts-ignore
|
||||
globalThis.page = page;
|
||||
// @ts-ignore
|
||||
globalThis.editor = editor;
|
||||
}, []);
|
||||
|
||||
if (!page) {
|
||||
return <>loading...</>;
|
||||
}
|
||||
return (
|
||||
<BlockSuiteEditor
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
page={page}
|
||||
mode="page"
|
||||
onInit={onInit}
|
||||
onLoad={onLoad}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Editor;
|
||||
@@ -11,23 +11,11 @@ export type EditorProps = {
|
||||
blockSuiteWorkspace: BlockSuiteWorkspace;
|
||||
page: Page;
|
||||
mode: 'page' | 'edgeless';
|
||||
onInit?: (page: Page, editor: Readonly<EditorContainer>) => void;
|
||||
onInit: (page: Page, editor: Readonly<EditorContainer>) => void;
|
||||
onLoad?: (page: Page, editor: EditorContainer) => void;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
|
||||
import markdown from '../../../templates/Welcome-to-AFFiNE.md';
|
||||
|
||||
const exampleTitle = markdown
|
||||
.split('\n')
|
||||
.splice(0, 1)
|
||||
.join('')
|
||||
.replaceAll('#', '')
|
||||
.trim();
|
||||
const exampleText = markdown.split('\n').slice(1).join('\n');
|
||||
|
||||
const kFirstPage = 'affine-first-page';
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
var currentBlockSuiteWorkspace: BlockSuiteWorkspace | undefined;
|
||||
@@ -60,31 +48,7 @@ export const BlockSuiteEditor = (props: EditorProps) => {
|
||||
|
||||
editor.page = page;
|
||||
if (page.root === null) {
|
||||
if (props.onInit) {
|
||||
props.onInit(page, editor);
|
||||
} else {
|
||||
console.debug('Initializing page with default content');
|
||||
// Add page block and surface block at root level
|
||||
const isFirstPage = page.meta.init === true;
|
||||
if (isFirstPage) {
|
||||
page.workspace.setPageMeta(page.id, {
|
||||
init: false,
|
||||
});
|
||||
}
|
||||
const title = isFirstPage ? exampleTitle : undefined;
|
||||
const pageBlockId = page.addBlockByFlavour('affine:page', {
|
||||
title: new page.Text(title),
|
||||
});
|
||||
page.addBlockByFlavour('affine:surface', {}, null);
|
||||
const frameId = page.addBlockByFlavour('affine:frame', {}, pageBlockId);
|
||||
page.addBlockByFlavour('affine:paragraph', {}, frameId);
|
||||
if (isFirstPage) {
|
||||
editor.clipboard.importMarkdown(exampleText, frameId);
|
||||
props.blockSuiteWorkspace.setPageMeta(page.id, { title });
|
||||
localStorage.setItem(kFirstPage, 'true');
|
||||
}
|
||||
page.resetHistory();
|
||||
}
|
||||
props.onInit(page, editor);
|
||||
}
|
||||
if (config.exposeInternal) {
|
||||
globalThis.currentBlockSuiteWorkspace = props.blockSuiteWorkspace;
|
||||
|
||||
@@ -15,7 +15,7 @@ export type PageDetailEditorProps = {
|
||||
isPreview?: boolean;
|
||||
blockSuiteWorkspace: BlockSuiteWorkspace;
|
||||
pageId: string;
|
||||
onInit?: (page: Page, editor: Readonly<EditorContainer>) => void;
|
||||
onInit: (page: Page, editor: Readonly<EditorContainer>) => void;
|
||||
onLoad?: (page: Page, editor: EditorContainer) => void;
|
||||
header?: React.ReactNode;
|
||||
};
|
||||
|
||||
41
apps/web/src/pages/_debug/init-page.dev.tsx
Normal file
41
apps/web/src/pages/_debug/init-page.dev.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { NoSsr } from '@mui/material';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import { StyledPage, StyledWrapper } from '../../layouts/styles';
|
||||
import { NextPageWithLayout } from '../../shared';
|
||||
import { initPage } from '../../utils/blocksuite';
|
||||
|
||||
const Editor = dynamic(
|
||||
() => import('../../components/__debug__/client/Editor'),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
);
|
||||
|
||||
const InitPagePage: NextPageWithLayout = () => {
|
||||
const router = useRouter();
|
||||
if (!router.isReady) {
|
||||
return <>loading...</>;
|
||||
}
|
||||
let testType: 'empty' | 'importMarkdown' = 'empty';
|
||||
if (router.query.type === 'importMarkdown') {
|
||||
testType = 'importMarkdown';
|
||||
} else if (router.query.type === 'empty') {
|
||||
testType = 'empty';
|
||||
}
|
||||
return (
|
||||
<StyledPage>
|
||||
<StyledWrapper>
|
||||
<Editor onInit={initPage} testType={testType} />
|
||||
<div id="toolWrapper" />
|
||||
</StyledWrapper>
|
||||
</StyledPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default InitPagePage;
|
||||
|
||||
InitPagePage.getLayout = page => {
|
||||
return <NoSsr>{page}</NoSsr>;
|
||||
};
|
||||
@@ -23,6 +23,7 @@ import { useBlockSuiteWorkspaceAvatarUrl } from '../../../hooks/use-blocksuite-w
|
||||
import { useBlockSuiteWorkspaceName } from '../../../hooks/use-blocksuite-workspace-name';
|
||||
import { WorkspaceLayout } from '../../../layouts';
|
||||
import { NextPageWithLayout } from '../../../shared';
|
||||
import { initPage } from '../../../utils/blocksuite';
|
||||
|
||||
export const NavContainer = styled('div')(({ theme }) => {
|
||||
return {
|
||||
@@ -85,6 +86,7 @@ const PublicWorkspaceDetailPageInner: React.FC<{
|
||||
const { page } = editor;
|
||||
page.awarenessStore.setReadonly(page, true);
|
||||
}}
|
||||
onInit={initPage}
|
||||
header={
|
||||
<NavContainer>
|
||||
<Breadcrumbs>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from '../../shared';
|
||||
import { apis, clientAuth } from '../../shared/apis';
|
||||
import { createEmptyBlockSuiteWorkspace } from '../../utils';
|
||||
import { initPage } from '../../utils/blocksuite';
|
||||
import { WorkspacePlugin } from '..';
|
||||
import { QueryKey } from './fetcher';
|
||||
|
||||
@@ -215,6 +216,7 @@ export const AffinePlugin: WorkspacePlugin<RemWorkspaceFlavour.AFFINE> = {
|
||||
<PageDetailEditor
|
||||
pageId={currentPageId}
|
||||
blockSuiteWorkspace={currentWorkspace.blockSuiteWorkspace}
|
||||
onInit={initPage}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
RemWorkspaceFlavour,
|
||||
} from '../../shared';
|
||||
import { createEmptyBlockSuiteWorkspace } from '../../utils';
|
||||
import { initPage } from '../../utils/blocksuite';
|
||||
import { WorkspacePlugin } from '..';
|
||||
|
||||
const getStorage = () => createJSONStorage(() => localStorage);
|
||||
@@ -117,6 +118,7 @@ export const LocalPlugin: WorkspacePlugin<RemWorkspaceFlavour.LOCAL> = {
|
||||
<>
|
||||
<PageDetailEditor
|
||||
pageId={currentPageId}
|
||||
onInit={initPage}
|
||||
blockSuiteWorkspace={currentWorkspace.blockSuiteWorkspace}
|
||||
/>
|
||||
</>
|
||||
|
||||
55
apps/web/src/utils/blocksuite.ts
Normal file
55
apps/web/src/utils/blocksuite.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import { EditorContainer } from '@blocksuite/editor';
|
||||
import { Page } from '@blocksuite/store';
|
||||
|
||||
import markdown from '../templates/Welcome-to-AFFiNE.md';
|
||||
|
||||
const demoTitle = markdown
|
||||
.split('\n')
|
||||
.splice(0, 1)
|
||||
.join('')
|
||||
.replaceAll('#', '')
|
||||
.trim();
|
||||
|
||||
const demoText = markdown.split('\n').slice(1).join('\n');
|
||||
|
||||
const logger = new DebugLogger('init-page');
|
||||
|
||||
export function initPage(page: Page, editor: Readonly<EditorContainer>): void {
|
||||
logger.debug('initEmptyPage', page.id);
|
||||
// Add page block and surface block at root level
|
||||
const isFirstPage = page.meta.init === true;
|
||||
if (isFirstPage) {
|
||||
page.workspace.setPageMeta(page.id, {
|
||||
init: false,
|
||||
});
|
||||
_initPageWithDemoMarkdown(page, editor);
|
||||
} else {
|
||||
_initEmptyPage(page, editor);
|
||||
}
|
||||
page.resetHistory();
|
||||
}
|
||||
|
||||
export function _initEmptyPage(page: Page, _: Readonly<EditorContainer>) {
|
||||
const pageBlockId = page.addBlockByFlavour('affine:page', {
|
||||
title: new page.Text(''),
|
||||
});
|
||||
page.addBlockByFlavour('affine:surface', {}, null);
|
||||
const frameId = page.addBlockByFlavour('affine:frame', {}, pageBlockId);
|
||||
page.addBlockByFlavour('affine:paragraph', {}, frameId);
|
||||
}
|
||||
|
||||
export function _initPageWithDemoMarkdown(
|
||||
page: Page,
|
||||
editor: Readonly<EditorContainer>
|
||||
): void {
|
||||
logger.debug('initPageWithDefaultMarkdown', page.id);
|
||||
const pageBlockId = page.addBlockByFlavour('affine:page', {
|
||||
title: new page.Text(demoTitle),
|
||||
});
|
||||
page.addBlockByFlavour('affine:surface', {}, null);
|
||||
const frameId = page.addBlockByFlavour('affine:frame', {}, pageBlockId);
|
||||
page.addBlockByFlavour('affine:paragraph', {}, frameId);
|
||||
editor.clipboard.importMarkdown(demoText, frameId);
|
||||
page.workspace.setPageMeta(page.id, { demoTitle });
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { __unstableSchemas, builtInSchemas } from '@blocksuite/blocks/models';
|
||||
import type { BlobOptionsGetter } from '@blocksuite/store';
|
||||
import type { BlobOptionsGetter, Generator } from '@blocksuite/store';
|
||||
|
||||
import { BlockSuiteWorkspace } from '../shared';
|
||||
|
||||
@@ -27,7 +27,8 @@ export function stringToColour(str: string) {
|
||||
const hashMap = new Map<string, BlockSuiteWorkspace>();
|
||||
export const createEmptyBlockSuiteWorkspace = (
|
||||
id: string,
|
||||
blobOptionsGetter?: BlobOptionsGetter
|
||||
blobOptionsGetter?: BlobOptionsGetter,
|
||||
idGenerator?: Generator
|
||||
): BlockSuiteWorkspace => {
|
||||
if (hashMap.has(id)) {
|
||||
return hashMap.get(id) as BlockSuiteWorkspace;
|
||||
@@ -36,6 +37,7 @@ export const createEmptyBlockSuiteWorkspace = (
|
||||
id,
|
||||
isSSR: typeof window === 'undefined',
|
||||
blobOptionsGetter,
|
||||
idGenerator,
|
||||
})
|
||||
.register(builtInSchemas)
|
||||
.register(__unstableSchemas);
|
||||
|
||||
Reference in New Issue
Block a user