@squiz/resource-browser 3.3.8 → 3.3.9
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 +8 -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 +7 -7
- 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/index.spec.tsx +58 -23
- package/src/index.tsx +8 -4
package/CHANGELOG.md
CHANGED
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);
|
@@ -96,7 +96,8 @@ const ResourceBrowser = (props) => {
|
|
96
96
|
// The modal has some control over it own open/closed state (for WCAG reasons) so keep this in sync with our state
|
97
97
|
const handleModalStateChange = (0, react_1.useCallback)((isOpen) => {
|
98
98
|
setIsModalOpen(isOpen);
|
99
|
-
|
99
|
+
onModalStateChangeExternalNotification?.(isOpen);
|
100
|
+
}, [setIsModalOpen, onModalStateChangeExternalNotification]);
|
100
101
|
// 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
102
|
(0, react_1.useEffect)(() => {
|
102
103
|
// If modal is closed and we dont have a value
|
@@ -120,7 +121,7 @@ const ResourceBrowser = (props) => {
|
|
120
121
|
}, [reloadSources]);
|
121
122
|
// Render a default "plugin" and one for each item in the plugins array. They are conditionally rendered based on what is selected
|
122
123
|
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: () => {
|
124
|
+
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
125
|
return {
|
125
126
|
data: null,
|
126
127
|
error: null,
|
@@ -128,7 +129,6 @@ const ResourceBrowser = (props) => {
|
|
128
129
|
};
|
129
130
|
}, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange, onRetry: handleReset }),
|
130
131
|
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.sourceId === source.id ? value : null) : null, isOtherSourceValue: value && source ? (value.sourceId !== source.id ? 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
|
+
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.sourceId === source.id ? value : null) : null, isOtherSourceValue: value && source ? (value.sourceId !== source.id ? 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
133
|
})));
|
133
|
-
};
|
134
|
-
exports.ResourceBrowser = ResourceBrowser;
|
134
|
+
});
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@squiz/resource-browser",
|
3
|
-
"version": "3.3.
|
3
|
+
"version": "3.3.9",
|
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.5",
|
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
@@ -568,12 +568,47 @@ describe('Resource browser input', () => {
|
|
568
568
|
});
|
569
569
|
});
|
570
570
|
|
571
|
+
it('onModalStateChange calls onModalStateChangeExternalNotification when provided', async () => {
|
572
|
+
const mockOnModalStateChange = jest.fn();
|
573
|
+
const sourcesInput = [mockSource({ type: 'dam' })];
|
574
|
+
mockRequestSources.mockResolvedValueOnce(sourcesInput);
|
575
|
+
|
576
|
+
renderComponent({ onModalStateChange: mockOnModalStateChange });
|
577
|
+
await waitFor(() => {
|
578
|
+
expect(RBI.ResourceBrowserInput).toHaveBeenCalled();
|
579
|
+
});
|
580
|
+
|
581
|
+
const { onModalStateChange } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
|
582
|
+
|
583
|
+
// Test opening modal
|
584
|
+
act(() => {
|
585
|
+
onModalStateChange(true);
|
586
|
+
});
|
587
|
+
|
588
|
+
await waitFor(() => {
|
589
|
+
expect(mockOnModalStateChange).toHaveBeenCalledWith(true);
|
590
|
+
});
|
591
|
+
|
592
|
+
// Test closing modal
|
593
|
+
act(() => {
|
594
|
+
onModalStateChange(false);
|
595
|
+
});
|
596
|
+
|
597
|
+
await waitFor(() => {
|
598
|
+
expect(mockOnModalStateChange).toHaveBeenCalledWith(false);
|
599
|
+
});
|
600
|
+
|
601
|
+
expect(mockOnModalStateChange).toHaveBeenCalledTimes(2);
|
602
|
+
});
|
603
|
+
|
571
604
|
describe('Resource browser plugin', () => {
|
605
|
+
let PluginRenderSpy: jest.SpyInstance;
|
572
606
|
beforeEach(() => {
|
573
|
-
|
607
|
+
//@ts-ignore
|
608
|
+
PluginRenderSpy = jest.spyOn(Plugin.PluginRender, 'render');
|
574
609
|
});
|
575
610
|
afterEach(() => {
|
576
|
-
|
611
|
+
PluginRenderSpy.mockRestore();
|
577
612
|
});
|
578
613
|
|
579
614
|
it('Will default to a non plugin based render for initial load and selection of first source', async () => {
|
@@ -583,29 +618,29 @@ describe('Resource browser input', () => {
|
|
583
618
|
|
584
619
|
// Will render a default with no selected source
|
585
620
|
await waitFor(() => {
|
586
|
-
expect(
|
621
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
587
622
|
expect.objectContaining({
|
588
623
|
render: true,
|
589
624
|
type: null,
|
590
625
|
plugin: null,
|
591
626
|
}),
|
592
|
-
|
627
|
+
null,
|
593
628
|
);
|
594
629
|
|
595
|
-
expect(
|
630
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
596
631
|
expect.objectContaining({
|
597
632
|
render: false,
|
598
633
|
type: 'dam',
|
599
634
|
}),
|
600
|
-
|
635
|
+
null,
|
601
636
|
);
|
602
637
|
|
603
|
-
expect(
|
638
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
604
639
|
expect.objectContaining({
|
605
640
|
render: false,
|
606
641
|
type: 'matrix',
|
607
642
|
}),
|
608
|
-
|
643
|
+
null,
|
609
644
|
);
|
610
645
|
});
|
611
646
|
});
|
@@ -619,28 +654,28 @@ describe('Resource browser input', () => {
|
|
619
654
|
|
620
655
|
// Will render a default with no selected source
|
621
656
|
await waitFor(() => {
|
622
|
-
expect(
|
657
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
623
658
|
expect.objectContaining({
|
624
659
|
render: false,
|
625
660
|
type: null,
|
626
661
|
}),
|
627
|
-
|
662
|
+
null,
|
628
663
|
);
|
629
664
|
|
630
|
-
expect(
|
665
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
631
666
|
expect.objectContaining({
|
632
667
|
render: true,
|
633
668
|
type: 'dam',
|
634
669
|
}),
|
635
|
-
|
670
|
+
null,
|
636
671
|
);
|
637
672
|
|
638
|
-
expect(
|
673
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
639
674
|
expect.objectContaining({
|
640
675
|
render: false,
|
641
676
|
type: 'matrix',
|
642
677
|
}),
|
643
|
-
|
678
|
+
null,
|
644
679
|
);
|
645
680
|
});
|
646
681
|
|
@@ -672,28 +707,28 @@ describe('Resource browser input', () => {
|
|
672
707
|
|
673
708
|
// Will render a default with no selected source
|
674
709
|
await waitFor(() => {
|
675
|
-
expect(
|
710
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
676
711
|
expect.objectContaining({
|
677
712
|
render: true,
|
678
713
|
type: null,
|
679
714
|
}),
|
680
|
-
|
715
|
+
null,
|
681
716
|
);
|
682
717
|
|
683
|
-
expect(
|
718
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
684
719
|
expect.objectContaining({
|
685
720
|
render: true,
|
686
721
|
type: 'dam',
|
687
722
|
}),
|
688
|
-
|
723
|
+
null,
|
689
724
|
);
|
690
725
|
|
691
|
-
expect(
|
726
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
692
727
|
expect.objectContaining({
|
693
728
|
render: false,
|
694
729
|
type: 'matrix',
|
695
730
|
}),
|
696
|
-
|
731
|
+
null,
|
697
732
|
);
|
698
733
|
});
|
699
734
|
});
|
@@ -724,20 +759,20 @@ describe('Resource browser input', () => {
|
|
724
759
|
);
|
725
760
|
});
|
726
761
|
|
727
|
-
(
|
762
|
+
(PluginRenderSpy as unknown as jest.Mock).mockClear();
|
728
763
|
|
729
764
|
act(() => {
|
730
765
|
setSource({ ...matrixSource, plugin: pluginB });
|
731
766
|
});
|
732
767
|
|
733
768
|
await waitFor(() => {
|
734
|
-
expect(
|
769
|
+
expect(PluginRenderSpy).toHaveBeenCalledWith(
|
735
770
|
expect.objectContaining({
|
736
771
|
plugin: pluginB,
|
737
772
|
}),
|
738
773
|
expect.any(Object),
|
739
774
|
);
|
740
|
-
expect(
|
775
|
+
expect(PluginRenderSpy).not.toHaveBeenCalledWith(
|
741
776
|
expect.objectContaining({
|
742
777
|
plugin: pluginA,
|
743
778
|
}),
|
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
|
|
@@ -98,8 +99,9 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
98
99
|
const handleModalStateChange = useCallback(
|
99
100
|
(isOpen: boolean) => {
|
100
101
|
setIsModalOpen(isOpen);
|
102
|
+
onModalStateChangeExternalNotification?.(isOpen);
|
101
103
|
},
|
102
|
-
[setIsModalOpen],
|
104
|
+
[setIsModalOpen, onModalStateChangeExternalNotification],
|
103
105
|
);
|
104
106
|
|
105
107
|
// If the modal closes and we dont have a value clear the source state so it goes back to the launcher on re-open
|
@@ -130,6 +132,7 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
130
132
|
<div className="squiz-rb-scope">
|
131
133
|
<PluginRender
|
132
134
|
key="default"
|
135
|
+
ref={forwardRef} // This is passed to every plugin but only actually render on one at a time
|
133
136
|
type={null}
|
134
137
|
render={plugin === null}
|
135
138
|
inline={!!inline}
|
@@ -159,6 +162,7 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
159
162
|
return (
|
160
163
|
<PluginRender
|
161
164
|
key={thisPlugin.type}
|
165
|
+
ref={forwardRef} // This is passed to every plugin but only actually render on one at a time
|
162
166
|
type={thisPlugin.type}
|
163
167
|
render={thisPlugin.type === plugin?.type}
|
164
168
|
inline={!!inline}
|
@@ -183,4 +187,4 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
|
|
183
187
|
})}
|
184
188
|
</div>
|
185
189
|
);
|
186
|
-
};
|
190
|
+
});
|