@squiz/resource-browser 3.3.8 → 3.3.10
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 +16 -0
- package/lib/Plugin/Plugin.d.ts +2 -1
- package/lib/Plugin/Plugin.js +26 -7
- package/lib/ResourceBrowserInlineButton/ResourceBrowserInlineButton.d.ts +4 -1
- package/lib/ResourceBrowserInlineButton/ResourceBrowserInlineButton.js +4 -4
- package/lib/index.d.ts +2 -1
- package/lib/index.js +26 -10
- package/lib/types.d.ts +1 -0
- package/package.json +2 -2
- package/src/Plugin/Plugin.spec.tsx +4 -2
- package/src/Plugin/Plugin.tsx +9 -4
- package/src/ResourceBrowserInlineButton/ResourceBrowserInlineButton.tsx +25 -22
- package/src/__mocks__/sample-sources.json +2 -1
- package/src/index.spec.tsx +187 -23
- package/src/index.stories.tsx +9 -0
- package/src/index.tsx +31 -9
- package/src/types.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 3.3.10
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8d3c8e0: source alias support
|
|
8
|
+
- Updated dependencies [8d3c8e0]
|
|
9
|
+
- @squiz/resource-browser-ui-lib@1.2.6
|
|
10
|
+
|
|
11
|
+
## 3.3.9
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 53de26d: add ref support and modal state change callback
|
|
16
|
+
- Updated dependencies [53de26d]
|
|
17
|
+
- @squiz/resource-browser-ui-lib@1.2.5
|
|
18
|
+
|
|
3
19
|
## 3.3.8
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/lib/Plugin/Plugin.d.ts
CHANGED
|
@@ -14,5 +14,6 @@ export type PluginRenderType = ResourceBrowserInputProps & {
|
|
|
14
14
|
inline: boolean;
|
|
15
15
|
inlineType?: InlineType;
|
|
16
16
|
onRetry: () => void;
|
|
17
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
17
18
|
};
|
|
18
|
-
export declare const PluginRender:
|
|
19
|
+
export declare const PluginRender: React.ForwardRefExoticComponent<Omit<PluginRenderType, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
package/lib/Plugin/Plugin.js
CHANGED
|
@@ -1,18 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
4
24
|
};
|
|
5
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
26
|
exports.PluginRender = void 0;
|
|
7
|
-
const react_1 =
|
|
27
|
+
const react_1 = __importStar(require("react"));
|
|
8
28
|
const ResourceBrowserInput_1 = require("../ResourceBrowserInput/ResourceBrowserInput");
|
|
9
29
|
const ResourceBrowserInlineButton_1 = require("../ResourceBrowserInlineButton/ResourceBrowserInlineButton");
|
|
10
30
|
const AuthProvider_1 = require("../ResourceBrowserContext/AuthProvider");
|
|
11
|
-
|
|
31
|
+
exports.PluginRender = (0, react_1.forwardRef)(({ render, inline, inlineType, ...props }, forwardRef) => {
|
|
12
32
|
if (!render)
|
|
13
33
|
return react_1.default.createElement(react_1.default.Fragment, null);
|
|
14
34
|
const requiresAuth = Boolean(props.source?.configuration?.authUrl);
|
|
15
|
-
const content = inline && inlineType ? react_1.default.createElement(ResourceBrowserInlineButton_1.ResourceBrowserInlineButton, { inlineType: inlineType, ...props }) : react_1.default.createElement(ResourceBrowserInput_1.ResourceBrowserInput, { ...props });
|
|
35
|
+
const content = inline && inlineType ? (react_1.default.createElement(ResourceBrowserInlineButton_1.ResourceBrowserInlineButton, { ref: forwardRef, inlineType: inlineType, ...props })) : (react_1.default.createElement(ResourceBrowserInput_1.ResourceBrowserInput, { ...props }));
|
|
16
36
|
return requiresAuth ? react_1.default.createElement(AuthProvider_1.AuthProvider, { authConfig: props.source }, content) : content;
|
|
17
|
-
};
|
|
18
|
-
exports.PluginRender = PluginRender;
|
|
37
|
+
});
|
|
@@ -5,4 +5,7 @@ export type ResourceBrowserInlineButtonProps = ResourceBrowserInputProps & {
|
|
|
5
5
|
inlineType: InlineType;
|
|
6
6
|
onRetry: () => void;
|
|
7
7
|
};
|
|
8
|
-
export declare const ResourceBrowserInlineButton:
|
|
8
|
+
export declare const ResourceBrowserInlineButton: React.ForwardRefExoticComponent<ResourceBrowserInputProps & {
|
|
9
|
+
inlineType: InlineType;
|
|
10
|
+
onRetry: () => void;
|
|
11
|
+
} & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -34,7 +34,8 @@ const InlineLoadingErrorState_1 = require("./InlineLoadingErrorState/InlineLoadi
|
|
|
34
34
|
const ImageInline_1 = require("../Icons/ImageInline");
|
|
35
35
|
const LinkInline_1 = require("../Icons/LinkInline");
|
|
36
36
|
const AdsClickIcon_1 = require("../Icons/AdsClickIcon");
|
|
37
|
-
|
|
37
|
+
exports.ResourceBrowserInlineButton = (0, react_1.forwardRef)((props, forwardRef) => {
|
|
38
|
+
const { inlineType, modalTitle, allowedTypes, onChange, onRetry, value, useResource, isDisabled, plugin, pluginMode, searchEnabled, source, sources, isLoading, error, setSource, isModalOpen, onModalStateChange, } = props;
|
|
38
39
|
// If an error happens loading the resource the inline browser just opens without it as if no preselectedResource is provided
|
|
39
40
|
const { data: resource, isLoading: isResourceLoading } = useResource(value || null, source);
|
|
40
41
|
const inlineTypePickerLabels = (0, react_1.useMemo)(() => ({
|
|
@@ -48,12 +49,11 @@ const ResourceBrowserInlineButton = ({ inlineType, modalTitle, allowedTypes, onC
|
|
|
48
49
|
resource: react_1.default.createElement(AdsClickIcon_1.AdsClickIcon, { "aria-label": "change resource" }),
|
|
49
50
|
}), [inlineType]);
|
|
50
51
|
return (react_1.default.createElement("div", { className: "inline-launch-button" },
|
|
51
|
-
react_1.default.createElement(resource_browser_ui_lib_1.ModalTrigger, { overlayTriggerState: {
|
|
52
|
+
react_1.default.createElement(resource_browser_ui_lib_1.ModalTrigger, { ref: forwardRef, overlayTriggerState: {
|
|
52
53
|
isOpen: isModalOpen,
|
|
53
54
|
onOpenChange: onModalStateChange,
|
|
54
55
|
}, showLabel: false, label: "", icon: inlineTypePickerIcons[inlineType], isDisabled: isDisabled, scope: "squiz-rb-scope", containerClasses: "inline-launch-button__button" }, (onClose, titleProps) => (react_1.default.createElement(react_1.default.Fragment, null,
|
|
55
56
|
(isLoading || isResourceLoading || error) && (react_1.default.createElement(InlineLoadingErrorState_1.InlineLoadingErrorState, { title: modalTitle, titleAriaProps: titleProps, onClose: onClose, onRetry: onRetry, isLoading: isLoading || isResourceLoading, error: error })),
|
|
56
57
|
!(isLoading || isResourceLoading) && !error && (react_1.default.createElement(MainContainer_1.default, { selectedSource: source, sources: sources, preselectedResource: resource, plugin: plugin, pluginMode: pluginMode, searchEnabled: searchEnabled, title: modalTitle, titleAriaProps: titleProps, allowedTypes: allowedTypes, onSourceSelect: setSource, onClose: onClose, onChange: onChange }))))),
|
|
57
58
|
react_1.default.createElement("div", { className: "inline-launch-button__label" }, inlineTypePickerLabels[inlineType])));
|
|
58
|
-
};
|
|
59
|
-
exports.ResourceBrowserInlineButton = ResourceBrowserInlineButton;
|
|
59
|
+
});
|
package/lib/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export type ResourceBrowserProps = {
|
|
|
16
16
|
inline?: boolean;
|
|
17
17
|
inlineType?: InlineType;
|
|
18
18
|
onChange(resource: ResourceBrowserResource | null): void;
|
|
19
|
+
onModalStateChange?(isOpen: boolean): void;
|
|
19
20
|
onClear?(): void;
|
|
20
21
|
};
|
|
21
|
-
export declare const ResourceBrowser:
|
|
22
|
+
export declare const ResourceBrowser: React.ForwardRefExoticComponent<ResourceBrowserProps & React.RefAttributes<HTMLButtonElement>>;
|
package/lib/index.js
CHANGED
|
@@ -47,8 +47,8 @@ exports.SourceDropdown = SourceDropdown_1.default;
|
|
|
47
47
|
const SourceDropdownContainer_1 = __importDefault(require("./SourceDropdownContainer/SourceDropdownContainer"));
|
|
48
48
|
exports.SourceDropdownContainer = SourceDropdownContainer_1.default;
|
|
49
49
|
__exportStar(require("./types"), exports);
|
|
50
|
-
|
|
51
|
-
const { value, inline, inlineType, allowedPlugins } = props;
|
|
50
|
+
exports.ResourceBrowser = react_1.default.forwardRef((props, forwardRef) => {
|
|
51
|
+
const { value, inline, inlineType, allowedPlugins, onModalStateChange: onModalStateChangeExternalNotification } = props;
|
|
52
52
|
const [error, setError] = (0, react_1.useState)(null);
|
|
53
53
|
const { onRequestSources, searchEnabled, plugins: allPlugins } = (0, react_1.useContext)(ResourceBrowserContext_1.ResourceBrowserContext);
|
|
54
54
|
const [isModalOpen, setIsModalOpen] = (0, react_1.useState)(false);
|
|
@@ -64,6 +64,23 @@ const ResourceBrowser = (props) => {
|
|
|
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 @@ const ResourceBrowser = (props) => {
|
|
|
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
|
|
@@ -96,7 +113,8 @@ const ResourceBrowser = (props) => {
|
|
|
96
113
|
// The modal has some control over it own open/closed state (for WCAG reasons) so keep this in sync with our state
|
|
97
114
|
const handleModalStateChange = (0, react_1.useCallback)((isOpen) => {
|
|
98
115
|
setIsModalOpen(isOpen);
|
|
99
|
-
|
|
116
|
+
onModalStateChangeExternalNotification?.(isOpen);
|
|
117
|
+
}, [setIsModalOpen, onModalStateChangeExternalNotification]);
|
|
100
118
|
// If the modal closes and we dont have a value clear the source state so it goes back to the launcher on re-open
|
|
101
119
|
(0, react_1.useEffect)(() => {
|
|
102
120
|
// If modal is closed and we dont have a value
|
|
@@ -109,8 +127,7 @@ const ResourceBrowser = (props) => {
|
|
|
109
127
|
setMode(null);
|
|
110
128
|
// If there is a value passed in, reset the source so the preselected asset preview renders correctly
|
|
111
129
|
if (value && value.sourceId !== source?.id) {
|
|
112
|
-
|
|
113
|
-
setSource(source);
|
|
130
|
+
setSource(findSourceById(value, sources));
|
|
114
131
|
}
|
|
115
132
|
}
|
|
116
133
|
}, [sources, isModalOpen]);
|
|
@@ -120,7 +137,7 @@ const ResourceBrowser = (props) => {
|
|
|
120
137
|
}, [reloadSources]);
|
|
121
138
|
// Render a default "plugin" and one for each item in the plugins array. They are conditionally rendered based on what is selected
|
|
122
139
|
return (react_1.default.createElement("div", { className: "squiz-rb-scope" },
|
|
123
|
-
react_1.default.createElement(Plugin_1.PluginRender, { key: "default", type: null, render: plugin === null, inline: !!inline, inlineType: inlineType, ...props, source: source, sources: sources, setSource: handleSourceSelect, isLoading: isLoading, isOtherSourceValue: false, error: sourcesError || error, plugin: plugin, pluginMode: mode, searchEnabled: searchEnabled, useResource: () => {
|
|
140
|
+
react_1.default.createElement(Plugin_1.PluginRender, { key: "default", ref: forwardRef, type: null, render: plugin === null, inline: !!inline, inlineType: inlineType, ...props, source: source, sources: sources, setSource: handleSourceSelect, isLoading: isLoading, isOtherSourceValue: false, error: sourcesError || error, plugin: plugin, pluginMode: mode, searchEnabled: searchEnabled, useResource: () => {
|
|
124
141
|
return {
|
|
125
142
|
data: null,
|
|
126
143
|
error: null,
|
|
@@ -128,7 +145,6 @@ const ResourceBrowser = (props) => {
|
|
|
128
145
|
};
|
|
129
146
|
}, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange, onRetry: handleReset }),
|
|
130
147
|
plugins.map((thisPlugin) => {
|
|
131
|
-
return (react_1.default.createElement(Plugin_1.PluginRender, { key: thisPlugin.type, 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 }));
|
|
132
149
|
})));
|
|
133
|
-
};
|
|
134
|
-
exports.ResourceBrowser = ResourceBrowser;
|
|
150
|
+
});
|
package/lib/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squiz/resource-browser",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.10",
|
|
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",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
2
|
import { render, waitFor } from '@testing-library/react';
|
|
3
3
|
|
|
4
4
|
import { PluginRender } from './Plugin';
|
|
@@ -8,7 +8,9 @@ import * as RBInlineButton from '../ResourceBrowserInlineButton/ResourceBrowserI
|
|
|
8
8
|
import * as AuthContext from '../ResourceBrowserContext/AuthProvider';
|
|
9
9
|
|
|
10
10
|
jest.spyOn(RBI, 'ResourceBrowserInput');
|
|
11
|
-
jest.
|
|
11
|
+
jest.mock('../ResourceBrowserInlineButton/ResourceBrowserInlineButton', () => ({
|
|
12
|
+
ResourceBrowserInlineButton: jest.fn(() => <div data-testid="mocked-inline-button" />),
|
|
13
|
+
}));
|
|
12
14
|
jest.spyOn(AuthContext, 'AuthProvider');
|
|
13
15
|
|
|
14
16
|
const baseProps = {
|
package/src/Plugin/Plugin.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
2
|
import { ResourceBrowserInput, ResourceBrowserInputProps } from '../ResourceBrowserInput/ResourceBrowserInput';
|
|
3
3
|
import { ResourceBrowserInlineButton } from '../ResourceBrowserInlineButton/ResourceBrowserInlineButton';
|
|
4
4
|
import { AuthProvider } from '../ResourceBrowserContext/AuthProvider';
|
|
@@ -17,14 +17,19 @@ export type PluginRenderType = ResourceBrowserInputProps & {
|
|
|
17
17
|
inline: boolean;
|
|
18
18
|
inlineType?: InlineType;
|
|
19
19
|
onRetry: () => void;
|
|
20
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
20
21
|
};
|
|
21
|
-
export const PluginRender = ({ render, inline, inlineType, ...props }
|
|
22
|
+
export const PluginRender = forwardRef<HTMLButtonElement, PluginRenderType>(({ render, inline, inlineType, ...props }, forwardRef) => {
|
|
22
23
|
if (!render) return <></>;
|
|
23
24
|
|
|
24
25
|
const requiresAuth = Boolean((props.source as ResourceBrowserSourceWithConfig)?.configuration?.authUrl);
|
|
25
26
|
|
|
26
27
|
const content =
|
|
27
|
-
inline && inlineType ?
|
|
28
|
+
inline && inlineType ? (
|
|
29
|
+
<ResourceBrowserInlineButton ref={forwardRef} inlineType={inlineType} {...props} />
|
|
30
|
+
) : (
|
|
31
|
+
<ResourceBrowserInput {...props} />
|
|
32
|
+
);
|
|
28
33
|
|
|
29
34
|
return requiresAuth ? <AuthProvider authConfig={props.source}>{content}</AuthProvider> : content;
|
|
30
|
-
};
|
|
35
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
1
|
+
import React, { forwardRef, useMemo } 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';
|
|
@@ -13,26 +13,28 @@ export type ResourceBrowserInlineButtonProps = ResourceBrowserInputProps & {
|
|
|
13
13
|
onRetry: () => void;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export const ResourceBrowserInlineButton = ({
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
16
|
+
export const ResourceBrowserInlineButton = forwardRef<HTMLButtonElement, ResourceBrowserInlineButtonProps>((props, forwardRef) => {
|
|
17
|
+
const {
|
|
18
|
+
inlineType,
|
|
19
|
+
modalTitle,
|
|
20
|
+
allowedTypes,
|
|
21
|
+
onChange,
|
|
22
|
+
onRetry,
|
|
23
|
+
value,
|
|
24
|
+
useResource,
|
|
25
|
+
isDisabled,
|
|
26
|
+
plugin,
|
|
27
|
+
pluginMode,
|
|
28
|
+
searchEnabled,
|
|
29
|
+
source,
|
|
30
|
+
sources,
|
|
31
|
+
isLoading,
|
|
32
|
+
error,
|
|
33
|
+
setSource,
|
|
34
|
+
isModalOpen,
|
|
35
|
+
onModalStateChange,
|
|
36
|
+
} = props;
|
|
37
|
+
|
|
36
38
|
// If an error happens loading the resource the inline browser just opens without it as if no preselectedResource is provided
|
|
37
39
|
const { data: resource, isLoading: isResourceLoading } = useResource(value || null, source);
|
|
38
40
|
|
|
@@ -56,6 +58,7 @@ export const ResourceBrowserInlineButton = ({
|
|
|
56
58
|
return (
|
|
57
59
|
<div className="inline-launch-button">
|
|
58
60
|
<ModalTrigger
|
|
61
|
+
ref={forwardRef}
|
|
59
62
|
overlayTriggerState={{
|
|
60
63
|
isOpen: isModalOpen,
|
|
61
64
|
onOpenChange: onModalStateChange,
|
|
@@ -101,4 +104,4 @@ export const ResourceBrowserInlineButton = ({
|
|
|
101
104
|
<div className="inline-launch-button__label">{inlineTypePickerLabels[inlineType]}</div>
|
|
102
105
|
</div>
|
|
103
106
|
);
|
|
104
|
-
};
|
|
107
|
+
});
|
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);
|
|
@@ -568,12 +653,47 @@ describe('Resource browser input', () => {
|
|
|
568
653
|
});
|
|
569
654
|
});
|
|
570
655
|
|
|
656
|
+
it('onModalStateChange calls onModalStateChangeExternalNotification when provided', async () => {
|
|
657
|
+
const mockOnModalStateChange = jest.fn();
|
|
658
|
+
const sourcesInput = [mockSource({ type: 'dam' })];
|
|
659
|
+
mockRequestSources.mockResolvedValueOnce(sourcesInput);
|
|
660
|
+
|
|
661
|
+
renderComponent({ onModalStateChange: mockOnModalStateChange });
|
|
662
|
+
await waitFor(() => {
|
|
663
|
+
expect(RBI.ResourceBrowserInput).toHaveBeenCalled();
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
const { onModalStateChange } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
|
|
667
|
+
|
|
668
|
+
// Test opening modal
|
|
669
|
+
act(() => {
|
|
670
|
+
onModalStateChange(true);
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
await waitFor(() => {
|
|
674
|
+
expect(mockOnModalStateChange).toHaveBeenCalledWith(true);
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
// Test closing modal
|
|
678
|
+
act(() => {
|
|
679
|
+
onModalStateChange(false);
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
await waitFor(() => {
|
|
683
|
+
expect(mockOnModalStateChange).toHaveBeenCalledWith(false);
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
expect(mockOnModalStateChange).toHaveBeenCalledTimes(2);
|
|
687
|
+
});
|
|
688
|
+
|
|
571
689
|
describe('Resource browser plugin', () => {
|
|
690
|
+
let PluginRenderSpy: jest.SpyInstance;
|
|
572
691
|
beforeEach(() => {
|
|
573
|
-
|
|
692
|
+
//@ts-ignore
|
|
693
|
+
PluginRenderSpy = jest.spyOn(Plugin.PluginRender, 'render');
|
|
574
694
|
});
|
|
575
695
|
afterEach(() => {
|
|
576
|
-
|
|
696
|
+
PluginRenderSpy.mockRestore();
|
|
577
697
|
});
|
|
578
698
|
|
|
579
699
|
it('Will default to a non plugin based render for initial load and selection of first source', async () => {
|
|
@@ -583,29 +703,29 @@ describe('Resource browser input', () => {
|
|
|
583
703
|
|
|
584
704
|
// Will render a default with no selected source
|
|
585
705
|
await waitFor(() => {
|
|
586
|
-
expect(
|
|
706
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
|
587
707
|
expect.objectContaining({
|
|
588
708
|
render: true,
|
|
589
709
|
type: null,
|
|
590
710
|
plugin: null,
|
|
591
711
|
}),
|
|
592
|
-
|
|
712
|
+
null,
|
|
593
713
|
);
|
|
594
714
|
|
|
595
|
-
expect(
|
|
715
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
|
596
716
|
expect.objectContaining({
|
|
597
717
|
render: false,
|
|
598
718
|
type: 'dam',
|
|
599
719
|
}),
|
|
600
|
-
|
|
720
|
+
null,
|
|
601
721
|
);
|
|
602
722
|
|
|
603
|
-
expect(
|
|
723
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
|
604
724
|
expect.objectContaining({
|
|
605
725
|
render: false,
|
|
606
726
|
type: 'matrix',
|
|
607
727
|
}),
|
|
608
|
-
|
|
728
|
+
null,
|
|
609
729
|
);
|
|
610
730
|
});
|
|
611
731
|
});
|
|
@@ -619,28 +739,28 @@ describe('Resource browser input', () => {
|
|
|
619
739
|
|
|
620
740
|
// Will render a default with no selected source
|
|
621
741
|
await waitFor(() => {
|
|
622
|
-
expect(
|
|
742
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
|
623
743
|
expect.objectContaining({
|
|
624
744
|
render: false,
|
|
625
745
|
type: null,
|
|
626
746
|
}),
|
|
627
|
-
|
|
747
|
+
null,
|
|
628
748
|
);
|
|
629
749
|
|
|
630
|
-
expect(
|
|
750
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
|
631
751
|
expect.objectContaining({
|
|
632
752
|
render: true,
|
|
633
753
|
type: 'dam',
|
|
634
754
|
}),
|
|
635
|
-
|
|
755
|
+
null,
|
|
636
756
|
);
|
|
637
757
|
|
|
638
|
-
expect(
|
|
758
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
|
639
759
|
expect.objectContaining({
|
|
640
760
|
render: false,
|
|
641
761
|
type: 'matrix',
|
|
642
762
|
}),
|
|
643
|
-
|
|
763
|
+
null,
|
|
644
764
|
);
|
|
645
765
|
});
|
|
646
766
|
|
|
@@ -672,28 +792,28 @@ describe('Resource browser input', () => {
|
|
|
672
792
|
|
|
673
793
|
// Will render a default with no selected source
|
|
674
794
|
await waitFor(() => {
|
|
675
|
-
expect(
|
|
795
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
|
676
796
|
expect.objectContaining({
|
|
677
797
|
render: true,
|
|
678
798
|
type: null,
|
|
679
799
|
}),
|
|
680
|
-
|
|
800
|
+
null,
|
|
681
801
|
);
|
|
682
802
|
|
|
683
|
-
expect(
|
|
803
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
|
684
804
|
expect.objectContaining({
|
|
685
805
|
render: true,
|
|
686
806
|
type: 'dam',
|
|
687
807
|
}),
|
|
688
|
-
|
|
808
|
+
null,
|
|
689
809
|
);
|
|
690
810
|
|
|
691
|
-
expect(
|
|
811
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
|
692
812
|
expect.objectContaining({
|
|
693
813
|
render: false,
|
|
694
814
|
type: 'matrix',
|
|
695
815
|
}),
|
|
696
|
-
|
|
816
|
+
null,
|
|
697
817
|
);
|
|
698
818
|
});
|
|
699
819
|
});
|
|
@@ -724,20 +844,20 @@ describe('Resource browser input', () => {
|
|
|
724
844
|
);
|
|
725
845
|
});
|
|
726
846
|
|
|
727
|
-
(
|
|
847
|
+
(PluginRenderSpy as unknown as jest.Mock).mockClear();
|
|
728
848
|
|
|
729
849
|
act(() => {
|
|
730
850
|
setSource({ ...matrixSource, plugin: pluginB });
|
|
731
851
|
});
|
|
732
852
|
|
|
733
853
|
await waitFor(() => {
|
|
734
|
-
expect(
|
|
854
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
|
735
855
|
expect.objectContaining({
|
|
736
856
|
plugin: pluginB,
|
|
737
857
|
}),
|
|
738
858
|
expect.any(Object),
|
|
739
859
|
);
|
|
740
|
-
expect(
|
|
860
|
+
expect(PluginRenderSpy).not.toHaveBeenCalledWith(
|
|
741
861
|
expect.objectContaining({
|
|
742
862
|
plugin: pluginA,
|
|
743
863
|
}),
|
|
@@ -745,5 +865,49 @@ describe('Resource browser input', () => {
|
|
|
745
865
|
);
|
|
746
866
|
});
|
|
747
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
|
+
});
|
|
748
912
|
});
|
|
749
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
|
@@ -38,11 +38,12 @@ export type ResourceBrowserProps = {
|
|
|
38
38
|
inline?: boolean; // Will render open button only, no input / showing of existing selection
|
|
39
39
|
inlineType?: InlineType; // Type of inline button to show
|
|
40
40
|
onChange(resource: ResourceBrowserResource | null): void;
|
|
41
|
+
onModalStateChange?(isOpen: boolean): void;
|
|
41
42
|
onClear?(): void;
|
|
42
43
|
};
|
|
43
44
|
|
|
44
|
-
export const ResourceBrowser = (props
|
|
45
|
-
const { value, inline, inlineType, allowedPlugins } = props;
|
|
45
|
+
export const ResourceBrowser = React.forwardRef<HTMLButtonElement, ResourceBrowserProps>((props, forwardRef) => {
|
|
46
|
+
const { value, inline, inlineType, allowedPlugins, onModalStateChange: onModalStateChangeExternalNotification } = props;
|
|
46
47
|
const [error, setError] = useState<Error | null>(null);
|
|
47
48
|
const { onRequestSources, searchEnabled, plugins: allPlugins } = useContext(ResourceBrowserContext);
|
|
48
49
|
|
|
@@ -59,6 +60,24 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
|
59
60
|
const { data: sources, isLoading, error: sourcesError, reload: reloadSources } = useSources({ onRequestSources, plugins });
|
|
60
61
|
const plugin = source?.plugin || null;
|
|
61
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
|
+
|
|
62
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
|
|
63
82
|
const handleSourceSelect = useCallback(
|
|
64
83
|
(source: ResourceBrowserSourceWithPlugin, mode?: PluginLaunchMode) => {
|
|
@@ -79,7 +98,8 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
|
79
98
|
// If there is a provided value try to use its source
|
|
80
99
|
if (value) {
|
|
81
100
|
// Search the sources for it matching against the value.source property
|
|
82
|
-
newSource =
|
|
101
|
+
newSource = findSourceById(value, sources);
|
|
102
|
+
|
|
83
103
|
// If the source is null and we arent loading the sources
|
|
84
104
|
if (newSource === null && !isLoading) {
|
|
85
105
|
// Set an error as the passed in value's source wasnt returned by onRequestSources
|
|
@@ -98,8 +118,9 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
|
98
118
|
const handleModalStateChange = useCallback(
|
|
99
119
|
(isOpen: boolean) => {
|
|
100
120
|
setIsModalOpen(isOpen);
|
|
121
|
+
onModalStateChangeExternalNotification?.(isOpen);
|
|
101
122
|
},
|
|
102
|
-
[setIsModalOpen],
|
|
123
|
+
[setIsModalOpen, onModalStateChangeExternalNotification],
|
|
103
124
|
);
|
|
104
125
|
|
|
105
126
|
// If the modal closes and we dont have a value clear the source state so it goes back to the launcher on re-open
|
|
@@ -114,8 +135,7 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
|
114
135
|
|
|
115
136
|
// If there is a value passed in, reset the source so the preselected asset preview renders correctly
|
|
116
137
|
if (value && value.sourceId !== source?.id) {
|
|
117
|
-
|
|
118
|
-
setSource(source);
|
|
138
|
+
setSource(findSourceById(value, sources));
|
|
119
139
|
}
|
|
120
140
|
}
|
|
121
141
|
}, [sources, isModalOpen]);
|
|
@@ -130,6 +150,7 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
|
130
150
|
<div className="squiz-rb-scope">
|
|
131
151
|
<PluginRender
|
|
132
152
|
key="default"
|
|
153
|
+
ref={forwardRef} // This is passed to every plugin but only actually render on one at a time
|
|
133
154
|
type={null}
|
|
134
155
|
render={plugin === null}
|
|
135
156
|
inline={!!inline}
|
|
@@ -159,13 +180,14 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
|
159
180
|
return (
|
|
160
181
|
<PluginRender
|
|
161
182
|
key={thisPlugin.type}
|
|
183
|
+
ref={forwardRef} // This is passed to every plugin but only actually render on one at a time
|
|
162
184
|
type={thisPlugin.type}
|
|
163
185
|
render={thisPlugin.type === plugin?.type}
|
|
164
186
|
inline={!!inline}
|
|
165
187
|
inlineType={inlineType}
|
|
166
188
|
{...props}
|
|
167
|
-
value={value && source ? (value
|
|
168
|
-
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
|
|
169
191
|
source={source}
|
|
170
192
|
sources={sources}
|
|
171
193
|
setSource={handleSourceSelect}
|
|
@@ -183,4 +205,4 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
|
183
205
|
})}
|
|
184
206
|
</div>
|
|
185
207
|
);
|
|
186
|
-
};
|
|
208
|
+
});
|
package/src/types.ts
CHANGED
|
@@ -19,6 +19,9 @@ export interface ResourceBrowserSource {
|
|
|
19
19
|
id: string;
|
|
20
20
|
// Source type e.g. Matrix, DAM etc determines what plugin will be used for resource selection
|
|
21
21
|
type: ResourceBrowserPluginType;
|
|
22
|
+
|
|
23
|
+
//alias' for the source to identify plugin to use for the source
|
|
24
|
+
aliases?: string[];
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
export interface ResourceBrowserSourceWithPlugin extends ResourceBrowserSource {
|