@squiz/resource-browser 3.0.1-rc.6 → 3.0.1-rc.7
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/CHANGELOG.md +4 -0
- package/lib/Plugin/Plugin.d.ts +1 -0
- package/lib/ResourceBrowserInlineButton/InlineLoadingErrorState/InlineLoadingErrorState.d.ts +11 -0
- package/lib/ResourceBrowserInlineButton/InlineLoadingErrorState/InlineLoadingErrorState.js +22 -0
- package/lib/ResourceBrowserInlineButton/ResourceBrowserInlineButton.d.ts +2 -1
- package/lib/ResourceBrowserInlineButton/ResourceBrowserInlineButton.js +7 -3
- package/lib/index.css +5797 -743
- package/lib/index.js +7 -3
- package/package.json +4 -3
- package/src/Plugin/Plugin.spec.tsx +2 -0
- package/src/Plugin/Plugin.tsx +1 -0
- package/src/ResourceBrowserInlineButton/InlineLoadingErrorState/InlineLoadingErrorState.spec.tsx +47 -0
- package/src/ResourceBrowserInlineButton/InlineLoadingErrorState/InlineLoadingErrorState.tsx +54 -0
- package/src/ResourceBrowserInlineButton/ResourceBrowserInlineButton.spec.tsx +46 -1
- package/src/ResourceBrowserInlineButton/ResourceBrowserInlineButton.tsx +33 -15
- package/src/__mocks__/StorybookHelpers.tsx +12 -4
- package/src/index.scss +2 -0
- package/src/index.stories.tsx +22 -2
- package/src/index.tsx +9 -2
package/lib/index.js
CHANGED
@@ -54,7 +54,7 @@ const ResourceBrowser = (props) => {
|
|
54
54
|
const [isModalOpen, setIsModalOpen] = (0, react_1.useState)(false);
|
55
55
|
const [source, setSource] = (0, react_1.useState)(null);
|
56
56
|
const [mode, setMode] = (0, react_1.useState)(null);
|
57
|
-
const { data: sources, isLoading, error: sourcesError } = (0, useSources_1.useSources)({ onRequestSources, plugins });
|
57
|
+
const { data: sources, isLoading, error: sourcesError, reload: reloadSources } = (0, useSources_1.useSources)({ onRequestSources, plugins });
|
58
58
|
const [plugin, setPlugin] = (0, react_1.useState)(null);
|
59
59
|
// MainContainer will render a list of sources of one is not provided to it, callback to allow it to set the source once a user selects
|
60
60
|
const handleSourceSelect = (0, react_1.useCallback)((source, mode) => {
|
@@ -103,6 +103,10 @@ const ResourceBrowser = (props) => {
|
|
103
103
|
setMode(null);
|
104
104
|
}
|
105
105
|
}, [sources, isModalOpen]);
|
106
|
+
// Reset function to allow user manual reload if sources fail in inline usage
|
107
|
+
const handleReset = (0, react_1.useCallback)(() => {
|
108
|
+
reloadSources();
|
109
|
+
}, [reloadSources]);
|
106
110
|
// Render a default "plugin" and one for each item in the plugins array. They are conditionally rendered based on what is selected
|
107
111
|
return (react_1.default.createElement("div", { className: "squiz-rb-scope" },
|
108
112
|
react_1.default.createElement(Plugin_1.PluginRender, { key: "default", render: plugin === null, inline: !!inline, inlineType: inlineType, ...props, source: source, sources: sources, setSource: handleSourceSelect, isLoading: isLoading, error: sourcesError || error, plugin: plugin, pluginMode: mode, searchEnabled: searchEnabled, useResource: () => {
|
@@ -111,9 +115,9 @@ const ResourceBrowser = (props) => {
|
|
111
115
|
error: null,
|
112
116
|
isLoading: false,
|
113
117
|
};
|
114
|
-
}, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange }),
|
118
|
+
}, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange, onRetry: handleReset }),
|
115
119
|
plugins.map((thisPlugin) => {
|
116
|
-
return (react_1.default.createElement(Plugin_1.PluginRender, { key: thisPlugin.type, render: thisPlugin === plugin, inline: !!inline, inlineType: inlineType, ...props, source: source, sources: sources, setSource: handleSourceSelect, isLoading: isLoading, error: error, plugin: plugin, pluginMode: mode, searchEnabled: searchEnabled, useResource: thisPlugin.useResolveResource, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange }));
|
120
|
+
return (react_1.default.createElement(Plugin_1.PluginRender, { key: thisPlugin.type, render: thisPlugin === plugin, inline: !!inline, inlineType: inlineType, ...props, source: source, sources: sources, setSource: handleSourceSelect, isLoading: isLoading, error: sourcesError || error, plugin: plugin, pluginMode: mode, searchEnabled: searchEnabled, useResource: thisPlugin.useResolveResource, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange, onRetry: handleReset }));
|
117
121
|
})));
|
118
122
|
};
|
119
123
|
exports.ResourceBrowser = ResourceBrowser;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@squiz/resource-browser",
|
3
|
-
"version": "3.0.1-rc.
|
3
|
+
"version": "3.0.1-rc.7",
|
4
4
|
"main": "lib/index.js",
|
5
5
|
"types": "lib/index.d.ts",
|
6
6
|
"private": false,
|
@@ -28,7 +28,8 @@
|
|
28
28
|
"@react-types/shared": "^3.23.1",
|
29
29
|
"@squiz/dx-json-schema-lib": "^1.67.0",
|
30
30
|
"@squiz/generic-browser-lib": "1.67.2",
|
31
|
-
"@squiz/resource-browser-ui-lib": "^1.0.0-rc.
|
31
|
+
"@squiz/resource-browser-ui-lib": "^1.0.0-rc.6",
|
32
|
+
"@squiz/sds": "^1.3.1",
|
32
33
|
"clsx": "^2.1.0",
|
33
34
|
"expiry-map": "^2.0.0",
|
34
35
|
"p-memoize": "^4.0.4",
|
@@ -88,5 +89,5 @@
|
|
88
89
|
"volta": {
|
89
90
|
"node": "18.18.0"
|
90
91
|
},
|
91
|
-
"gitHead": "
|
92
|
+
"gitHead": "b1ca30987b9284691e8d455792e65f13a32fdceb"
|
92
93
|
}
|
@@ -40,6 +40,7 @@ describe('Plugin', () => {
|
|
40
40
|
setSource: () => {},
|
41
41
|
isModalOpen: false,
|
42
42
|
onModalStateChange: () => {},
|
43
|
+
onRetry: () => {},
|
43
44
|
};
|
44
45
|
render(<PluginRender render={true} inline={false} {...props} />);
|
45
46
|
|
@@ -71,6 +72,7 @@ describe('Plugin', () => {
|
|
71
72
|
setSource: () => {},
|
72
73
|
isModalOpen: false,
|
73
74
|
onModalStateChange: () => {},
|
75
|
+
onRetry: () => {},
|
74
76
|
};
|
75
77
|
render(<PluginRender render={true} inline={true} inlineType="image" {...props} />);
|
76
78
|
|
package/src/Plugin/Plugin.tsx
CHANGED
@@ -15,6 +15,7 @@ export type PluginRenderType = ResourceBrowserInputProps & {
|
|
15
15
|
render: boolean;
|
16
16
|
inline: boolean;
|
17
17
|
inlineType?: InlineType;
|
18
|
+
onRetry: () => void;
|
18
19
|
};
|
19
20
|
export const PluginRender = ({ render, inline, inlineType, ...props }: PluginRenderType) => {
|
20
21
|
if (render) {
|
package/src/ResourceBrowserInlineButton/InlineLoadingErrorState/InlineLoadingErrorState.spec.tsx
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
import React, { act } from 'react';
|
2
|
+
import { render, waitFor, screen, fireEvent } from '@testing-library/react';
|
3
|
+
import { Error } from '@squiz/resource-browser-ui-lib';
|
4
|
+
import { InlineLoadingErrorState } from './InlineLoadingErrorState';
|
5
|
+
|
6
|
+
jest.mock('@squiz/resource-browser-ui-lib', () => {
|
7
|
+
return {
|
8
|
+
...jest.requireActual('@squiz/resource-browser-ui-lib'),
|
9
|
+
Error: jest.fn(() => <div></div>),
|
10
|
+
};
|
11
|
+
});
|
12
|
+
|
13
|
+
describe('InlineLoadingErrorState', () => {
|
14
|
+
const defaultProps = {
|
15
|
+
isLoading: false,
|
16
|
+
error: undefined,
|
17
|
+
};
|
18
|
+
|
19
|
+
it('clicking close button should call the onClose callback', async () => {
|
20
|
+
const onClose = jest.fn();
|
21
|
+
|
22
|
+
//@ts-ignore
|
23
|
+
render(<InlineLoadingErrorState {...defaultProps} title="Select Asset" onClose={onClose} />);
|
24
|
+
|
25
|
+
await act(() => fireEvent.click(screen.getByRole('button', { name: 'Close Select Asset dialog' })));
|
26
|
+
expect(onClose).toHaveBeenCalled();
|
27
|
+
});
|
28
|
+
|
29
|
+
it('Will render loading on isLoading === true', async () => {
|
30
|
+
//@ts-ignore
|
31
|
+
render(<InlineLoadingErrorState {...defaultProps} isLoading={true} />);
|
32
|
+
|
33
|
+
await waitFor(() => {
|
34
|
+
expect(screen.getByText('Loading...')).toBeTruthy();
|
35
|
+
});
|
36
|
+
});
|
37
|
+
|
38
|
+
it('Will render error on error !== null', async () => {
|
39
|
+
const onRetry = jest.fn();
|
40
|
+
//@ts-ignore
|
41
|
+
render(<InlineLoadingErrorState {...defaultProps} onRetry={onRetry} error={new Error('test')} />);
|
42
|
+
|
43
|
+
await waitFor(() => {
|
44
|
+
expect(Error).toHaveBeenCalledWith(expect.objectContaining({ onReset: onRetry }), {});
|
45
|
+
});
|
46
|
+
});
|
47
|
+
});
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { DOMAttributes, FocusableElement } from '@react-types/shared';
|
3
|
+
import { Error } from '@squiz/resource-browser-ui-lib';
|
4
|
+
import { Spinner } from '@squiz/sds';
|
5
|
+
|
6
|
+
export type InlineLoadingErrorStateProps = {
|
7
|
+
title: string;
|
8
|
+
titleAriaProps: DOMAttributes<FocusableElement>;
|
9
|
+
onClose: () => void;
|
10
|
+
onRetry: () => void;
|
11
|
+
|
12
|
+
isLoading: boolean;
|
13
|
+
error: Error | null;
|
14
|
+
};
|
15
|
+
|
16
|
+
export const InlineLoadingErrorState = ({ title, titleAriaProps, onClose, onRetry, isLoading, error }: InlineLoadingErrorStateProps) => {
|
17
|
+
return (
|
18
|
+
<div className="relative flex flex-col h-full text-gray-800">
|
19
|
+
<div className="flex items-center py-3 pl-6 pr-10 min-h-[68px]">
|
20
|
+
<h2 {...titleAriaProps} className="text-xl leading-6 text-gray-800 font-semibold mr-6">
|
21
|
+
{title}
|
22
|
+
</h2>
|
23
|
+
<button
|
24
|
+
type="button"
|
25
|
+
aria-label={`Close ${title} dialog`}
|
26
|
+
onClick={onClose}
|
27
|
+
className="absolute top-2 right-2 p-2.5 rounded hover:bg-blue-100 focus:bg-blue-100"
|
28
|
+
>
|
29
|
+
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
30
|
+
<path
|
31
|
+
d="M13.3 0.710017C13.1131 0.522765 12.8595 0.417532 12.595 0.417532C12.3305 0.417532 12.0768 0.522765 11.89 0.710017L6.99997 5.59002L2.10997 0.700017C1.92314 0.512765 1.66949 0.407532 1.40497 0.407532C1.14045 0.407532 0.886802 0.512765 0.699971 0.700017C0.309971 1.09002 0.309971 1.72002 0.699971 2.11002L5.58997 7.00002L0.699971 11.89C0.309971 12.28 0.309971 12.91 0.699971 13.3C1.08997 13.69 1.71997 13.69 2.10997 13.3L6.99997 8.41002L11.89 13.3C12.28 13.69 12.91 13.69 13.3 13.3C13.69 12.91 13.69 12.28 13.3 11.89L8.40997 7.00002L13.3 2.11002C13.68 1.73002 13.68 1.09002 13.3 0.710017Z"
|
32
|
+
fill="currentColor"
|
33
|
+
/>
|
34
|
+
</svg>
|
35
|
+
</button>
|
36
|
+
</div>
|
37
|
+
<div className="border-t border-gray-300 overflow-y-hidden">
|
38
|
+
<div className="w-screen max-w-[400px] min-h-[320px] flex-1 grow-[3] border-r border-gray-300 bg-gray-100 pl-6 pr-6 pb-6 pt-4 flex justify-center items-center text-gray-500">
|
39
|
+
{isLoading && <Spinner type="lg" />}
|
40
|
+
|
41
|
+
{!isLoading && error && (
|
42
|
+
<Error
|
43
|
+
onReset={onRetry}
|
44
|
+
infoText={
|
45
|
+
'There was a problem loading this feature. If this problem persists, contact your administrator or support team.'
|
46
|
+
}
|
47
|
+
buttonText={'Try again'}
|
48
|
+
/>
|
49
|
+
)}
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
);
|
54
|
+
};
|
@@ -1,10 +1,15 @@
|
|
1
1
|
import React from 'react';
|
2
|
-
import { render, waitFor, screen } from '@testing-library/react';
|
2
|
+
import { render, waitFor, screen, fireEvent } from '@testing-library/react';
|
3
3
|
|
4
4
|
import { ResourceBrowserInlineButton } from './ResourceBrowserInlineButton';
|
5
5
|
|
6
|
+
import * as ILES from './InlineLoadingErrorState/InlineLoadingErrorState';
|
7
|
+
jest.spyOn(ILES, 'InlineLoadingErrorState');
|
8
|
+
|
6
9
|
describe('ResourceBrowserInlineButton', () => {
|
7
10
|
const defaultProps = {
|
11
|
+
isLoading: false,
|
12
|
+
error: undefined,
|
8
13
|
useResource: () => {
|
9
14
|
return {
|
10
15
|
data: null,
|
@@ -30,4 +35,44 @@ describe('ResourceBrowserInlineButton', () => {
|
|
30
35
|
expect(screen.getByLabelText('change link')).toBeTruthy();
|
31
36
|
});
|
32
37
|
});
|
38
|
+
|
39
|
+
it('On trigger press render loading if still awaiting external data', async () => {
|
40
|
+
//@ts-ignore
|
41
|
+
render(<ResourceBrowserInlineButton {...defaultProps} inlineType={'image'} isLoading={true} />);
|
42
|
+
|
43
|
+
const button = screen.getByRole('button');
|
44
|
+
fireEvent.click(button);
|
45
|
+
|
46
|
+
await waitFor(() => {
|
47
|
+
expect(ILES.InlineLoadingErrorState).toHaveBeenCalledWith(expect.objectContaining({ isLoading: true }), {});
|
48
|
+
});
|
49
|
+
});
|
50
|
+
|
51
|
+
it('On trigger press render error if external error provided', async () => {
|
52
|
+
const error = new Error('test');
|
53
|
+
//@ts-ignore
|
54
|
+
render(<ResourceBrowserInlineButton {...defaultProps} inlineType={'image'} error={error} />);
|
55
|
+
|
56
|
+
const button = screen.getByRole('button');
|
57
|
+
fireEvent.click(button);
|
58
|
+
|
59
|
+
await waitFor(() => {
|
60
|
+
expect(ILES.InlineLoadingErrorState).toHaveBeenCalledWith(expect.objectContaining({ error }), {});
|
61
|
+
});
|
62
|
+
});
|
63
|
+
|
64
|
+
it('On trigger press render loading if still awaiting resource data', async () => {
|
65
|
+
const useResource = () => {
|
66
|
+
return { data: null, isLoading: true };
|
67
|
+
};
|
68
|
+
//@ts-ignore
|
69
|
+
render(<ResourceBrowserInlineButton {...defaultProps} inlineType={'image'} useResource={useResource} />);
|
70
|
+
|
71
|
+
const button = screen.getByRole('button');
|
72
|
+
fireEvent.click(button);
|
73
|
+
|
74
|
+
await waitFor(() => {
|
75
|
+
expect(ILES.InlineLoadingErrorState).toHaveBeenCalledWith(expect.objectContaining({ isLoading: true }), {});
|
76
|
+
});
|
77
|
+
});
|
33
78
|
});
|
@@ -2,12 +2,14 @@ import React from 'react';
|
|
2
2
|
import MainContainer from '../MainContainer/MainContainer';
|
3
3
|
import { ResourceBrowserInputProps } from '../ResourceBrowserInput/ResourceBrowserInput';
|
4
4
|
import { ModalTrigger } from '@squiz/resource-browser-ui-lib';
|
5
|
+
import { InlineLoadingErrorState } from './InlineLoadingErrorState/InlineLoadingErrorState';
|
5
6
|
import { ImageInline } from '../Icons/ImageInline';
|
6
7
|
import { LinkInline } from '../Icons/LinkInline';
|
7
8
|
import { InlineType } from '../types';
|
8
9
|
|
9
10
|
export type ResourceBrowserInlineButtonProps = ResourceBrowserInputProps & {
|
10
11
|
inlineType: InlineType;
|
12
|
+
onRetry: () => void;
|
11
13
|
};
|
12
14
|
|
13
15
|
export const ResourceBrowserInlineButton = ({
|
@@ -15,6 +17,7 @@ export const ResourceBrowserInlineButton = ({
|
|
15
17
|
modalTitle,
|
16
18
|
allowedTypes,
|
17
19
|
onChange,
|
20
|
+
onRetry,
|
18
21
|
value,
|
19
22
|
useResource,
|
20
23
|
isDisabled,
|
@@ -29,7 +32,8 @@ export const ResourceBrowserInlineButton = ({
|
|
29
32
|
isModalOpen,
|
30
33
|
onModalStateChange,
|
31
34
|
}: ResourceBrowserInlineButtonProps) => {
|
32
|
-
|
35
|
+
// If an error happens loading the resource the inline browser just opens without it as if no preselectedResource is provided
|
36
|
+
const { data: resource, isLoading: isResourceLoading } = useResource(value?.resourceId || null, source);
|
33
37
|
|
34
38
|
const isImagePicker = inlineType === 'image';
|
35
39
|
|
@@ -48,20 +52,34 @@ export const ResourceBrowserInlineButton = ({
|
|
48
52
|
containerClasses="inline-launch-button__button"
|
49
53
|
>
|
50
54
|
{(onClose, titleProps) => (
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
55
|
+
<>
|
56
|
+
{(isLoading || isResourceLoading || error) && (
|
57
|
+
<InlineLoadingErrorState
|
58
|
+
title={modalTitle}
|
59
|
+
titleAriaProps={titleProps}
|
60
|
+
onClose={onClose}
|
61
|
+
onRetry={onRetry}
|
62
|
+
isLoading={isLoading || isResourceLoading}
|
63
|
+
error={error}
|
64
|
+
/>
|
65
|
+
)}
|
66
|
+
{!(isLoading || isResourceLoading) && !error && (
|
67
|
+
<MainContainer
|
68
|
+
selectedSource={source}
|
69
|
+
sources={sources}
|
70
|
+
preselectedResource={resource}
|
71
|
+
plugin={plugin}
|
72
|
+
pluginMode={pluginMode}
|
73
|
+
searchEnabled={searchEnabled}
|
74
|
+
title={modalTitle}
|
75
|
+
titleAriaProps={titleProps}
|
76
|
+
allowedTypes={allowedTypes}
|
77
|
+
onSourceSelect={setSource}
|
78
|
+
onClose={onClose}
|
79
|
+
onChange={onChange}
|
80
|
+
/>
|
81
|
+
)}
|
82
|
+
</>
|
65
83
|
)}
|
66
84
|
</ModalTrigger>
|
67
85
|
<div className="inline-launch-button__label">{isImagePicker ? `Change image` : `Change link`}</div>
|
@@ -11,7 +11,7 @@ type CreateCallbacksProps = Partial<{
|
|
11
11
|
error?: string;
|
12
12
|
}>;
|
13
13
|
|
14
|
-
export const createPlugins = (callbackWait: number, headerPortal = false): ResourceBrowserPlugin[] => {
|
14
|
+
export const createPlugins = (callbackWait: number, headerPortal = false, resourceError?: boolean): ResourceBrowserPlugin[] => {
|
15
15
|
const types = ['dam', 'matrix'];
|
16
16
|
return types.map((type): ResourceBrowserPlugin => {
|
17
17
|
return {
|
@@ -20,6 +20,7 @@ export const createPlugins = (callbackWait: number, headerPortal = false): Resou
|
|
20
20
|
createHeaderPortal: headerPortal,
|
21
21
|
sourceBrowserComponent: () => {
|
22
22
|
return (props) => {
|
23
|
+
console.log('sourceBrowserComponent invoked with', props);
|
23
24
|
return (
|
24
25
|
<div className="h-screen lg:h-[calc(100vh-9rem)] w-screen max-w-[52rem]">
|
25
26
|
<div>THIS IS A {type} BROWSE PLUGIN</div>
|
@@ -40,6 +41,7 @@ export const createPlugins = (callbackWait: number, headerPortal = false): Resou
|
|
40
41
|
},
|
41
42
|
sourceSearchComponent: () => {
|
42
43
|
return (props) => {
|
44
|
+
console.log('sourceSearchComponent invoked with', props);
|
43
45
|
return (
|
44
46
|
<div className="h-screen lg:h-[calc(100vh-9rem)] w-screen max-w-[52rem]">
|
45
47
|
<div>THIS IS A {type} SEARCH PLUGIN</div>
|
@@ -108,9 +110,14 @@ export const createPlugins = (callbackWait: number, headerPortal = false): Resou
|
|
108
110
|
},
|
109
111
|
useResolveResource: (unresolvedResource) => {
|
110
112
|
const [resource, setResource] = useState<ResourceBrowserResource | null>(null);
|
111
|
-
const [error, setError] = useState<Error | null>(
|
113
|
+
const [error, setError] = useState<Error | null>(
|
114
|
+
resourceError ? new Error(`Error loading: ${JSON.stringify(unresolvedResource)}`) : null,
|
115
|
+
);
|
112
116
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
113
117
|
|
118
|
+
if (resourceError) {
|
119
|
+
console.log('useResolveResource has returned an error');
|
120
|
+
}
|
114
121
|
useEffect(() => {
|
115
122
|
if (unresolvedResource !== null) {
|
116
123
|
setTimeout(() => {
|
@@ -144,7 +151,7 @@ export const createPlugins = (callbackWait: number, headerPortal = false): Resou
|
|
144
151
|
}, [unresolvedResource, setResource, setIsLoading]);
|
145
152
|
|
146
153
|
return {
|
147
|
-
data: resource,
|
154
|
+
data: !error ? resource : null,
|
148
155
|
error: error,
|
149
156
|
isLoading: isLoading,
|
150
157
|
};
|
@@ -161,10 +168,11 @@ export const createResourceBrowserCallbacks = ({
|
|
161
168
|
}: CreateCallbacksProps = {}) => {
|
162
169
|
return {
|
163
170
|
onRequestSources: () => {
|
171
|
+
console.log(`onRequestSources invoked`);
|
164
172
|
return new Promise((resolve, reject) => {
|
165
173
|
if (!sourceIsLoading) {
|
166
174
|
setTimeout(() => {
|
167
|
-
if (error
|
175
|
+
if (error) {
|
168
176
|
reject(new Error(error));
|
169
177
|
} else {
|
170
178
|
resolve(singleSource ? [sampleSources[0]] : sampleSources);
|
package/src/index.scss
CHANGED
@@ -3,7 +3,9 @@
|
|
3
3
|
@import 'tailwindcss/components';
|
4
4
|
@import 'tailwindcss/utilities';
|
5
5
|
|
6
|
+
@import '@squiz/sds/lib/package.css';
|
6
7
|
@import '@squiz/sds/lib/styles/_imports.scss';
|
8
|
+
@import '@squiz/resource-browser-ui-lib/src/index';
|
7
9
|
|
8
10
|
// Components
|
9
11
|
@import './ResourcePicker/resource-picker';
|
package/src/index.stories.tsx
CHANGED
@@ -11,8 +11,9 @@ export default {
|
|
11
11
|
const Template: StoryFn<typeof ResourceBrowser> = (props) => {
|
12
12
|
const [resource, setResource] = useState<ResourceBrowserUnresolvedResource | null>(props?.value || null);
|
13
13
|
const { onRequestSources } = createResourceBrowserCallbacks({
|
14
|
-
sourceIsLoading: false,
|
14
|
+
sourceIsLoading: props.sourceIsLoading || false,
|
15
15
|
singleSource: props.singleSource,
|
16
|
+
error: props.sourceError ? 'Error' : undefined,
|
16
17
|
});
|
17
18
|
|
18
19
|
const onChange = (resource: ResourceBrowserResource | null) => {
|
@@ -32,7 +33,7 @@ const Template: StoryFn<typeof ResourceBrowser> = (props) => {
|
|
32
33
|
}
|
33
34
|
};
|
34
35
|
|
35
|
-
const plugins: ResourceBrowserPlugin[] = createPlugins(props.callbackWait, props.headerPortal);
|
36
|
+
const plugins: ResourceBrowserPlugin[] = createPlugins(props.callbackWait, props.headerPortal, props.resourceError);
|
36
37
|
|
37
38
|
return (
|
38
39
|
<div className="w-[400px] m-3">
|
@@ -54,6 +55,7 @@ export const Primary = Template.bind({});
|
|
54
55
|
Primary.args = {
|
55
56
|
modalTitle: 'Choose asset',
|
56
57
|
sourceIsLoading: false,
|
58
|
+
sourceIsError: false,
|
57
59
|
resourceIsLoading: false,
|
58
60
|
error: '',
|
59
61
|
callbackWait: 0,
|
@@ -100,3 +102,21 @@ InlineEditSelected.args = {
|
|
100
102
|
sourceId: 'c90feac1-55f3-4e1f-9b56-c22829e3f510',
|
101
103
|
},
|
102
104
|
};
|
105
|
+
|
106
|
+
export const InlineEditLoading = Template.bind({});
|
107
|
+
InlineEditLoading.args = {
|
108
|
+
...InlineEditSelected.args,
|
109
|
+
sourceIsLoading: true,
|
110
|
+
};
|
111
|
+
|
112
|
+
export const InlineEditError = Template.bind({});
|
113
|
+
InlineEditError.args = {
|
114
|
+
...InlineEditSelected.args,
|
115
|
+
sourceError: true,
|
116
|
+
};
|
117
|
+
|
118
|
+
export const InlineEditResourceError = Template.bind({});
|
119
|
+
InlineEditResourceError.args = {
|
120
|
+
...InlineEditSelected.args,
|
121
|
+
resourceError: true,
|
122
|
+
};
|
package/src/index.tsx
CHANGED
@@ -48,7 +48,7 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
48
48
|
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
49
49
|
const [source, setSource] = useState<ResourceBrowserSourceWithPlugin | null>(null);
|
50
50
|
const [mode, setMode] = useState<PluginLaunchMode | null>(null);
|
51
|
-
const { data: sources, isLoading, error: sourcesError } = useSources({ onRequestSources, plugins });
|
51
|
+
const { data: sources, isLoading, error: sourcesError, reload: reloadSources } = useSources({ onRequestSources, plugins });
|
52
52
|
const [plugin, setPlugin] = useState<ResourceBrowserPlugin | null>(null);
|
53
53
|
|
54
54
|
// MainContainer will render a list of sources of one is not provided to it, callback to allow it to set the source once a user selects
|
@@ -109,6 +109,11 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
109
109
|
}
|
110
110
|
}, [sources, isModalOpen]);
|
111
111
|
|
112
|
+
// Reset function to allow user manual reload if sources fail in inline usage
|
113
|
+
const handleReset = useCallback(() => {
|
114
|
+
reloadSources();
|
115
|
+
}, [reloadSources]);
|
116
|
+
|
112
117
|
// Render a default "plugin" and one for each item in the plugins array. They are conditionally rendered based on what is selected
|
113
118
|
return (
|
114
119
|
<div className="squiz-rb-scope">
|
@@ -135,6 +140,7 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
135
140
|
}}
|
136
141
|
isModalOpen={isModalOpen}
|
137
142
|
onModalStateChange={handleModalStateChange}
|
143
|
+
onRetry={handleReset}
|
138
144
|
/>
|
139
145
|
{plugins.map((thisPlugin) => {
|
140
146
|
return (
|
@@ -148,13 +154,14 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
148
154
|
sources={sources}
|
149
155
|
setSource={handleSourceSelect}
|
150
156
|
isLoading={isLoading}
|
151
|
-
error={error}
|
157
|
+
error={sourcesError || error}
|
152
158
|
plugin={plugin}
|
153
159
|
pluginMode={mode}
|
154
160
|
searchEnabled={searchEnabled}
|
155
161
|
useResource={thisPlugin.useResolveResource}
|
156
162
|
isModalOpen={isModalOpen}
|
157
163
|
onModalStateChange={handleModalStateChange}
|
164
|
+
onRetry={handleReset}
|
158
165
|
/>
|
159
166
|
);
|
160
167
|
})}
|