@dxos/react-client 0.6.14-main.7bd9c89 → 0.6.14-main.f49f251
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/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +61 -11
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +60 -9
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +61 -11
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/testing/withClientProvider.d.ts +3 -0
- package/dist/types/src/testing/withClientProvider.d.ts.map +1 -1
- package/package.json +40 -63
- package/src/playwright/smoke.spec.ts +1 -1
- package/src/testing/withClientProvider.tsx +37 -10
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Decorator } from '@storybook/react';
|
|
6
|
-
import React, { useRef } from 'react';
|
|
7
|
-
import { type FallbackProps, ErrorBoundary } from 'react-error-boundary';
|
|
6
|
+
import React, { createContext, type PropsWithChildren, useContext, useEffect, useRef, useState } from 'react';
|
|
7
|
+
import { type FallbackProps, ErrorBoundary as NativeErrorBoundary } from 'react-error-boundary';
|
|
8
8
|
|
|
9
9
|
import { Trigger } from '@dxos/async';
|
|
10
10
|
import { type Client } from '@dxos/client';
|
|
11
11
|
import { type Space } from '@dxos/client/echo';
|
|
12
12
|
import { performInvitation, TestBuilder } from '@dxos/client/testing';
|
|
13
|
+
import { log } from '@dxos/log';
|
|
13
14
|
import { type MaybePromise } from '@dxos/util';
|
|
14
15
|
|
|
15
16
|
import { ClientProvider, type ClientProviderProps } from '../client';
|
|
@@ -66,7 +67,7 @@ export const withClientProvider = ({
|
|
|
66
67
|
};
|
|
67
68
|
|
|
68
69
|
return (
|
|
69
|
-
<ErrorBoundary
|
|
70
|
+
<ErrorBoundary>
|
|
70
71
|
<ClientProvider onInitialized={handleInitialized} {...props}>
|
|
71
72
|
<Story />
|
|
72
73
|
</ClientProvider>
|
|
@@ -80,6 +81,10 @@ export const withClientProvider = ({
|
|
|
80
81
|
// TODO(burdon): Delay/jitter for creation of other clients.
|
|
81
82
|
export type WithMultiClientProviderProps = InitializeProps & ClientProviderProps & { numClients?: number };
|
|
82
83
|
|
|
84
|
+
const MultiClientContext = createContext<{ id: number }>({ id: 0 });
|
|
85
|
+
|
|
86
|
+
export const useMultiClient = () => useContext(MultiClientContext);
|
|
87
|
+
|
|
83
88
|
/**
|
|
84
89
|
* Decorator that creates a scaffold for multiple clients.
|
|
85
90
|
* Orchestrates invitations between a randomly selected host and the remaining clients.
|
|
@@ -99,16 +104,20 @@ export const withMultiClientProvider = ({
|
|
|
99
104
|
const spaceReady = useRef(new Trigger<Space | undefined>());
|
|
100
105
|
|
|
101
106
|
// Handle invitations.
|
|
102
|
-
|
|
107
|
+
// NOTE: The zeroth client isn't necessarily the first to be initialized.
|
|
108
|
+
const handleInitialized = async (client: Client, index: number) => {
|
|
109
|
+
log.info('initialized', { index });
|
|
103
110
|
if (createSpace) {
|
|
104
111
|
if (!hostRef.current) {
|
|
105
112
|
hostRef.current = client;
|
|
106
113
|
const space = await initializeClient(client, { createIdentity, createSpace, onSpaceCreated, onInitialized });
|
|
107
114
|
spaceReady.current.wake(space);
|
|
115
|
+
log.info('inviting', { index });
|
|
108
116
|
} else {
|
|
109
117
|
await initializeClient(client, { createIdentity, onInitialized });
|
|
110
118
|
const space = await spaceReady.current.wait();
|
|
111
119
|
if (space) {
|
|
120
|
+
log.info('joining', { index });
|
|
112
121
|
await Promise.all(performInvitation({ host: space, guest: client.spaces }));
|
|
113
122
|
}
|
|
114
123
|
}
|
|
@@ -116,28 +125,46 @@ export const withMultiClientProvider = ({
|
|
|
116
125
|
};
|
|
117
126
|
|
|
118
127
|
return (
|
|
119
|
-
|
|
128
|
+
<ErrorBoundary>
|
|
120
129
|
{Array.from({ length: numClients }).map((_, index) => (
|
|
121
|
-
<
|
|
130
|
+
<MultiClientContext.Provider key={index} value={{ id: index }}>
|
|
122
131
|
<ClientProvider
|
|
123
132
|
services={builder.current.createLocalClientServices()}
|
|
124
|
-
onInitialized={handleInitialized}
|
|
133
|
+
onInitialized={(client) => handleInitialized(client, index)}
|
|
125
134
|
{...props}
|
|
126
135
|
>
|
|
127
136
|
<Story />
|
|
128
137
|
</ClientProvider>
|
|
129
|
-
</
|
|
138
|
+
</MultiClientContext.Provider>
|
|
130
139
|
))}
|
|
131
|
-
|
|
140
|
+
</ErrorBoundary>
|
|
132
141
|
);
|
|
133
142
|
};
|
|
134
143
|
};
|
|
135
144
|
|
|
145
|
+
const ErrorBoundary = ({ children }: PropsWithChildren) => {
|
|
146
|
+
const [error, setError] = useState<Error>();
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
const handleError = (event: PromiseRejectionEvent) => {
|
|
149
|
+
setError(event.reason);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
window.addEventListener('unhandledrejection', handleError);
|
|
153
|
+
return () => window.removeEventListener('unhandledrejection', handleError);
|
|
154
|
+
}, []);
|
|
155
|
+
|
|
156
|
+
if (error) {
|
|
157
|
+
return <ErrorFallback error={error} resetErrorBoundary={() => setError(undefined)} />;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return <NativeErrorBoundary FallbackComponent={ErrorFallback}>{children}</NativeErrorBoundary>;
|
|
161
|
+
};
|
|
162
|
+
|
|
136
163
|
const ErrorFallback = ({ error }: FallbackProps) => {
|
|
137
164
|
const { name, message, stack } =
|
|
138
165
|
error instanceof Error ? error : { name: 'Error', message: String(error), stack: undefined };
|
|
139
166
|
return (
|
|
140
|
-
<div role='alert' className='flex flex-col
|
|
167
|
+
<div role='alert' className='flex flex-col p-4 gap-4 overflow-auto'>
|
|
141
168
|
<h1 className='text-xl text-red-500'>{name}</h1>
|
|
142
169
|
<div className='text-lg'>{message}</div>
|
|
143
170
|
{stack && <pre className='whitespace-pre-wrap text-sm text-subdued'>{stack}</pre>}
|