mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
feat(infra): framework
This commit is contained in:
@@ -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');
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 */
|
||||
),
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user