@dxos/react-client 0.6.11-staging.e6894a4 → 0.6.12-main.15a606f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/{chunk-NLQLAARM.mjs → chunk-ESSNDYBJ.mjs} +116 -79
- package/dist/lib/browser/chunk-ESSNDYBJ.mjs.map +7 -0
- package/dist/lib/browser/devtools/index.mjs +6 -18
- package/dist/lib/browser/devtools/index.mjs.map +3 -3
- package/dist/lib/browser/echo/index.mjs +9 -7
- package/dist/lib/browser/echo/index.mjs.map +3 -3
- package/dist/lib/browser/halo/index.mjs +5 -5
- package/dist/lib/browser/halo/index.mjs.map +1 -1
- package/dist/lib/browser/index.mjs +4 -4
- package/dist/lib/browser/index.mjs.map +1 -1
- package/dist/lib/browser/mesh/index.mjs +2 -2
- package/dist/lib/browser/mesh/index.mjs.map +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +115 -12
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node/{chunk-E3S6YHNJ.cjs → chunk-N474KOAU.cjs} +102 -69
- package/dist/lib/node/chunk-N474KOAU.cjs.map +7 -0
- package/dist/lib/node/devtools/index.cjs +8 -20
- package/dist/lib/node/devtools/index.cjs.map +3 -3
- package/dist/lib/node/echo/index.cjs +14 -12
- package/dist/lib/node/echo/index.cjs.map +3 -3
- package/dist/lib/node/halo/index.cjs +13 -13
- package/dist/lib/node/halo/index.cjs.map +2 -2
- package/dist/lib/node/index.cjs +15 -15
- package/dist/lib/node/index.cjs.map +1 -1
- package/dist/lib/node/mesh/index.cjs +4 -4
- package/dist/lib/node/mesh/index.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +113 -10
- package/dist/lib/node/testing/index.cjs.map +4 -4
- package/dist/lib/node-esm/chunk-ACLHQASD.mjs +302 -0
- package/dist/lib/node-esm/chunk-ACLHQASD.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-UYJUBYMU.mjs +165 -0
- package/dist/lib/node-esm/chunk-UYJUBYMU.mjs.map +7 -0
- package/dist/lib/node-esm/devtools/index.mjs +44 -0
- package/dist/lib/node-esm/devtools/index.mjs.map +7 -0
- package/dist/lib/node-esm/echo/index.mjs +134 -0
- package/dist/lib/node-esm/echo/index.mjs.map +7 -0
- package/dist/lib/node-esm/halo/index.mjs +100 -0
- package/dist/lib/node-esm/halo/index.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +33 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/invitations/index.mjs +18 -0
- package/dist/lib/node-esm/invitations/index.mjs.map +7 -0
- package/dist/lib/node-esm/mesh/index.mjs +18 -0
- package/dist/lib/node-esm/mesh/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/testing/index.mjs +173 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/lib/node-esm/worker.mjs +5 -0
- package/dist/lib/node-esm/worker.mjs.map +7 -0
- package/dist/types/src/client/AgentHostingProvider.d.ts +6 -0
- package/dist/types/src/client/AgentHostingProvider.d.ts.map +1 -1
- package/dist/types/src/client/ClientProvider.d.ts +108 -0
- package/dist/types/src/client/ClientProvider.d.ts.map +1 -0
- package/dist/types/src/client/ClientProvider.test.d.ts +2 -0
- package/dist/types/src/client/ClientProvider.test.d.ts.map +1 -0
- package/dist/types/src/client/context.d.ts +6 -0
- package/dist/types/src/client/context.d.ts.map +1 -0
- package/dist/types/src/client/index.d.ts +4 -2
- package/dist/types/src/client/index.d.ts.map +1 -1
- package/dist/types/src/client/useClient.d.ts +6 -0
- package/dist/types/src/client/useClient.d.ts.map +1 -0
- package/dist/types/src/client/useClientStatus.d.ts +3 -0
- package/dist/types/src/client/useClientStatus.d.ts.map +1 -0
- package/dist/types/src/devtools/useDevtools.d.ts.map +1 -1
- package/dist/types/src/echo/useQuery.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/playwright/playwright.config.d.ts +2 -2
- package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
- package/dist/types/src/testing/ClientRepeater.d.ts +6 -5
- package/dist/types/src/testing/ClientRepeater.d.ts.map +1 -1
- package/dist/types/src/testing/{decorators.stories.d.ts → ClientRepeater.stories.d.ts} +3 -3
- package/dist/types/src/testing/ClientRepeater.stories.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +1 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/util.d.ts +14 -0
- package/dist/types/src/testing/util.d.ts.map +1 -0
- package/dist/types/src/testing/withClientProvider.d.ts +29 -0
- package/dist/types/src/testing/withClientProvider.d.ts.map +1 -0
- package/dist/types/src/testing/withClientProvider.stories.d.ts +14 -0
- package/dist/types/src/testing/withClientProvider.stories.d.ts.map +1 -0
- package/package.json +48 -36
- package/project.json +3 -23
- package/src/client/AgentHostingProvider.tsx +7 -6
- package/src/client/{ClientContext.test.tsx → ClientProvider.test.tsx} +9 -5
- package/src/client/ClientProvider.tsx +196 -0
- package/src/client/context.ts +20 -0
- package/src/client/index.ts +5 -2
- package/src/client/useClient.ts +18 -0
- package/src/client/useClientStatus.ts +21 -0
- package/src/client/useConfig.test.tsx +7 -11
- package/src/client/useConfig.ts +1 -1
- package/src/client/useShell.ts +1 -1
- package/src/client/useStatus.ts +1 -1
- package/src/devtools/useDevtools.ts +3 -7
- package/src/echo/useMembers.ts +1 -1
- package/src/echo/useQuery.ts +3 -6
- package/src/echo/useSpaceInvitations.ts +1 -1
- package/src/echo/useSpaces.test.tsx +11 -37
- package/src/echo/useSpaces.ts +1 -1
- package/src/halo/useContacts.ts +1 -1
- package/src/halo/useDevices.test.tsx +4 -13
- package/src/halo/useDevices.ts +1 -1
- package/src/halo/useHaloInvitations.ts +1 -1
- package/src/halo/useIdentity.ts +1 -1
- package/src/index.ts +1 -1
- package/src/mesh/useNetworkStatus.ts +1 -1
- package/src/playwright/playwright.config.ts +13 -2
- package/src/playwright/smoke.spec.ts +5 -10
- package/src/testing/ClientRepeater.stories.tsx +58 -0
- package/src/testing/ClientRepeater.tsx +14 -13
- package/src/testing/index.ts +1 -0
- package/src/testing/util.tsx +39 -0
- package/src/testing/withClientProvider.stories.tsx +65 -0
- package/src/testing/withClientProvider.tsx +146 -0
- package/tsconfig.json +11 -3
- package/vitest.config.ts +22 -0
- package/dist/lib/browser/chunk-NLQLAARM.mjs.map +0 -7
- package/dist/lib/node/chunk-E3S6YHNJ.cjs.map +0 -7
- package/dist/types/src/client/ClientContext.d.ts +0 -57
- package/dist/types/src/client/ClientContext.d.ts.map +0 -1
- package/dist/types/src/client/ClientContext.stories.d.ts +0 -15
- package/dist/types/src/client/ClientContext.stories.d.ts.map +0 -1
- package/dist/types/src/client/ClientContext.test.d.ts +0 -2
- package/dist/types/src/client/ClientContext.test.d.ts.map +0 -1
- package/dist/types/src/client/useClientServices.d.ts +0 -3
- package/dist/types/src/client/useClientServices.d.ts.map +0 -1
- package/dist/types/src/testing/decorators.stories.d.ts.map +0 -1
- package/src/client/ClientContext.stories.tsx +0 -94
- package/src/client/ClientContext.tsx +0 -182
- package/src/client/useClientServices.ts +0 -15
- package/src/testing/decorators.stories.tsx +0 -63
|
@@ -6,7 +6,7 @@ import { useMemo } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { MulticastObservable } from '@dxos/async';
|
|
8
8
|
import { type PublicKey } from '@dxos/client';
|
|
9
|
-
import { useMulticastObservable } from '@dxos/react-
|
|
9
|
+
import { useMulticastObservable } from '@dxos/react-hooks';
|
|
10
10
|
|
|
11
11
|
import { useSpace } from './useSpaces';
|
|
12
12
|
import { type CancellableInvitationObservable, useInvitationStatus } from '../invitations';
|
|
@@ -3,38 +3,22 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { act, renderHook } from '@testing-library/react';
|
|
6
|
-
import { expect } from '
|
|
7
|
-
import React from 'react';
|
|
8
|
-
|
|
9
|
-
import { Client, fromHost } from '@dxos/client';
|
|
10
|
-
import { describe, test } from '@dxos/test';
|
|
6
|
+
import { describe, expect, test } from 'vitest';
|
|
11
7
|
|
|
12
8
|
import { useSpace, useSpaces } from './useSpaces';
|
|
13
|
-
import {
|
|
9
|
+
import { createClient, createClientContextProvider } from '../testing/util';
|
|
14
10
|
|
|
15
11
|
describe('useSpaces', () => {
|
|
16
12
|
test('lists existing spaces', async () => {
|
|
17
|
-
const client =
|
|
18
|
-
await client
|
|
19
|
-
await client.halo.createIdentity();
|
|
20
|
-
await client.spaces.isReady.wait();
|
|
21
|
-
// TODO(wittjosiah): Factor out.
|
|
22
|
-
const wrapper = ({ children }: any) => (
|
|
23
|
-
<ClientContext.Provider value={{ client }}>{children}</ClientContext.Provider>
|
|
24
|
-
);
|
|
13
|
+
const { client } = await createClient({ createIdentity: true });
|
|
14
|
+
const wrapper = await createClientContextProvider(client);
|
|
25
15
|
const { result } = renderHook(() => useSpaces(), { wrapper });
|
|
26
16
|
expect(result.current.length).to.eq(1);
|
|
27
17
|
});
|
|
28
18
|
|
|
29
19
|
test('updates when new spaces are created', async () => {
|
|
30
|
-
const client =
|
|
31
|
-
await client
|
|
32
|
-
await client.halo.createIdentity();
|
|
33
|
-
await client.spaces.isReady.wait();
|
|
34
|
-
// TODO(wittjosiah): Factor out.
|
|
35
|
-
const wrapper = ({ children }: any) => (
|
|
36
|
-
<ClientContext.Provider value={{ client }}>{children}</ClientContext.Provider>
|
|
37
|
-
);
|
|
20
|
+
const { client } = await createClient({ createIdentity: true });
|
|
21
|
+
const wrapper = await createClientContextProvider(client);
|
|
38
22
|
const { result, rerender } = renderHook(() => useSpaces(), { wrapper });
|
|
39
23
|
expect(result.current.length).to.eq(1);
|
|
40
24
|
await act(async () => {
|
|
@@ -47,12 +31,8 @@ describe('useSpaces', () => {
|
|
|
47
31
|
|
|
48
32
|
describe('useSpace', () => {
|
|
49
33
|
test('gets default space', async () => {
|
|
50
|
-
const client =
|
|
51
|
-
await client
|
|
52
|
-
// TODO(wittjosiah): Factor out.
|
|
53
|
-
const wrapper = ({ children }: any) => (
|
|
54
|
-
<ClientContext.Provider value={{ client }}>{children}</ClientContext.Provider>
|
|
55
|
-
);
|
|
34
|
+
const { client } = await createClient();
|
|
35
|
+
const wrapper = await createClientContextProvider(client);
|
|
56
36
|
const { result, rerender } = renderHook(() => useSpace(), { wrapper });
|
|
57
37
|
expect(result.current).to.be.undefined;
|
|
58
38
|
await act(async () => {
|
|
@@ -64,15 +44,9 @@ describe('useSpace', () => {
|
|
|
64
44
|
});
|
|
65
45
|
|
|
66
46
|
test('gets space by key', async () => {
|
|
67
|
-
const client =
|
|
68
|
-
await client
|
|
69
|
-
|
|
70
|
-
const space = await client.spaces.create();
|
|
71
|
-
// TODO(wittjosiah): Factor out.
|
|
72
|
-
const wrapper = ({ children }: any) => (
|
|
73
|
-
<ClientContext.Provider value={{ client }}>{children}</ClientContext.Provider>
|
|
74
|
-
);
|
|
75
|
-
const { result } = renderHook(() => useSpace(space.key), { wrapper });
|
|
47
|
+
const { client, space } = await createClient({ createIdentity: true, createSpace: true });
|
|
48
|
+
const wrapper = await createClientContextProvider(client);
|
|
49
|
+
const { result } = renderHook(() => useSpace(space!.key), { wrapper });
|
|
76
50
|
expect(result.current).to.not.be.undefined;
|
|
77
51
|
});
|
|
78
52
|
});
|
package/src/echo/useSpaces.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { useEffect, useState } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { type PublicKeyLike } from '@dxos/client';
|
|
8
8
|
import { type Space, SpaceState } from '@dxos/client/echo';
|
|
9
|
-
import { useMulticastObservable } from '@dxos/react-
|
|
9
|
+
import { useMulticastObservable } from '@dxos/react-hooks';
|
|
10
10
|
|
|
11
11
|
import { useClient } from '../client';
|
|
12
12
|
|
package/src/halo/useContacts.ts
CHANGED
|
@@ -3,24 +3,15 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { renderHook } from '@testing-library/react';
|
|
6
|
-
import { expect } from '
|
|
7
|
-
import React from 'react';
|
|
8
|
-
|
|
9
|
-
import { Client, fromHost } from '@dxos/client';
|
|
10
|
-
import { describe, test } from '@dxos/test';
|
|
6
|
+
import { describe, expect, test } from 'vitest';
|
|
11
7
|
|
|
12
8
|
import { useDevices } from './useDevices';
|
|
13
|
-
import {
|
|
9
|
+
import { createClient, createClientContextProvider } from '../testing/util';
|
|
14
10
|
|
|
15
11
|
describe('useDevices', () => {
|
|
16
12
|
test('lists existing devices', async () => {
|
|
17
|
-
const client =
|
|
18
|
-
await client
|
|
19
|
-
await client.halo.createIdentity();
|
|
20
|
-
// TODO(wittjosiah): Factor out.
|
|
21
|
-
const wrapper = ({ children }: any) => (
|
|
22
|
-
<ClientContext.Provider value={{ client }}>{children}</ClientContext.Provider>
|
|
23
|
-
);
|
|
13
|
+
const { client } = await createClient({ createIdentity: true });
|
|
14
|
+
const wrapper = await createClientContextProvider(client);
|
|
24
15
|
const { result } = renderHook(() => useDevices(), { wrapper });
|
|
25
16
|
expect(result.current?.length).to.eq(1);
|
|
26
17
|
});
|
package/src/halo/useDevices.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { useMemo } from 'react';
|
|
6
6
|
|
|
7
|
-
import { useMulticastObservable } from '@dxos/react-
|
|
7
|
+
import { useMulticastObservable } from '@dxos/react-hooks';
|
|
8
8
|
|
|
9
9
|
import { useClient } from '../client';
|
|
10
10
|
import { useInvitationStatus, type CancellableInvitationObservable } from '../invitations';
|
package/src/halo/useIdentity.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { nxE2EPreset } from '@nx/playwright/preset';
|
|
6
|
+
import { defineConfig } from '@playwright/test';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
import { e2ePreset } from '@dxos/test-utils/playwright';
|
|
9
|
+
|
|
10
|
+
export default defineConfig({
|
|
11
|
+
...nxE2EPreset(__filename, { testDir: __dirname }),
|
|
12
|
+
...e2ePreset(__dirname),
|
|
13
|
+
webServer: {
|
|
14
|
+
command: 'pnpm -w nx storybook-e2e stories',
|
|
15
|
+
port: 9009,
|
|
16
|
+
reuseExistingServer: !process.env.CI,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
// Copyright 2021 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type Page, test } from '@playwright/test';
|
|
6
|
-
import { expect } from 'chai';
|
|
5
|
+
import { type Page, expect, test } from '@playwright/test';
|
|
7
6
|
|
|
8
|
-
import { setupPage } from '@dxos/test/playwright';
|
|
7
|
+
import { setupPage } from '@dxos/test-utils/playwright';
|
|
9
8
|
|
|
10
9
|
// TODO(wittjosiah): Factor out.
|
|
11
10
|
// TODO(burdon): No hard-coding of ports; reconcile all DXOS tools ports.
|
|
@@ -15,17 +14,13 @@ test.describe('Smoke test', () => {
|
|
|
15
14
|
let page: Page;
|
|
16
15
|
|
|
17
16
|
test.beforeAll(async ({ browser }) => {
|
|
18
|
-
const result = await setupPage(browser, {
|
|
19
|
-
url: storybookUrl('react-client-clientcontext--default'),
|
|
20
|
-
waitFor: (page) => page.isVisible(':has-text("initialized")'),
|
|
21
|
-
});
|
|
22
|
-
|
|
17
|
+
const result = await setupPage(browser, { url: storybookUrl('react-client-withclientprovider--default') });
|
|
23
18
|
page = result.page;
|
|
19
|
+
await page.locator(':text("initialized")').waitFor({ state: 'visible' });
|
|
24
20
|
});
|
|
25
21
|
|
|
26
22
|
// NOTE: This test depends on connecting to the default production deployed HALO vault.
|
|
27
23
|
test('Renders remote client info', async () => {
|
|
28
|
-
|
|
29
|
-
expect(isVisible).to.be.true;
|
|
24
|
+
await expect(page.locator(':text("initialized")')).toBeVisible();
|
|
30
25
|
});
|
|
31
26
|
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxos-theme';
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
import { Input } from '@dxos/react-ui';
|
|
10
|
+
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
11
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
12
|
+
|
|
13
|
+
import { ClientRepeater, type ClientRepeatedComponentProps } from './ClientRepeater';
|
|
14
|
+
import { useClient } from '../client';
|
|
15
|
+
import { useSpace } from '../echo';
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
title: 'react-client/ClientRepeater',
|
|
19
|
+
decorators: [withTheme],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const JsonPanel = ({ value }: { value: any }) => (
|
|
23
|
+
<SyntaxHighlighter language='json'>{JSON.stringify(value, undefined, 2)}</SyntaxHighlighter>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const ClientStory = () => {
|
|
27
|
+
const client = useClient();
|
|
28
|
+
return <JsonPanel value={client.toJSON()} />;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const ClientSpace = ({ spaceKey }: ClientRepeatedComponentProps) => {
|
|
32
|
+
const client = useClient();
|
|
33
|
+
const space = useSpace(spaceKey);
|
|
34
|
+
if (!space?.isOpen) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className='flex flex-col'>
|
|
40
|
+
<Input.Root>
|
|
41
|
+
<Input.TextInput
|
|
42
|
+
placeholder='Name'
|
|
43
|
+
value={space.properties.name}
|
|
44
|
+
onChange={(event) => (space.properties.name = event.target.value)}
|
|
45
|
+
/>
|
|
46
|
+
</Input.Root>
|
|
47
|
+
<JsonPanel value={client.toJSON()} />
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const Default = {
|
|
53
|
+
render: () => <ClientRepeater component={ClientStory} count={2} />,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const Space = {
|
|
57
|
+
render: () => <ClientRepeater component={ClientSpace} count={2} createSpace />,
|
|
58
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, { useState, type FC, useEffect } from 'react';
|
|
5
|
+
import React, { useState, type FC, useEffect, useRef } from 'react';
|
|
6
6
|
|
|
7
7
|
import { Client, type PublicKey } from '@dxos/client';
|
|
8
8
|
import { type Space } from '@dxos/client/echo';
|
|
@@ -12,15 +12,13 @@ import { registerSignalFactory } from '@dxos/echo-signals/react';
|
|
|
12
12
|
import { faker } from '@dxos/random';
|
|
13
13
|
import { type MaybePromise } from '@dxos/util';
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { ClientProvider } from '../client';
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
export type ClientRepeatedComponentProps = { id: number; count: number; spaceKey?: PublicKey };
|
|
18
18
|
|
|
19
|
-
export type
|
|
20
|
-
|
|
21
|
-
export type ClientRepeaterProps<P extends RepeatedComponentProps> = {
|
|
22
|
-
component: FC<any>;
|
|
19
|
+
export type ClientRepeaterProps<P extends ClientRepeatedComponentProps> = {
|
|
23
20
|
className?: string;
|
|
21
|
+
component: FC<ClientRepeatedComponentProps>;
|
|
24
22
|
controls?: FC<{ clients: Client[] }>;
|
|
25
23
|
clients?: Client[];
|
|
26
24
|
count?: number;
|
|
@@ -36,12 +34,12 @@ export type ClientRepeaterProps<P extends RepeatedComponentProps> = {
|
|
|
36
34
|
* Utility component for Storybook stories which sets up clients for n peers.
|
|
37
35
|
* The `Component` property is rendered n times, once for each peer.
|
|
38
36
|
*/
|
|
37
|
+
// TODO(burdon): To discuss: evolve ClientRepeater with optional decorator that uses it.
|
|
39
38
|
// NOTE: This is specifically not a storybook decorator because it broke stories as a decorator.
|
|
40
39
|
// This seems primarily due to the fact that it required top-level await for the clients to initialize.
|
|
41
40
|
// Storybook seemed to handle it alright, but Chromatic had a lot of trouble with it.
|
|
42
41
|
// There was also a question of whether or not calling the story function multiple times was a good idea.
|
|
43
|
-
|
|
44
|
-
export const ClientRepeater = <P extends RepeatedComponentProps>(props: ClientRepeaterProps<P>) => {
|
|
42
|
+
export const ClientRepeater = <P extends ClientRepeatedComponentProps>(props: ClientRepeaterProps<P>) => {
|
|
45
43
|
const {
|
|
46
44
|
component: Component,
|
|
47
45
|
controls: Controls,
|
|
@@ -58,9 +56,12 @@ export const ClientRepeater = <P extends RepeatedComponentProps>(props: ClientRe
|
|
|
58
56
|
const [clients, setClients] = useState(props.clients ?? []);
|
|
59
57
|
const [spaceKey, setSpaceKey] = useState<PublicKey>();
|
|
60
58
|
|
|
59
|
+
const testBuilder = useRef(new TestBuilder());
|
|
61
60
|
useEffect(() => {
|
|
62
61
|
const timeout = setTimeout(async () => {
|
|
63
|
-
const clients = [...Array(count)].map(
|
|
62
|
+
const clients = [...Array(count)].map(
|
|
63
|
+
(_) => new Client({ services: testBuilder.current.createLocalClientServices() }),
|
|
64
|
+
);
|
|
64
65
|
await Promise.all(clients.map((client) => client.initialize()));
|
|
65
66
|
types && clients.map((client) => client.addTypes(types));
|
|
66
67
|
|
|
@@ -91,9 +92,9 @@ export const ClientRepeater = <P extends RepeatedComponentProps>(props: ClientRe
|
|
|
91
92
|
<>
|
|
92
93
|
{Controls && <Controls clients={clients} />}
|
|
93
94
|
{clients.map((client, index) => (
|
|
94
|
-
<
|
|
95
|
-
<Component id={index} count={clients.length} {...{ ...props.args
|
|
96
|
-
</
|
|
95
|
+
<ClientProvider key={index} client={client}>
|
|
96
|
+
<Component id={index} count={clients.length} spaceKey={spaceKey} {...{ ...props.args }} />
|
|
97
|
+
</ClientProvider>
|
|
97
98
|
))}
|
|
98
99
|
</>
|
|
99
100
|
);
|
package/src/testing/index.ts
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { Client, type Config, fromHost } from '@dxos/client';
|
|
8
|
+
import { type Space } from '@dxos/client/echo';
|
|
9
|
+
|
|
10
|
+
import { ClientContext, ClientProvider } from '../client';
|
|
11
|
+
|
|
12
|
+
// TODO(burdon): Reconcile with initializeClient.
|
|
13
|
+
export const createClient = async ({
|
|
14
|
+
config,
|
|
15
|
+
createIdentity,
|
|
16
|
+
createSpace,
|
|
17
|
+
}: { config?: Config; createIdentity?: boolean; createSpace?: boolean } = {}) => {
|
|
18
|
+
const client = new Client({ config, services: fromHost(config) });
|
|
19
|
+
await client.initialize();
|
|
20
|
+
if (createIdentity) {
|
|
21
|
+
await client.halo.createIdentity();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let space: Space | undefined;
|
|
25
|
+
if (createSpace) {
|
|
26
|
+
space = await client.spaces.create();
|
|
27
|
+
await client.spaces.isReady.wait();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { client, space };
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const createClientContextProvider = async (client: Client) => {
|
|
34
|
+
return ({ children }: any) => <ClientContext.Provider value={{ client }}>{children}</ClientContext.Provider>;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const createClientProvider = async (client: Client) => {
|
|
38
|
+
return ({ children }: any) => <ClientProvider client={client}>{children}</ClientProvider>;
|
|
39
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxos-theme';
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
import { log } from '@dxos/log';
|
|
10
|
+
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
11
|
+
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
12
|
+
|
|
13
|
+
import { withClientProvider, type WithClientProviderProps, withMultiClientProvider } from './withClientProvider';
|
|
14
|
+
import { useClient } from '../client';
|
|
15
|
+
import { type Space, useSpaces } from '../echo';
|
|
16
|
+
|
|
17
|
+
const SpaceInfo = ({ space }: { space: Space }) => {
|
|
18
|
+
const name = space.isOpen ? space.properties.name : '';
|
|
19
|
+
return <SyntaxHighlighter language='json'>{JSON.stringify({ id: space.id, name }, null, 2)}</SyntaxHighlighter>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const Test = () => {
|
|
23
|
+
const client = useClient();
|
|
24
|
+
const spaces = useSpaces();
|
|
25
|
+
if (!client) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className='flex flex-col min-w-[28rem] divide-y divide-separator border border-separator rounded'>
|
|
31
|
+
<SyntaxHighlighter language='json'>{JSON.stringify(client.toJSON(), null, 2)}</SyntaxHighlighter>
|
|
32
|
+
{spaces.map((space) => (
|
|
33
|
+
<SpaceInfo key={space.id} space={space} />
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default {
|
|
40
|
+
title: 'react-client/withClientProvider',
|
|
41
|
+
render: Test,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const clientProps: WithClientProviderProps = {
|
|
45
|
+
createIdentity: true,
|
|
46
|
+
createSpace: true,
|
|
47
|
+
onInitialized: async (client) => {
|
|
48
|
+
log.info('onInitialized', { client });
|
|
49
|
+
},
|
|
50
|
+
onSpaceCreated: async ({ client, space }) => {
|
|
51
|
+
log.info('onSpaceCreated', { client, space });
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const Default = {
|
|
56
|
+
decorators: [withClientProvider(clientProps), withTheme, withLayout({ classNames: ['flex gap-2'] })],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const Multiple = {
|
|
60
|
+
decorators: [
|
|
61
|
+
withMultiClientProvider({ ...clientProps, numClients: 3 }),
|
|
62
|
+
withTheme,
|
|
63
|
+
withLayout({ classNames: ['flex gap-2'] }),
|
|
64
|
+
],
|
|
65
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Decorator } from '@storybook/react';
|
|
6
|
+
import React, { useRef } from 'react';
|
|
7
|
+
import { type FallbackProps, ErrorBoundary } from 'react-error-boundary';
|
|
8
|
+
|
|
9
|
+
import { Trigger } from '@dxos/async';
|
|
10
|
+
import { type Client } from '@dxos/client';
|
|
11
|
+
import { type Space } from '@dxos/client/echo';
|
|
12
|
+
import { performInvitation, TestBuilder } from '@dxos/client/testing';
|
|
13
|
+
import { type MaybePromise } from '@dxos/util';
|
|
14
|
+
|
|
15
|
+
import { ClientProvider, type ClientProviderProps } from '../client';
|
|
16
|
+
|
|
17
|
+
type InitializeProps = {
|
|
18
|
+
createIdentity?: boolean;
|
|
19
|
+
createSpace?: boolean;
|
|
20
|
+
onSpaceCreated?: (props: { client: Client; space: Space }) => MaybePromise<void>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Initialize client, identity and first space.
|
|
25
|
+
*/
|
|
26
|
+
const initializeClient = async (
|
|
27
|
+
client: Client,
|
|
28
|
+
{
|
|
29
|
+
createIdentity,
|
|
30
|
+
createSpace,
|
|
31
|
+
onSpaceCreated,
|
|
32
|
+
onInitialized,
|
|
33
|
+
}: InitializeProps & Pick<WithClientProviderProps, 'onInitialized'>,
|
|
34
|
+
): Promise<Space | undefined> => {
|
|
35
|
+
if (createIdentity || createSpace) {
|
|
36
|
+
if (!client.halo.identity.get()) {
|
|
37
|
+
await client.halo.createIdentity();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let space: Space | undefined;
|
|
42
|
+
if (createSpace) {
|
|
43
|
+
space = await client.spaces.create({ name: 'Test Space' });
|
|
44
|
+
await onSpaceCreated?.({ client, space });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
await onInitialized?.(client);
|
|
48
|
+
return space;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type WithClientProviderProps = InitializeProps & ClientProviderProps;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Decorator that provides the client context.
|
|
55
|
+
*/
|
|
56
|
+
export const withClientProvider = ({
|
|
57
|
+
createIdentity,
|
|
58
|
+
createSpace,
|
|
59
|
+
onSpaceCreated,
|
|
60
|
+
onInitialized,
|
|
61
|
+
...props
|
|
62
|
+
}: WithClientProviderProps = {}): Decorator => {
|
|
63
|
+
return (Story) => {
|
|
64
|
+
const handleInitialized = async (client: Client) => {
|
|
65
|
+
await initializeClient(client, { createIdentity, createSpace, onSpaceCreated, onInitialized });
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
|
70
|
+
<ClientProvider onInitialized={handleInitialized} {...props}>
|
|
71
|
+
<Story />
|
|
72
|
+
</ClientProvider>
|
|
73
|
+
</ErrorBoundary>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// TODO(burdon): Implement context per client for context.
|
|
79
|
+
// TODO(burdon): Callback once all invitations have completed.
|
|
80
|
+
// TODO(burdon): Delay/jitter for creation of other clients.
|
|
81
|
+
export type WithMultiClientProviderProps = InitializeProps & ClientProviderProps & { numClients?: number };
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Decorator that creates a scaffold for multiple clients.
|
|
85
|
+
* Orchestrates invitations between a randomly selected host and the remaining clients.
|
|
86
|
+
* NOTE: Should come before withLayout.
|
|
87
|
+
*/
|
|
88
|
+
export const withMultiClientProvider = ({
|
|
89
|
+
numClients = 2,
|
|
90
|
+
createIdentity,
|
|
91
|
+
createSpace,
|
|
92
|
+
onSpaceCreated,
|
|
93
|
+
onInitialized,
|
|
94
|
+
...props
|
|
95
|
+
}: WithMultiClientProviderProps): Decorator => {
|
|
96
|
+
return (Story) => {
|
|
97
|
+
const builder = useRef(new TestBuilder());
|
|
98
|
+
const hostRef = useRef<Client>();
|
|
99
|
+
const spaceReady = useRef(new Trigger<Space | undefined>());
|
|
100
|
+
|
|
101
|
+
// Handle invitations.
|
|
102
|
+
const handleInitialized = async (client: Client) => {
|
|
103
|
+
if (createSpace) {
|
|
104
|
+
if (!hostRef.current) {
|
|
105
|
+
hostRef.current = client;
|
|
106
|
+
const space = await initializeClient(client, { createIdentity, createSpace, onSpaceCreated, onInitialized });
|
|
107
|
+
spaceReady.current.wake(space);
|
|
108
|
+
} else {
|
|
109
|
+
await initializeClient(client, { createIdentity, onInitialized });
|
|
110
|
+
const space = await spaceReady.current.wait();
|
|
111
|
+
if (space) {
|
|
112
|
+
await Promise.all(performInvitation({ host: space, guest: client.spaces }));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<>
|
|
120
|
+
{Array.from({ length: numClients }).map((_, index) => (
|
|
121
|
+
<ErrorBoundary key={index} FallbackComponent={ErrorFallback}>
|
|
122
|
+
<ClientProvider
|
|
123
|
+
services={builder.current.createLocalClientServices()}
|
|
124
|
+
onInitialized={handleInitialized}
|
|
125
|
+
{...props}
|
|
126
|
+
>
|
|
127
|
+
<Story />
|
|
128
|
+
</ClientProvider>
|
|
129
|
+
</ErrorBoundary>
|
|
130
|
+
))}
|
|
131
|
+
</>
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const ErrorFallback = ({ error }: FallbackProps) => {
|
|
137
|
+
const { name, message, stack } =
|
|
138
|
+
error instanceof Error ? error : { name: 'Error', message: String(error), stack: undefined };
|
|
139
|
+
return (
|
|
140
|
+
<div role='alert' className='flex flex-col overflow-hidden p-4 gap-4'>
|
|
141
|
+
<h1 className='text-xl text-red-500'>{name}</h1>
|
|
142
|
+
<div className='text-lg'>{message}</div>
|
|
143
|
+
{stack && <pre className='whitespace-pre-wrap text-sm text-subdued'>{stack}</pre>}
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
};
|
package/tsconfig.json
CHANGED
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
"DOM",
|
|
7
7
|
"ESNext"
|
|
8
8
|
],
|
|
9
|
-
"module": "ESNext"
|
|
10
|
-
"outDir": "dist/types"
|
|
9
|
+
"module": "ESNext"
|
|
11
10
|
},
|
|
12
11
|
"include": [
|
|
13
12
|
"playwright",
|
|
@@ -36,7 +35,7 @@
|
|
|
36
35
|
"path": "../../common/random"
|
|
37
36
|
},
|
|
38
37
|
{
|
|
39
|
-
"path": "../../common/
|
|
38
|
+
"path": "../../common/test-utils"
|
|
40
39
|
},
|
|
41
40
|
{
|
|
42
41
|
"path": "../../common/util"
|
|
@@ -47,9 +46,18 @@
|
|
|
47
46
|
{
|
|
48
47
|
"path": "../../core/echo/echo-signals"
|
|
49
48
|
},
|
|
49
|
+
{
|
|
50
|
+
"path": "../../core/protocols"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"path": "../../ui/primitives/react-hooks"
|
|
54
|
+
},
|
|
50
55
|
{
|
|
51
56
|
"path": "../../ui/react-ui"
|
|
52
57
|
},
|
|
58
|
+
{
|
|
59
|
+
"path": "../../ui/react-ui-syntax-highlighter"
|
|
60
|
+
},
|
|
53
61
|
{
|
|
54
62
|
"path": "../../ui/react-ui-theme"
|
|
55
63
|
},
|