feat(infra): framework

This commit is contained in:
EYHN
2024-04-17 14:12:29 +08:00
parent ab17a05df3
commit 06fda3b62c
467 changed files with 9996 additions and 8697 deletions

View File

@@ -77,7 +77,5 @@ test('Delete last workspace', async ({ page }) => {
await page.getByTestId('create-workspace-create-button').click();
await page.waitForTimeout(1000);
await page.waitForSelector('[data-testid="workspace-name"]');
expect(await page.getByTestId('workspace-name').textContent()).toBe(
'Test Workspace'
);
await expect(page.getByTestId('workspace-name')).toHaveText('Test Workspace');
});

View File

@@ -138,7 +138,6 @@ test('create multi workspace in the workspace list', async ({
await page.waitForTimeout(1000);
// check workspace list length
{
await page.waitForTimeout(1000);
const workspaceCards = page.getByTestId('workspace-card');
await expect(workspaceCards).toHaveCount(3);
}

View File

@@ -8,22 +8,24 @@ import { useDarkMode } from 'storybook-dark-mode';
import { AffineContext } from '@affine/component/context';
import useSWR from 'swr';
import type { Decorator } from '@storybook/react';
import { _setCurrentStore } from '@toeverything/infra';
import {
FrameworkRoot,
FrameworkScope,
GlobalContextService,
LifecycleService,
WorkspacesService,
_setCurrentStore,
configureTestingInfraModules,
useLiveData,
} from '@toeverything/infra';
import { setupGlobal, type Environment } from '@affine/env/global';
import type { Preview } from '@storybook/react';
import { useLayoutEffect, useRef } from 'react';
import { useLayoutEffect, useMemo, useRef } from 'react';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { ServiceCollection } from '@toeverything/infra';
import {
WorkspaceManager,
configureInfraServices,
configureTestingInfraServices,
} from '@toeverything/infra';
import { CurrentWorkspaceService } from '@affine/core/modules/workspace';
import { configureBusinessServices } from '@affine/core/modules/services';
import { Framework } from '@toeverything/infra';
import { configureCommonModules } from '@affine/core/modules';
import { createStore } from 'jotai';
import { GlobalScopeProvider } from '@affine/core/modules/infra-web/global-scope';
setupGlobal();
export const parameters = {
@@ -71,41 +73,59 @@ window.localStorage.setItem('dismissAiOnboarding', 'true');
window.localStorage.setItem('dismissAiOnboardingEdgeless', 'true');
window.localStorage.setItem('dismissAiOnboardingLocal', 'true');
const services = new ServiceCollection();
const framework = new Framework();
configureInfraServices(services);
configureTestingInfraServices(services);
configureBusinessServices(services);
configureCommonModules(framework);
configureTestingInfraModules(framework);
const provider = services.provider();
const frameworkProvider = framework.provider();
frameworkProvider.get(LifecycleService).applicationStart();
const globalContextService = frameworkProvider.get(GlobalContextService);
const store = createStore();
_setCurrentStore(store);
provider
.get(WorkspaceManager)
.createWorkspace(WorkspaceFlavour.LOCAL, async w => {
frameworkProvider
.get(WorkspacesService)
.create(WorkspaceFlavour.LOCAL, async w => {
w.meta.setName('test-workspace');
w.meta.writeVersion(w);
})
.then(workspaceMetadata => {
const currentWorkspace = provider.get(CurrentWorkspaceService);
const workspaceManager = provider.get(WorkspaceManager);
currentWorkspace.openWorkspace(
workspaceManager.open(workspaceMetadata).workspace
);
.then(meta => {
globalContextService.globalContext.workspaceId.set(meta.id);
});
const withContextDecorator: Decorator = (Story, context) => {
const workspaceId = useLiveData(
globalContextService.globalContext.workspaceId.$
);
const { workspace } =
useMemo(() => {
if (!workspaceId) {
return null;
}
return frameworkProvider.get(WorkspacesService).open({
metadata: { flavour: WorkspaceFlavour.LOCAL, id: workspaceId },
});
}, []) ?? {};
if (!workspace) {
return <>loading..</>;
}
return (
<GlobalScopeProvider provider={provider}>
<ThemeProvider>
<AffineContext store={store}>
<ThemeChange />
<Story {...context} />
</AffineContext>
</ThemeProvider>
</GlobalScopeProvider>
<FrameworkRoot framework={frameworkProvider}>
<FrameworkScope scope={workspace.scope}>
<ThemeProvider>
<AffineContext store={store}>
<ThemeChange />
<Story {...context} />
</AffineContext>
</ThemeProvider>
</FrameworkScope>
</FrameworkRoot>
);
};

View File

@@ -10,7 +10,6 @@
"@affine/cli": "workspace:*",
"@affine/component": "workspace:*",
"@affine/i18n": "workspace:*",
"@affine/workspace-impl": "workspace:*",
"@dnd-kit/sortable": "^8.0.0",
"@storybook/jest": "^0.2.3",
"@storybook/testing-library": "^0.2.2",

View File

@@ -1,5 +1,4 @@
import { NavigateContext } from '@affine/core/hooks/use-navigate-helper';
import { topLevelRoutes } from '@affine/core/router';
import { NavigateContext, topLevelRoutes } from '@affine/core/router';
import { assertExists } from '@blocksuite/global/utils';
import type { StoryFn } from '@storybook/react';
import { screen, userEvent, waitFor, within } from '@storybook/testing-library';
@@ -35,7 +34,9 @@ export const Index: StoryFn = () => {
Index.decorators = [withRouter];
Index.parameters = {
reactRouter: reactRouterParameters({
routing: reactRouterOutlets(topLevelRoutes),
routing: reactRouterOutlets(
topLevelRoutes[0].children /* skip root wrapper */
),
}),
};

View File

@@ -3,11 +3,11 @@ import { ImagePreviewModal } from '@affine/core/components/image-preview';
import type { Meta, StoryFn } from '@storybook/react';
import type { Doc } from '@toeverything/infra';
import {
DocsService,
FrameworkScope,
initEmptyPage,
PageManager,
ServiceProviderContext,
useService,
Workspace,
WorkspaceService,
} from '@toeverything/infra';
import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
@@ -19,16 +19,16 @@ export default {
} satisfies Meta;
export const Default: StoryFn = () => {
const workspace = useService(Workspace);
const pageManager = useService(PageManager);
const workspace = useService(WorkspaceService).workspace;
const docsService = useService(DocsService);
const [page, setPage] = useState<Doc | null>(null);
const [doc, setDoc] = useState<Doc | null>(null);
useEffect(() => {
const bsPage = workspace.docCollection.createDoc({ id: 'page0' });
initEmptyPage(bsPage);
const bsDoc = workspace.docCollection.createDoc({ id: 'page0' });
initEmptyPage(bsDoc);
const { page, release } = pageManager.open(bsPage.meta!.id);
const { doc, release } = docsService.open(bsDoc.meta!.id);
fetch(new URL('@affine-test/fixtures/large-image.png', import.meta.url))
.then(res => res.arrayBuffer())
@@ -36,17 +36,17 @@ export const Default: StoryFn = () => {
const id = await workspace.docCollection.blob.set(
new Blob([buffer], { type: 'image/png' })
);
const frameId = bsPage.getBlockByFlavour('affine:note')[0].id;
bsPage.addBlock(
const frameId = bsDoc.getBlockByFlavour('affine:note')[0].id;
bsDoc.addBlock(
'affine:paragraph',
{
text: new bsPage.Text(
text: new bsDoc.Text(
'Please double click the image to preview it.'
),
},
frameId
);
bsPage.addBlock(
bsDoc.addBlock(
'affine:image',
{
sourceId: id,
@@ -57,19 +57,19 @@ export const Default: StoryFn = () => {
.catch(err => {
console.error('Failed to load large-image.png', err);
});
setPage(page);
setDoc(doc);
return () => {
release();
};
}, [pageManager, workspace]);
}, [docsService, workspace]);
if (!page) {
if (!doc) {
return <div />;
}
return (
<ServiceProviderContext.Provider value={page.services}>
<FrameworkScope scope={doc.scope}>
<div
style={{
height: '100vh',
@@ -77,16 +77,16 @@ export const Default: StoryFn = () => {
overflow: 'auto',
}}
>
<BlockSuiteEditor mode="page" page={page.blockSuiteDoc} />
<BlockSuiteEditor mode="page" page={doc.blockSuiteDoc} />
{createPortal(
<ImagePreviewModal
pageId={page.id}
docCollection={page.blockSuiteDoc.collection}
pageId={doc.id}
docCollection={doc.blockSuiteDoc.collection}
/>,
document.body
)}
</div>
</ServiceProviderContext.Provider>
</FrameworkScope>
);
};

View File

@@ -5,7 +5,11 @@ import { WorkspaceFlavour } from '@affine/env/workspace';
import type { Doc } from '@blocksuite/store';
import { expect } from '@storybook/jest';
import type { Meta, StoryFn } from '@storybook/react';
import { initEmptyPage, useService, Workspace } from '@toeverything/infra';
import {
initEmptyPage,
useService,
WorkspaceService,
} from '@toeverything/infra';
import { nanoid } from 'nanoid';
import { useEffect, useState } from 'react';
@@ -22,7 +26,7 @@ async function unimplemented() {
}
export const Basic: StoryFn = () => {
const workspace = useService(Workspace);
const workspace = useService(WorkspaceService).workspace;
const [page, setPage] = useState<Doc | null>(null);
@@ -64,7 +68,7 @@ Basic.play = async ({ canvasElement }) => {
};
export const AffineBasic: StoryFn = () => {
const workspace = useService(Workspace);
const workspace = useService(WorkspaceService).workspace;
const [page, setPage] = useState<Doc | null>(null);

View File

@@ -1,7 +1,11 @@
import type { WorkspaceListProps } from '@affine/component/workspace-list';
import { WorkspaceList } from '@affine/component/workspace-list';
import type { Meta } from '@storybook/react';
import { useLiveData, useService, WorkspaceManager } from '@toeverything/infra';
import {
useLiveData,
useService,
WorkspacesService,
} from '@toeverything/infra';
export default {
title: 'AFFiNE/WorkspaceList',
@@ -12,7 +16,7 @@ export default {
} satisfies Meta<WorkspaceListProps>;
export const Default = () => {
const list = useLiveData(useService(WorkspaceManager).list.workspaceList$);
const list = useLiveData(useService(WorkspacesService).list.workspaces$);
return (
<WorkspaceList
currentWorkspaceId={null}