@squiz/resource-browser 3.3.9 → 3.3.11
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 +14 -0
- package/lib/Hooks/useAuth.d.ts +1 -0
- package/lib/Hooks/useAuth.js +20 -0
- package/lib/Plugin/Plugin.js +2 -1
- package/lib/ResourceBrowserContext/AuthProvider.d.ts +1 -0
- package/lib/index.js +20 -4
- package/lib/types.d.ts +2 -0
- package/package.json +2 -2
- package/src/Hooks/useAuth.spec.tsx +45 -2
- package/src/Hooks/useAuth.ts +23 -0
- package/src/Plugin/Plugin.spec.tsx +17 -0
- package/src/Plugin/Plugin.tsx +3 -1
- package/src/ResourceBrowserContext/AuthProvider.spec.tsx +4 -1
- package/src/ResourceBrowserContext/AuthProvider.tsx +1 -0
- package/src/__mocks__/sample-sources.json +2 -1
- package/src/index.spec.tsx +129 -0
- package/src/index.stories.tsx +9 -0
- package/src/index.tsx +23 -5
- package/src/types.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 3.3.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6c4d5b3: Support for proxying all dam requests
|
|
8
|
+
|
|
9
|
+
## 3.3.10
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 8d3c8e0: source alias support
|
|
14
|
+
- Updated dependencies [8d3c8e0]
|
|
15
|
+
- @squiz/resource-browser-ui-lib@1.2.6
|
|
16
|
+
|
|
3
17
|
## 3.3.9
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/lib/Hooks/useAuth.d.ts
CHANGED
package/lib/Hooks/useAuth.js
CHANGED
|
@@ -61,6 +61,11 @@ const useAuth = (authConfig) => {
|
|
|
61
61
|
setIsAuthenticated(!!token);
|
|
62
62
|
};
|
|
63
63
|
(0, react_1.useEffect)(() => {
|
|
64
|
+
// Don't run internal processes if we are always using the resource request proxy
|
|
65
|
+
if (authConfig?.alwaysUseResourceRequestProxy) {
|
|
66
|
+
// Consistent return value for cleanup function
|
|
67
|
+
return () => { };
|
|
68
|
+
}
|
|
64
69
|
const handleTabChange = () => {
|
|
65
70
|
if (!document.hidden) {
|
|
66
71
|
syncAuthState();
|
|
@@ -75,15 +80,30 @@ const useAuth = (authConfig) => {
|
|
|
75
80
|
};
|
|
76
81
|
}, []);
|
|
77
82
|
(0, react_1.useEffect)(() => {
|
|
83
|
+
// Don't run internal processes if we are always using the resource request proxy
|
|
84
|
+
if (authConfig?.alwaysUseResourceRequestProxy) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
78
87
|
refreshAccessToken().catch(() => {
|
|
79
88
|
setIsAuthenticated(false);
|
|
80
89
|
});
|
|
81
90
|
}, [authConfig, refreshAccessToken]);
|
|
91
|
+
// Return some dummy data if we are always using the resource request proxy
|
|
92
|
+
if (authConfig?.alwaysUseResourceRequestProxy) {
|
|
93
|
+
return {
|
|
94
|
+
authToken: null,
|
|
95
|
+
isAuthenticated: true,
|
|
96
|
+
login: () => { },
|
|
97
|
+
refreshAccessToken: () => Promise.resolve(''),
|
|
98
|
+
alwaysUseResourceRequestProxy: true,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
82
101
|
return {
|
|
83
102
|
authToken,
|
|
84
103
|
isAuthenticated,
|
|
85
104
|
login: handleLogin,
|
|
86
105
|
refreshAccessToken,
|
|
106
|
+
alwaysUseResourceRequestProxy: false,
|
|
87
107
|
};
|
|
88
108
|
};
|
|
89
109
|
exports.useAuth = useAuth;
|
package/lib/Plugin/Plugin.js
CHANGED
|
@@ -31,7 +31,8 @@ const AuthProvider_1 = require("../ResourceBrowserContext/AuthProvider");
|
|
|
31
31
|
exports.PluginRender = (0, react_1.forwardRef)(({ render, inline, inlineType, ...props }, forwardRef) => {
|
|
32
32
|
if (!render)
|
|
33
33
|
return react_1.default.createElement(react_1.default.Fragment, null);
|
|
34
|
-
const requiresAuth = Boolean(props.source?.configuration?.authUrl)
|
|
34
|
+
const requiresAuth = Boolean(props.source?.configuration?.authUrl) ||
|
|
35
|
+
Boolean(props.source?.configuration?.alwaysUseResourceRequestProxy);
|
|
35
36
|
const content = inline && inlineType ? (react_1.default.createElement(ResourceBrowserInlineButton_1.ResourceBrowserInlineButton, { ref: forwardRef, inlineType: inlineType, ...props })) : (react_1.default.createElement(ResourceBrowserInput_1.ResourceBrowserInput, { ...props }));
|
|
36
37
|
return requiresAuth ? react_1.default.createElement(AuthProvider_1.AuthProvider, { authConfig: props.source }, content) : content;
|
|
37
38
|
});
|
|
@@ -5,6 +5,7 @@ interface AuthContextProps {
|
|
|
5
5
|
isAuthenticated: boolean;
|
|
6
6
|
login: () => void;
|
|
7
7
|
refreshAccessToken: () => Promise<any>;
|
|
8
|
+
alwaysUseResourceRequestProxy: boolean;
|
|
8
9
|
}
|
|
9
10
|
export declare const AuthContext: React.Context<AuthContextProps | undefined>;
|
|
10
11
|
export declare const useAuthContext: () => AuthContextProps;
|
package/lib/index.js
CHANGED
|
@@ -64,6 +64,23 @@ exports.ResourceBrowser = react_1.default.forwardRef((props, forwardRef) => {
|
|
|
64
64
|
const [mode, setMode] = (0, react_1.useState)(null);
|
|
65
65
|
const { data: sources, isLoading, error: sourcesError, reload: reloadSources } = (0, useSources_1.useSources)({ onRequestSources, plugins });
|
|
66
66
|
const plugin = source?.plugin || null;
|
|
67
|
+
// Find source by its id or alias
|
|
68
|
+
const findSourceById = (value, sources) => {
|
|
69
|
+
let newSource = sources.find((source) => source.id === value?.sourceId) || null;
|
|
70
|
+
if (!newSource) {
|
|
71
|
+
newSource =
|
|
72
|
+
sources.find((source) => {
|
|
73
|
+
if (!source.aliases)
|
|
74
|
+
return false;
|
|
75
|
+
return source.aliases.includes(value.sourceId);
|
|
76
|
+
}) || null;
|
|
77
|
+
}
|
|
78
|
+
return newSource;
|
|
79
|
+
};
|
|
80
|
+
// Check if the value is for the source
|
|
81
|
+
const isValueForSource = (value, source) => {
|
|
82
|
+
return value?.sourceId === source.id || (source.aliases && source.aliases.includes(value?.sourceId));
|
|
83
|
+
};
|
|
67
84
|
// 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
|
|
68
85
|
const handleSourceSelect = (0, react_1.useCallback)((source, mode) => {
|
|
69
86
|
setSource(source);
|
|
@@ -79,7 +96,7 @@ exports.ResourceBrowser = react_1.default.forwardRef((props, forwardRef) => {
|
|
|
79
96
|
// If there is a provided value try to use its source
|
|
80
97
|
if (value) {
|
|
81
98
|
// Search the sources for it matching against the value.source property
|
|
82
|
-
newSource =
|
|
99
|
+
newSource = findSourceById(value, sources);
|
|
83
100
|
// If the source is null and we arent loading the sources
|
|
84
101
|
if (newSource === null && !isLoading) {
|
|
85
102
|
// Set an error as the passed in value's source wasnt returned by onRequestSources
|
|
@@ -110,8 +127,7 @@ exports.ResourceBrowser = react_1.default.forwardRef((props, forwardRef) => {
|
|
|
110
127
|
setMode(null);
|
|
111
128
|
// If there is a value passed in, reset the source so the preselected asset preview renders correctly
|
|
112
129
|
if (value && value.sourceId !== source?.id) {
|
|
113
|
-
|
|
114
|
-
setSource(source);
|
|
130
|
+
setSource(findSourceById(value, sources));
|
|
115
131
|
}
|
|
116
132
|
}
|
|
117
133
|
}, [sources, isModalOpen]);
|
|
@@ -129,6 +145,6 @@ exports.ResourceBrowser = react_1.default.forwardRef((props, forwardRef) => {
|
|
|
129
145
|
};
|
|
130
146
|
}, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange, onRetry: handleReset }),
|
|
131
147
|
plugins.map((thisPlugin) => {
|
|
132
|
-
return (react_1.default.createElement(Plugin_1.PluginRender, { key: thisPlugin.type, ref: forwardRef, type: thisPlugin.type, render: thisPlugin.type === plugin?.type, inline: !!inline, inlineType: inlineType, ...props, value: value && source ? (value
|
|
148
|
+
return (react_1.default.createElement(Plugin_1.PluginRender, { key: thisPlugin.type, ref: forwardRef, type: thisPlugin.type, render: thisPlugin.type === plugin?.type, inline: !!inline, inlineType: inlineType, ...props, value: value && source ? (isValueForSource(value, source) ? value : null) : null, isOtherSourceValue: value && source ? (!isValueForSource(value, source) ? true : false) : false, 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 }));
|
|
133
149
|
})));
|
|
134
150
|
});
|
package/lib/types.d.ts
CHANGED
|
@@ -8,11 +8,13 @@ export type AuthenticationConfiguration = {
|
|
|
8
8
|
redirectUrl: string;
|
|
9
9
|
clientId: string;
|
|
10
10
|
scope: string;
|
|
11
|
+
alwaysUseResourceRequestProxy?: boolean;
|
|
11
12
|
};
|
|
12
13
|
export interface ResourceBrowserSource {
|
|
13
14
|
name?: string;
|
|
14
15
|
id: string;
|
|
15
16
|
type: ResourceBrowserPluginType;
|
|
17
|
+
aliases?: string[];
|
|
16
18
|
}
|
|
17
19
|
export interface ResourceBrowserSourceWithPlugin extends ResourceBrowserSource {
|
|
18
20
|
plugin: ResourceBrowserPlugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squiz/resource-browser",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.11",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"private": false,
|
|
@@ -28,7 +28,7 @@
|
|
|
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.5",
|
|
31
|
-
"@squiz/resource-browser-ui-lib": "^1.2.
|
|
31
|
+
"@squiz/resource-browser-ui-lib": "^1.2.6",
|
|
32
32
|
"clsx": "^2.1.0",
|
|
33
33
|
"expiry-map": "^2.0.0",
|
|
34
34
|
"p-memoize": "^4.0.4",
|
|
@@ -29,9 +29,9 @@ describe('useAuth', () => {
|
|
|
29
29
|
await waitFor(() => {
|
|
30
30
|
expect(result.current.authToken).toBe('initialAuthToken');
|
|
31
31
|
expect(result.current.isAuthenticated).toBe(true);
|
|
32
|
+
expect(result.current.alwaysUseResourceRequestProxy).toBe(false);
|
|
32
33
|
});
|
|
33
34
|
});
|
|
34
|
-
|
|
35
35
|
it('should do nothing if missing crucial props', async () => {
|
|
36
36
|
const authConfigMissingProps = {
|
|
37
37
|
clientId: 'example-client-id',
|
|
@@ -285,7 +285,7 @@ describe('useAuth', () => {
|
|
|
285
285
|
});
|
|
286
286
|
|
|
287
287
|
it('should throw an error when getCookieValue throws unexpectedly', () => {
|
|
288
|
-
jest.spyOn(authUtils, 'getCookieValue').mockImplementation(() => {
|
|
288
|
+
const getCookieValueSpy = jest.spyOn(authUtils, 'getCookieValue').mockImplementation(() => {
|
|
289
289
|
throw new Error('Unexpected error');
|
|
290
290
|
});
|
|
291
291
|
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
@@ -293,5 +293,48 @@ describe('useAuth', () => {
|
|
|
293
293
|
expect(() => renderHook(() => useAuth(authConfig))).toThrow('Unexpected error');
|
|
294
294
|
|
|
295
295
|
consoleSpy.mockRestore();
|
|
296
|
+
getCookieValueSpy.mockRestore();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
describe('alwaysUseResourceRequestProxy', () => {
|
|
300
|
+
const authConfigAlwaysUseResourceRequestProxy = { ...authConfig, alwaysUseResourceRequestProxy: true };
|
|
301
|
+
it('should initialize with alwaysUseResourceRequestProxy true', async () => {
|
|
302
|
+
const { result } = renderHook(() => useAuth(authConfigAlwaysUseResourceRequestProxy));
|
|
303
|
+
|
|
304
|
+
await waitFor(() => {
|
|
305
|
+
expect(result.current.authToken).toBe(null);
|
|
306
|
+
expect(result.current.isAuthenticated).toBe(true);
|
|
307
|
+
expect(result.current.alwaysUseResourceRequestProxy).toBe(true);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// Check that refreshAccessToken is not called when authConfig changes
|
|
311
|
+
// Set up a spy and test that refreshAccessToken is not called when authConfig changes
|
|
312
|
+
const refreshAccessTokenSpy = jest.spyOn(authUtils, 'refreshAccessToken');
|
|
313
|
+
|
|
314
|
+
// Change authConfig and rerender
|
|
315
|
+
const newConfig = { ...authConfigAlwaysUseResourceRequestProxy, clientId: 'changed-client-id' };
|
|
316
|
+
const { rerender } = renderHook((props) => useAuth(props), { initialProps: authConfigAlwaysUseResourceRequestProxy });
|
|
317
|
+
rerender(newConfig);
|
|
318
|
+
|
|
319
|
+
// refreshAccessToken should not be called
|
|
320
|
+
expect(refreshAccessTokenSpy).not.toHaveBeenCalled();
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('should not attach event listeners when alwaysUseResourceRequestProxy is true', async () => {
|
|
324
|
+
mockGetCookieValue.mockReturnValueOnce(null);
|
|
325
|
+
const { result } = renderHook(() => useAuth(authConfigAlwaysUseResourceRequestProxy));
|
|
326
|
+
|
|
327
|
+
mockGetCookieValue.mockReturnValue('visibleAuthToken');
|
|
328
|
+
|
|
329
|
+
Object.defineProperty(document, 'hidden', { configurable: true, value: false });
|
|
330
|
+
|
|
331
|
+
act(() => {
|
|
332
|
+
document.dispatchEvent(new Event('visibilitychange'));
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
await waitFor(() => {
|
|
336
|
+
expect(result.current.authToken).toBe(null);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
296
339
|
});
|
|
297
340
|
});
|
package/src/Hooks/useAuth.ts
CHANGED
|
@@ -65,6 +65,12 @@ export const useAuth = (authConfig: AuthenticationConfiguration | undefined) =>
|
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
useEffect(() => {
|
|
68
|
+
// Don't run internal processes if we are always using the resource request proxy
|
|
69
|
+
if (authConfig?.alwaysUseResourceRequestProxy) {
|
|
70
|
+
// Consistent return value for cleanup function
|
|
71
|
+
return () => {};
|
|
72
|
+
}
|
|
73
|
+
|
|
68
74
|
const handleTabChange = () => {
|
|
69
75
|
if (!document.hidden) {
|
|
70
76
|
syncAuthState();
|
|
@@ -82,15 +88,32 @@ export const useAuth = (authConfig: AuthenticationConfiguration | undefined) =>
|
|
|
82
88
|
}, []);
|
|
83
89
|
|
|
84
90
|
useEffect(() => {
|
|
91
|
+
// Don't run internal processes if we are always using the resource request proxy
|
|
92
|
+
if (authConfig?.alwaysUseResourceRequestProxy) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
85
96
|
refreshAccessToken().catch(() => {
|
|
86
97
|
setIsAuthenticated(false);
|
|
87
98
|
});
|
|
88
99
|
}, [authConfig, refreshAccessToken]);
|
|
89
100
|
|
|
101
|
+
// Return some dummy data if we are always using the resource request proxy
|
|
102
|
+
if (authConfig?.alwaysUseResourceRequestProxy) {
|
|
103
|
+
return {
|
|
104
|
+
authToken: null,
|
|
105
|
+
isAuthenticated: true,
|
|
106
|
+
login: () => {},
|
|
107
|
+
refreshAccessToken: () => Promise.resolve(''),
|
|
108
|
+
alwaysUseResourceRequestProxy: true,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
90
112
|
return {
|
|
91
113
|
authToken,
|
|
92
114
|
isAuthenticated,
|
|
93
115
|
login: handleLogin,
|
|
94
116
|
refreshAccessToken,
|
|
117
|
+
alwaysUseResourceRequestProxy: false,
|
|
95
118
|
};
|
|
96
119
|
};
|
|
@@ -81,4 +81,21 @@ describe('Plugin', () => {
|
|
|
81
81
|
expect(AuthContext.AuthProvider).toHaveBeenCalledWith({ authConfig: sourceWithAuth, children: expect.anything() }, {});
|
|
82
82
|
});
|
|
83
83
|
});
|
|
84
|
+
|
|
85
|
+
it('Wraps content in AuthProvider when alwaysUseResourceRequestProxy is true', async () => {
|
|
86
|
+
const sourceWithAuth = {
|
|
87
|
+
id: '123',
|
|
88
|
+
type: 'dam' as ResourceBrowserPluginType,
|
|
89
|
+
configuration: {
|
|
90
|
+
alwaysUseResourceRequestProxy: true,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const props = { ...baseProps, source: sourceWithAuth };
|
|
95
|
+
render(<PluginRender render={true} inline={false} {...props} />);
|
|
96
|
+
|
|
97
|
+
await waitFor(() => {
|
|
98
|
+
expect(AuthContext.AuthProvider).toHaveBeenCalledWith({ authConfig: sourceWithAuth, children: expect.anything() }, {});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
84
101
|
});
|
package/src/Plugin/Plugin.tsx
CHANGED
|
@@ -22,7 +22,9 @@ export type PluginRenderType = ResourceBrowserInputProps & {
|
|
|
22
22
|
export const PluginRender = forwardRef<HTMLButtonElement, PluginRenderType>(({ render, inline, inlineType, ...props }, forwardRef) => {
|
|
23
23
|
if (!render) return <></>;
|
|
24
24
|
|
|
25
|
-
const requiresAuth =
|
|
25
|
+
const requiresAuth =
|
|
26
|
+
Boolean((props.source as ResourceBrowserSourceWithConfig)?.configuration?.authUrl) ||
|
|
27
|
+
Boolean((props.source as ResourceBrowserSourceWithConfig)?.configuration?.alwaysUseResourceRequestProxy);
|
|
26
28
|
|
|
27
29
|
const content =
|
|
28
30
|
inline && inlineType ? (
|
|
@@ -27,6 +27,7 @@ describe('AuthContext', () => {
|
|
|
27
27
|
isAuthenticated: true,
|
|
28
28
|
login: jest.fn(),
|
|
29
29
|
refreshAccessToken: jest.fn(),
|
|
30
|
+
alwaysUseResourceRequestProxy: false,
|
|
30
31
|
};
|
|
31
32
|
|
|
32
33
|
beforeEach(() => {
|
|
@@ -36,12 +37,13 @@ describe('AuthContext', () => {
|
|
|
36
37
|
|
|
37
38
|
it('should provide auth state and functions to consumers', async () => {
|
|
38
39
|
const TestComponent = () => {
|
|
39
|
-
const { authToken, isAuthenticated, login } = useAuthContext();
|
|
40
|
+
const { authToken, isAuthenticated, login, alwaysUseResourceRequestProxy } = useAuthContext();
|
|
40
41
|
return (
|
|
41
42
|
<div>
|
|
42
43
|
<span>{`Token: ${authToken}`}</span>
|
|
43
44
|
<span>{`Authenticated: ${isAuthenticated}`}</span>
|
|
44
45
|
<button onClick={login}>Login</button>
|
|
46
|
+
<span>{`Always use resource request proxy: ${alwaysUseResourceRequestProxy}`}</span>
|
|
45
47
|
</div>
|
|
46
48
|
);
|
|
47
49
|
};
|
|
@@ -54,6 +56,7 @@ describe('AuthContext', () => {
|
|
|
54
56
|
|
|
55
57
|
expect(screen.getByText(/Token: testAuthToken/)).toBeInTheDocument();
|
|
56
58
|
expect(screen.getByText(/Authenticated: true/)).toBeInTheDocument();
|
|
59
|
+
expect(screen.getByText(/Always use resource request proxy: false/)).toBeInTheDocument();
|
|
57
60
|
|
|
58
61
|
fireEvent.click(screen.getByText('Login'));
|
|
59
62
|
|
package/src/index.spec.tsx
CHANGED
|
@@ -141,6 +141,24 @@ describe('Resource browser input', () => {
|
|
|
141
141
|
});
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
+
it('If an aliased resource is provided will default to its true Source and Plugin to match', async () => {
|
|
145
|
+
const source = mockSource({ type: 'dam', aliases: ['alias-source-id'] });
|
|
146
|
+
mockRequestSources.mockResolvedValueOnce([source, mockSource({ type: 'matrix' })]);
|
|
147
|
+
mockResolveResource.mockResolvedValueOnce(mockResource({ source }));
|
|
148
|
+
renderComponent({ value: { sourceId: 'alias-source-id', resourceId: '100' } });
|
|
149
|
+
|
|
150
|
+
await waitFor(() => {
|
|
151
|
+
expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
|
|
152
|
+
expect.objectContaining({
|
|
153
|
+
source: calculateExpectedSource(source),
|
|
154
|
+
plugin: mockDamPlugin,
|
|
155
|
+
pluginMode: null,
|
|
156
|
+
}),
|
|
157
|
+
{},
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
144
162
|
it('If a resource is provided but its Source cannot be found it will error', async () => {
|
|
145
163
|
const source = mockSource({ type: 'dam' });
|
|
146
164
|
mockRequestSources.mockResolvedValueOnce([source, mockSource({ type: 'matrix' })]);
|
|
@@ -510,6 +528,73 @@ describe('Resource browser input', () => {
|
|
|
510
528
|
});
|
|
511
529
|
});
|
|
512
530
|
|
|
531
|
+
it('onModalStateChange called with false will reset the Source and Plugin to the aliases true source if the value uses an alias', async () => {
|
|
532
|
+
const originalSource = mockSource({
|
|
533
|
+
id: 'original-source-id',
|
|
534
|
+
name: 'Original Source',
|
|
535
|
+
type: 'dam',
|
|
536
|
+
aliases: ['alias-source-id'],
|
|
537
|
+
});
|
|
538
|
+
const aliasSourceId = 'alias-source-id';
|
|
539
|
+
const valueWithAlias = {
|
|
540
|
+
sourceId: aliasSourceId,
|
|
541
|
+
resourceId: '123456',
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
const sourcesInput = [originalSource, mockSource({ id: '2' })];
|
|
545
|
+
const calculatedSources = sourcesInput.map((source) => calculateExpectedSource(source));
|
|
546
|
+
mockRequestSources.mockResolvedValueOnce(sourcesInput);
|
|
547
|
+
|
|
548
|
+
renderComponent({ value: valueWithAlias });
|
|
549
|
+
|
|
550
|
+
await waitFor(() => {
|
|
551
|
+
expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
|
|
552
|
+
expect.objectContaining({
|
|
553
|
+
value: valueWithAlias,
|
|
554
|
+
sources: calculatedSources,
|
|
555
|
+
source: calculatedSources[0],
|
|
556
|
+
plugin: mockDamPlugin,
|
|
557
|
+
}),
|
|
558
|
+
{},
|
|
559
|
+
);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
const { onModalStateChange, setSource } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
|
|
563
|
+
// Invoke open and close the modal
|
|
564
|
+
act(() => {
|
|
565
|
+
onModalStateChange(true);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// Change the source
|
|
569
|
+
act(() => {
|
|
570
|
+
setSource(calculatedSources[1]);
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
await waitFor(() => {
|
|
574
|
+
expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
|
|
575
|
+
expect.objectContaining({
|
|
576
|
+
source: calculatedSources[1],
|
|
577
|
+
plugin: mockDamPlugin,
|
|
578
|
+
}),
|
|
579
|
+
{},
|
|
580
|
+
);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
act(() => {
|
|
584
|
+
onModalStateChange(false);
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
await waitFor(() => {
|
|
588
|
+
expect(RBI.ResourceBrowserInput).toHaveBeenLastCalledWith(
|
|
589
|
+
expect.objectContaining({
|
|
590
|
+
source: calculatedSources[0],
|
|
591
|
+
plugin: mockDamPlugin,
|
|
592
|
+
}),
|
|
593
|
+
{},
|
|
594
|
+
);
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
|
|
513
598
|
it('onModalStateChange called with false will reset the Mode', async () => {
|
|
514
599
|
const sourcesInput = [mockSource({ type: 'dam' }), mockSource()];
|
|
515
600
|
mockRequestSources.mockResolvedValueOnce(sourcesInput);
|
|
@@ -780,5 +865,49 @@ describe('Resource browser input', () => {
|
|
|
780
865
|
);
|
|
781
866
|
});
|
|
782
867
|
});
|
|
868
|
+
|
|
869
|
+
it('When resource browser is called with a value containing an alias source, Plugin is called with the original source not the alias value', async () => {
|
|
870
|
+
const originalSource = mockSource({
|
|
871
|
+
id: 'original-source-id',
|
|
872
|
+
name: 'Original Source',
|
|
873
|
+
type: 'dam',
|
|
874
|
+
aliases: ['alias-source-id'],
|
|
875
|
+
});
|
|
876
|
+
const aliasSourceId = 'alias-source-id';
|
|
877
|
+
|
|
878
|
+
mockRequestSources.mockResolvedValueOnce([originalSource]);
|
|
879
|
+
|
|
880
|
+
// Create a value that uses the alias source ID
|
|
881
|
+
const valueWithAlias = {
|
|
882
|
+
sourceId: aliasSourceId,
|
|
883
|
+
resourceId: '123456',
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
renderComponent({ value: valueWithAlias });
|
|
887
|
+
|
|
888
|
+
await waitFor(() => {
|
|
889
|
+
expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
|
|
890
|
+
expect.objectContaining({
|
|
891
|
+
value: valueWithAlias,
|
|
892
|
+
source: calculateExpectedSource(originalSource), // Should be the original source, not alias
|
|
893
|
+
plugin: mockDamPlugin,
|
|
894
|
+
}),
|
|
895
|
+
{},
|
|
896
|
+
);
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
// Verify that the PluginRender is called with the original source
|
|
900
|
+
await waitFor(() => {
|
|
901
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
|
902
|
+
expect.objectContaining({
|
|
903
|
+
render: true,
|
|
904
|
+
type: 'dam',
|
|
905
|
+
source: calculateExpectedSource(originalSource), // Should be the original source
|
|
906
|
+
isOtherSourceValue: false,
|
|
907
|
+
}),
|
|
908
|
+
null,
|
|
909
|
+
);
|
|
910
|
+
});
|
|
911
|
+
});
|
|
783
912
|
});
|
|
784
913
|
});
|
package/src/index.stories.tsx
CHANGED
|
@@ -103,6 +103,15 @@ RestrictedSource.args = {
|
|
|
103
103
|
searchEnabled: true,
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
+
export const SourceByAlias = Template.bind({});
|
|
107
|
+
SourceByAlias.args = {
|
|
108
|
+
...Primary.args,
|
|
109
|
+
value: {
|
|
110
|
+
resourceId: '1f7a25b4-380f-4540-9555-8be2dcab4019',
|
|
111
|
+
sourceId: 'bynder-789',
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
106
115
|
export const SearchEnabled = Template.bind({});
|
|
107
116
|
SearchEnabled.args = {
|
|
108
117
|
...Primary.args,
|
package/src/index.tsx
CHANGED
|
@@ -60,6 +60,24 @@ export const ResourceBrowser = React.forwardRef<HTMLButtonElement, ResourceBrows
|
|
|
60
60
|
const { data: sources, isLoading, error: sourcesError, reload: reloadSources } = useSources({ onRequestSources, plugins });
|
|
61
61
|
const plugin = source?.plugin || null;
|
|
62
62
|
|
|
63
|
+
// Find source by its id or alias
|
|
64
|
+
const findSourceById = (value: ResourceBrowserUnresolvedResource, sources: ResourceBrowserSourceWithPlugin[]) => {
|
|
65
|
+
let newSource = sources.find((source) => source.id === value?.sourceId) || null;
|
|
66
|
+
if (!newSource) {
|
|
67
|
+
newSource =
|
|
68
|
+
sources.find((source) => {
|
|
69
|
+
if (!source.aliases) return false;
|
|
70
|
+
return source.aliases.includes(value.sourceId);
|
|
71
|
+
}) || null;
|
|
72
|
+
}
|
|
73
|
+
return newSource;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Check if the value is for the source
|
|
77
|
+
const isValueForSource = (value: ResourceBrowserUnresolvedResource, source: ResourceBrowserSourceWithPlugin) => {
|
|
78
|
+
return value?.sourceId === source.id || (source.aliases && source.aliases.includes(value?.sourceId));
|
|
79
|
+
};
|
|
80
|
+
|
|
63
81
|
// 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
|
|
64
82
|
const handleSourceSelect = useCallback(
|
|
65
83
|
(source: ResourceBrowserSourceWithPlugin, mode?: PluginLaunchMode) => {
|
|
@@ -80,7 +98,8 @@ export const ResourceBrowser = React.forwardRef<HTMLButtonElement, ResourceBrows
|
|
|
80
98
|
// If there is a provided value try to use its source
|
|
81
99
|
if (value) {
|
|
82
100
|
// Search the sources for it matching against the value.source property
|
|
83
|
-
newSource =
|
|
101
|
+
newSource = findSourceById(value, sources);
|
|
102
|
+
|
|
84
103
|
// If the source is null and we arent loading the sources
|
|
85
104
|
if (newSource === null && !isLoading) {
|
|
86
105
|
// Set an error as the passed in value's source wasnt returned by onRequestSources
|
|
@@ -116,8 +135,7 @@ export const ResourceBrowser = React.forwardRef<HTMLButtonElement, ResourceBrows
|
|
|
116
135
|
|
|
117
136
|
// If there is a value passed in, reset the source so the preselected asset preview renders correctly
|
|
118
137
|
if (value && value.sourceId !== source?.id) {
|
|
119
|
-
|
|
120
|
-
setSource(source);
|
|
138
|
+
setSource(findSourceById(value, sources));
|
|
121
139
|
}
|
|
122
140
|
}
|
|
123
141
|
}, [sources, isModalOpen]);
|
|
@@ -168,8 +186,8 @@ export const ResourceBrowser = React.forwardRef<HTMLButtonElement, ResourceBrows
|
|
|
168
186
|
inline={!!inline}
|
|
169
187
|
inlineType={inlineType}
|
|
170
188
|
{...props}
|
|
171
|
-
value={value && source ? (value
|
|
172
|
-
isOtherSourceValue={value && source ? (value
|
|
189
|
+
value={value && source ? (isValueForSource(value, source) ? value : null) : null} // Don't pass value if value isn't for selected source
|
|
190
|
+
isOtherSourceValue={value && source ? (!isValueForSource(value, source) ? true : false) : false} // Block render of existing asset if not from this source
|
|
173
191
|
source={source}
|
|
174
192
|
sources={sources}
|
|
175
193
|
setSource={handleSourceSelect}
|
package/src/types.ts
CHANGED
|
@@ -10,6 +10,9 @@ export type AuthenticationConfiguration = {
|
|
|
10
10
|
redirectUrl: string;
|
|
11
11
|
clientId: string;
|
|
12
12
|
scope: string;
|
|
13
|
+
|
|
14
|
+
// Route all DAM requests through the DAM proxy
|
|
15
|
+
alwaysUseResourceRequestProxy?: boolean;
|
|
13
16
|
};
|
|
14
17
|
|
|
15
18
|
export interface ResourceBrowserSource {
|
|
@@ -19,6 +22,9 @@ export interface ResourceBrowserSource {
|
|
|
19
22
|
id: string;
|
|
20
23
|
// Source type e.g. Matrix, DAM etc determines what plugin will be used for resource selection
|
|
21
24
|
type: ResourceBrowserPluginType;
|
|
25
|
+
|
|
26
|
+
//alias' for the source to identify plugin to use for the source
|
|
27
|
+
aliases?: string[];
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
export interface ResourceBrowserSourceWithPlugin extends ResourceBrowserSource {
|