Skip to content

Commit af5f5f4

Browse files
authored
fix: Fix useDeferredApi export (#1742)
- It wasn't actually being exported - Also add a DeferredApiBootstrap component to gate on loading the API
1 parent 3312133 commit af5f5f4

3 files changed

Lines changed: 89 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react';
2+
import { act, render } from '@testing-library/react';
3+
import type { dh as DhType } from '@deephaven/jsapi-types';
4+
import { TestUtils } from '@deephaven/utils';
5+
import DeferredApiBootstrap from './DeferredApiBootstrap';
6+
import { DeferredApiContext } from './useDeferredApi';
7+
8+
it('should call the error callback if no API provider wrapped', () => {
9+
const onError = jest.fn();
10+
render(<DeferredApiBootstrap onError={onError} />);
11+
expect(onError).toHaveBeenCalled();
12+
});
13+
14+
it('renders children if the API is loaded', () => {
15+
const api = TestUtils.createMockProxy<DhType>();
16+
const { queryByText } = render(
17+
<DeferredApiContext.Provider value={api}>
18+
<DeferredApiBootstrap>
19+
<div>Child</div>
20+
</DeferredApiBootstrap>
21+
</DeferredApiContext.Provider>
22+
);
23+
expect(queryByText('Child')).not.toBeNull();
24+
});
25+
26+
it('waits to render children until the API is loaded', async () => {
27+
let resolveApi: (api: DhType) => void;
28+
const apiPromise = new Promise<DhType>(resolve => {
29+
resolveApi = resolve;
30+
});
31+
const deferredApi = jest.fn(() => apiPromise);
32+
const options = { foo: 'bar' };
33+
const { queryByText } = render(
34+
<DeferredApiContext.Provider value={deferredApi}>
35+
<DeferredApiBootstrap options={options}>
36+
<div>Child</div>
37+
</DeferredApiBootstrap>
38+
</DeferredApiContext.Provider>
39+
);
40+
expect(queryByText('Child')).toBeNull();
41+
expect(deferredApi).toHaveBeenCalledTimes(1);
42+
expect(deferredApi).toHaveBeenCalledWith(options);
43+
44+
const api = TestUtils.createMockProxy<DhType>();
45+
await act(async () => {
46+
resolveApi(api);
47+
await apiPromise;
48+
});
49+
expect(queryByText('Child')).not.toBeNull();
50+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from 'react';
2+
import useDeferredApi from './useDeferredApi';
3+
import { ApiContext } from './ApiBootstrap';
4+
5+
type DeferredApiBootstrapProps = React.PropsWithChildren<{
6+
onError?: (error: unknown) => void;
7+
/**
8+
* Options to use when fetching the deferred API.
9+
*/
10+
options?: Record<string, unknown>;
11+
}>;
12+
13+
/**
14+
* Does not render children until the deferred API is resolved.
15+
*/
16+
export const DeferredApiBootstrap = React.memo(
17+
({
18+
children,
19+
onError,
20+
options,
21+
}: DeferredApiBootstrapProps): JSX.Element | null => {
22+
const [api, apiError] = useDeferredApi(options);
23+
if (apiError != null) {
24+
onError?.(apiError);
25+
return null;
26+
}
27+
if (api == null) {
28+
// Still waiting for the API to load
29+
return null;
30+
}
31+
return <ApiContext.Provider value={api}>{children}</ApiContext.Provider>;
32+
}
33+
);
34+
35+
DeferredApiBootstrap.displayName = 'DeferredApiBootstrap';
36+
37+
export default DeferredApiBootstrap;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export * from './ApiBootstrap';
22
export * from './ClientBootstrap';
3+
export * from './DeferredApiBootstrap';
34
export * from './useApi';
45
export * from './useClient';
6+
export * from './useDeferredApi';

0 commit comments

Comments
 (0)