@squiz/resource-browser 2.4.12 → 3.0.1-pre-alpha.0
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/README.md +4 -0
- package/lib/BrowseToSource/BrowseToSource.d.ts +8 -0
- package/lib/BrowseToSource/BrowseToSource.js +50 -0
- package/lib/Hooks/useAuth.js +11 -15
- package/lib/Hooks/useSelectedState.js +3 -7
- package/lib/Hooks/useSources.d.ts +2 -2
- package/lib/Hooks/useSources.js +19 -9
- package/lib/Icons/AdsClickIcon.d.ts +4 -0
- package/lib/Icons/AdsClickIcon.js +5 -0
- package/lib/Icons/ArrowDownIcon.d.ts +4 -0
- package/lib/Icons/ArrowDownIcon.js +5 -0
- package/lib/Icons/CircledLoopIcon.js +4 -11
- package/lib/MainContainer/MainContainer.d.ts +6 -4
- package/lib/MainContainer/MainContainer.js +33 -52
- package/lib/Plugin/Plugin.js +7 -14
- package/lib/ResourceBrowserContext/AuthProvider.js +9 -37
- package/lib/ResourceBrowserContext/ResourceBrowserContext.d.ts +1 -0
- package/lib/ResourceBrowserContext/ResourceBrowserContext.js +10 -39
- package/lib/ResourceBrowserInput/ResourceBrowserInput.d.ts +6 -4
- package/lib/ResourceBrowserInput/ResourceBrowserInput.js +5 -12
- package/lib/ResourceLauncher/ResourceLauncher.d.ts +8 -0
- package/lib/ResourceLauncher/ResourceLauncher.js +12 -0
- package/lib/ResourcePicker/ResourcePicker.js +20 -27
- package/lib/ResourcePicker/States/Error.js +6 -13
- package/lib/ResourcePicker/States/Loading.js +4 -11
- package/lib/ResourcePicker/States/Selected.js +12 -19
- package/lib/SourceDropdown/SourceDropdown.d.ts +2 -2
- package/lib/SourceDropdown/SourceDropdown.js +22 -48
- package/lib/SourceDropdownContainer/SourceDropdownContainer.d.ts +5 -0
- package/lib/SourceDropdownContainer/SourceDropdownContainer.js +12 -0
- package/lib/SourceList/SourceList.js +11 -16
- package/lib/index.css +102 -26
- package/lib/index.d.ts +4 -1
- package/lib/index.js +40 -66
- package/lib/types.d.ts +35 -3
- package/lib/types.js +5 -2
- package/lib/utils/authUtils.js +9 -16
- package/lib-esm/BrowseToSource/BrowseToSource.d.ts +8 -0
- package/lib-esm/BrowseToSource/BrowseToSource.js +50 -0
- package/lib-esm/Hooks/useAuth.d.ts +7 -0
- package/lib-esm/Hooks/useAuth.js +54 -0
- package/lib-esm/Hooks/useSelectedState.d.ts +15 -0
- package/lib-esm/Hooks/useSelectedState.js +12 -0
- package/lib-esm/Hooks/useSources.d.ts +14 -0
- package/lib-esm/Hooks/useSources.js +44 -0
- package/lib-esm/Icons/AdsClickIcon.d.ts +4 -0
- package/lib-esm/Icons/AdsClickIcon.js +5 -0
- package/lib-esm/Icons/ArrowDownIcon.d.ts +4 -0
- package/lib-esm/Icons/ArrowDownIcon.js +5 -0
- package/lib-esm/Icons/CircledLoopIcon.d.ts +4 -0
- package/lib-esm/Icons/CircledLoopIcon.js +5 -0
- package/lib-esm/MainContainer/MainContainer.d.ts +19 -0
- package/lib-esm/MainContainer/MainContainer.js +43 -0
- package/lib-esm/Plugin/Plugin.d.ts +13 -0
- package/lib-esm/Plugin/Plugin.js +12 -0
- package/lib-esm/ResourceBrowserContext/AuthProvider.d.ts +16 -0
- package/lib-esm/ResourceBrowserContext/AuthProvider.js +18 -0
- package/lib-esm/ResourceBrowserContext/ResourceBrowserContext.d.ts +15 -0
- package/lib-esm/ResourceBrowserContext/ResourceBrowserContext.js +26 -0
- package/lib-esm/ResourceBrowserInput/ResourceBrowserInput.d.ts +26 -0
- package/lib-esm/ResourceBrowserInput/ResourceBrowserInput.js +9 -0
- package/lib-esm/ResourceLauncher/ResourceLauncher.d.ts +8 -0
- package/lib-esm/ResourceLauncher/ResourceLauncher.js +12 -0
- package/lib-esm/ResourcePicker/ResourcePicker.d.ts +16 -0
- package/lib-esm/ResourcePicker/ResourcePicker.js +25 -0
- package/lib-esm/ResourcePicker/States/Error.d.ts +7 -0
- package/lib-esm/ResourcePicker/States/Error.js +6 -0
- package/lib-esm/ResourcePicker/States/Loading.d.ts +2 -0
- package/lib-esm/ResourcePicker/States/Loading.js +4 -0
- package/lib-esm/ResourcePicker/States/Selected.d.ts +15 -0
- package/lib-esm/ResourcePicker/States/Selected.js +20 -0
- package/lib-esm/SourceDropdown/SourceDropdown.d.ts +7 -0
- package/lib-esm/SourceDropdown/SourceDropdown.js +46 -0
- package/lib-esm/SourceDropdownContainer/SourceDropdownContainer.d.ts +5 -0
- package/lib-esm/SourceDropdownContainer/SourceDropdownContainer.js +12 -0
- package/lib-esm/SourceList/SourceList.d.ts +8 -0
- package/lib-esm/SourceList/SourceList.js +16 -0
- package/lib-esm/index.d.ts +18 -0
- package/lib-esm/index.js +79 -0
- package/lib-esm/types.d.ts +97 -0
- package/lib-esm/types.js +5 -0
- package/lib-esm/utils/authUtils.d.ts +5 -0
- package/lib-esm/utils/authUtils.js +31 -0
- package/package.json +18 -6
- package/src/BrowseToSource/BrowseToSource.spec.tsx +111 -0
- package/src/BrowseToSource/BrowseToSource.stories.tsx +29 -0
- package/src/BrowseToSource/BrowseToSource.tsx +111 -0
- package/src/Hooks/useSources.spec.ts +8 -4
- package/src/Hooks/useSources.ts +28 -13
- package/src/Icons/AdsClickIcon.tsx +11 -0
- package/src/Icons/ArrowDownIcon.tsx +11 -0
- package/src/MainContainer/MainContainer.spec.tsx +322 -108
- package/src/MainContainer/MainContainer.tsx +67 -27
- package/src/Plugin/Plugin.spec.tsx +2 -0
- package/src/ResourceBrowserContext/ResourceBrowserContext.spec.tsx +3 -0
- package/src/ResourceBrowserContext/ResourceBrowserContext.tsx +2 -0
- package/src/ResourceBrowserInput/ResourceBrowserInput.spec.tsx +7 -0
- package/src/ResourceBrowserInput/ResourceBrowserInput.tsx +16 -3
- package/src/ResourceLauncher/ResourceLauncher.spec.tsx +65 -0
- package/src/ResourceLauncher/ResourceLauncher.tsx +35 -0
- package/src/SourceDropdown/SourceDropdown.stories.tsx +1 -2
- package/src/SourceDropdown/SourceDropdown.tsx +8 -8
- package/src/SourceDropdownContainer/SourceDropdownContainer.spec.tsx +50 -0
- package/src/SourceDropdownContainer/SourceDropdownContainer.stories.tsx +62 -0
- package/src/SourceDropdownContainer/SourceDropdownContainer.tsx +27 -0
- package/src/__mocks__/MockModels.ts +16 -2
- package/src/__mocks__/PluginExample.tsx +8 -0
- package/src/__mocks__/StorybookHelpers.tsx +37 -1
- package/src/__mocks__/renderWithContext.tsx +1 -0
- package/src/index.spec.tsx +135 -41
- package/src/index.stories.tsx +12 -1
- package/src/index.tsx +45 -16
- package/src/types.ts +43 -3
- package/.eslintrc +0 -40
- package/.storybook/main.ts +0 -23
- package/.storybook/preview-body.html +0 -1
- package/.storybook/preview-head.html +0 -12
- package/.storybook/preview.ts +0 -16
- package/CHANGELOG.md +0 -244
- package/LICENSE.md +0 -15
- package/build.js +0 -21
- package/jest.config.ts +0 -30
- package/postcss.config.js +0 -21
- package/tailwind.config.cjs +0 -98
- package/tsconfig.json +0 -22
- package/tsconfig.storybook.json +0 -4
- package/tsconfig.test.json +0 -12
- package/vite.config.js +0 -20
@@ -1,9 +1,9 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { render, screen, waitFor, act, fireEvent } from '@testing-library/react';
|
3
3
|
import MainContainer from './MainContainer';
|
4
|
-
import { ResourceBrowserPlugin,
|
4
|
+
import { ResourceBrowserPlugin, ResourceBrowserSourceWithPlugin, PluginLaunchModeType } from '../types';
|
5
5
|
|
6
|
-
import { mockSource, mockResource } from '../__mocks__/MockModels';
|
6
|
+
import { mockSource, mockResource, mockSourceWithPlugin } from '../__mocks__/MockModels';
|
7
7
|
|
8
8
|
import SourceList from '../SourceList/SourceList'; // Import Functional Component
|
9
9
|
jest.mock('../SourceList/SourceList'); // Mock the Functional Component
|
@@ -11,12 +11,23 @@ const ActualSourceList = jest.requireActual('../SourceList/SourceList').default;
|
|
11
11
|
const MockSourceList = SourceList as jest.MockedFunction<typeof SourceList>; // Cast the mocked function so TS stops complaining
|
12
12
|
MockSourceList.mockImplementation(ActualSourceList); // Return the actual function unless overridden
|
13
13
|
|
14
|
+
import ResourceLauncher from '../ResourceLauncher/ResourceLauncher'; // Import Functional Component
|
15
|
+
jest.mock('../ResourceLauncher/ResourceLauncher'); // Mock the Functional Component
|
16
|
+
const ActualResourceLauncher = jest.requireActual('../ResourceLauncher/ResourceLauncher').default; // Grab the real copy of it as normally we don't want the mock
|
17
|
+
const MockResourceLauncher = ResourceLauncher as jest.MockedFunction<typeof ResourceLauncher>; // Cast the mocked function so TS stops complaining
|
18
|
+
MockResourceLauncher.mockImplementation(ActualResourceLauncher); // Return the actual function unless overridden
|
19
|
+
|
14
20
|
import SourceDropdown from '../SourceDropdown/SourceDropdown'; // Import Functional Component
|
15
21
|
jest.mock('../SourceDropdown/SourceDropdown'); // Mock the Functional Component
|
16
22
|
const ActualSourceDropdown = jest.requireActual('../SourceDropdown/SourceDropdown').default; // Grab the real copy of it as normally we don't want the mock
|
17
23
|
const MockSourceDropdown = SourceDropdown as jest.MockedFunction<typeof SourceDropdown>; // Cast the mocked function so TS stops complaining
|
18
24
|
MockSourceDropdown.mockImplementation(ActualSourceDropdown); // Return the actual function unless overridden
|
19
25
|
|
26
|
+
import SourceDropdownContainer from '../SourceDropdownContainer/SourceDropdownContainer';
|
27
|
+
jest.mock('../SourceDropdownContainer/SourceDropdownContainer', () => {
|
28
|
+
return jest.fn().mockReturnValue(<div />);
|
29
|
+
});
|
30
|
+
|
20
31
|
const mockSourceBrowserComponent = jest.fn();
|
21
32
|
const defaultProps = {
|
22
33
|
title: '',
|
@@ -29,6 +40,8 @@ const defaultProps = {
|
|
29
40
|
onClose: jest.fn(),
|
30
41
|
preselectedResource: null,
|
31
42
|
plugin: null,
|
43
|
+
pluginMode: null,
|
44
|
+
searchEnabled: false,
|
32
45
|
};
|
33
46
|
|
34
47
|
describe('MainContainer', () => {
|
@@ -41,13 +54,13 @@ describe('MainContainer', () => {
|
|
41
54
|
expect(onClose).toHaveBeenCalled();
|
42
55
|
});
|
43
56
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
});
|
57
|
+
it('should render Environment Selector title', () => {
|
58
|
+
render(<MainContainer {...defaultProps} title="Select MyAsset" />);
|
59
|
+
expect(screen.getByText('Select MyAsset')).toBeInTheDocument();
|
60
|
+
expect(screen.queryByText('Environment Selector')).not.toBeInTheDocument();
|
61
|
+
});
|
50
62
|
|
63
|
+
describe('no source selected', () => {
|
51
64
|
it('should not render source selector dropdown', () => {
|
52
65
|
render(<MainContainer {...defaultProps} />);
|
53
66
|
expect(MockSourceDropdown).not.toHaveBeenCalled();
|
@@ -58,8 +71,15 @@ describe('MainContainer', () => {
|
|
58
71
|
expect(mockSourceBrowserComponent).not.toHaveBeenCalled();
|
59
72
|
});
|
60
73
|
|
74
|
+
it('should render resource launcher list when searchEnabled using provided props', () => {
|
75
|
+
const sources: ResourceBrowserSourceWithPlugin[] = [];
|
76
|
+
const onSourceSelect = jest.fn();
|
77
|
+
render(<MainContainer {...defaultProps} searchEnabled={true} sources={sources} onSourceSelect={onSourceSelect} />);
|
78
|
+
expect(MockResourceLauncher).toHaveBeenCalledWith({ sources, onSourceSelect }, {});
|
79
|
+
});
|
80
|
+
|
61
81
|
it('should render source list using provided props', () => {
|
62
|
-
const sources:
|
82
|
+
const sources: ResourceBrowserSourceWithPlugin[] = [];
|
63
83
|
const onSourceSelect = jest.fn();
|
64
84
|
render(<MainContainer {...defaultProps} sources={sources} onSourceSelect={onSourceSelect} />);
|
65
85
|
expect(MockSourceList).toHaveBeenCalledWith({ sources, onSourceSelect }, {});
|
@@ -68,136 +88,330 @@ describe('MainContainer', () => {
|
|
68
88
|
|
69
89
|
describe('source selected', () => {
|
70
90
|
const source = mockSource({ type: 'dam' });
|
71
|
-
const plugin = {
|
72
|
-
type: 'dam',
|
73
|
-
createHeaderPortal: false,
|
74
|
-
sourceBrowserComponent: mockSourceBrowserComponent,
|
75
|
-
renderSelectedResource: jest.fn(),
|
76
|
-
useResolveResource: jest.fn(),
|
77
|
-
} as ResourceBrowserPlugin;
|
78
|
-
it('should render Environment Selector title', () => {
|
79
|
-
render(<MainContainer {...defaultProps} title="Select MyAsset" selectedSource={source} plugin={plugin} />);
|
80
|
-
expect(screen.getByText('Select MyAsset')).toBeInTheDocument();
|
81
|
-
expect(screen.queryByText('Environment Selector')).not.toBeInTheDocument();
|
82
|
-
});
|
83
91
|
|
84
|
-
it('should
|
85
|
-
const
|
86
|
-
const
|
92
|
+
it('it should create a default source switcher if header portal not requested and sources > 1', async () => {
|
93
|
+
const source1 = mockSourceWithPlugin();
|
94
|
+
const source2 = mockSourceWithPlugin();
|
95
|
+
|
96
|
+
const plugin = {
|
97
|
+
type: 'dam',
|
98
|
+
createHeaderPortal: false,
|
99
|
+
sourceBrowserComponent: jest.fn,
|
100
|
+
renderSelectedResource: jest.fn(),
|
101
|
+
useResolveResource: jest.fn(),
|
102
|
+
sourceSearchComponent: jest.fn(),
|
103
|
+
renderResourceLauncher: jest.fn(),
|
104
|
+
} as ResourceBrowserPlugin;
|
87
105
|
render(
|
88
106
|
<MainContainer
|
89
107
|
{...defaultProps}
|
90
|
-
sources={
|
91
|
-
selectedSource={
|
108
|
+
sources={[source1, source2]}
|
109
|
+
selectedSource={source1}
|
92
110
|
plugin={plugin}
|
93
|
-
|
111
|
+
pluginMode={{ type: PluginLaunchModeType.Browse }}
|
94
112
|
/>,
|
95
113
|
);
|
96
|
-
|
114
|
+
|
115
|
+
expect(SourceDropdownContainer).toHaveBeenCalled();
|
97
116
|
});
|
98
117
|
|
99
|
-
it('should not
|
100
|
-
const
|
101
|
-
|
118
|
+
it('it should not create a default source switcher if header portal not requested and sources < 2', async () => {
|
119
|
+
const source1 = mockSourceWithPlugin();
|
120
|
+
|
121
|
+
const plugin = {
|
122
|
+
type: 'dam',
|
123
|
+
createHeaderPortal: false,
|
124
|
+
sourceBrowserComponent: jest.fn,
|
125
|
+
renderSelectedResource: jest.fn(),
|
126
|
+
useResolveResource: jest.fn(),
|
127
|
+
sourceSearchComponent: jest.fn(),
|
128
|
+
renderResourceLauncher: jest.fn(),
|
129
|
+
} as ResourceBrowserPlugin;
|
102
130
|
render(
|
103
131
|
<MainContainer
|
104
132
|
{...defaultProps}
|
105
|
-
sources={
|
106
|
-
selectedSource={
|
133
|
+
sources={[source1]}
|
134
|
+
selectedSource={source1}
|
107
135
|
plugin={plugin}
|
108
|
-
|
136
|
+
pluginMode={{ type: PluginLaunchModeType.Browse }}
|
109
137
|
/>,
|
110
138
|
);
|
111
|
-
|
139
|
+
|
140
|
+
expect(SourceDropdownContainer).not.toHaveBeenCalled();
|
112
141
|
});
|
113
142
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
expect(
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
143
|
+
describe('Browser mode', () => {
|
144
|
+
it('should create header portal for plugin', async () => {
|
145
|
+
const mockFunctionalComponent = jest.fn().mockReturnValue(<div>Source UI has been rendered</div>);
|
146
|
+
const mockSourceBrowserComponent = jest.fn().mockReturnValue(mockFunctionalComponent);
|
147
|
+
const plugin = {
|
148
|
+
type: 'dam',
|
149
|
+
createHeaderPortal: true,
|
150
|
+
sourceBrowserComponent: mockSourceBrowserComponent,
|
151
|
+
renderSelectedResource: jest.fn(),
|
152
|
+
useResolveResource: jest.fn(),
|
153
|
+
sourceSearchComponent: jest.fn(),
|
154
|
+
renderResourceLauncher: jest.fn(),
|
155
|
+
} as ResourceBrowserPlugin;
|
156
|
+
render(<MainContainer {...defaultProps} selectedSource={source} plugin={plugin} />);
|
157
|
+
expect(mockSourceBrowserComponent).toHaveBeenCalled();
|
158
|
+
|
159
|
+
await waitFor(() => {
|
160
|
+
expect(mockFunctionalComponent).toHaveBeenCalledWith(
|
161
|
+
expect.objectContaining({
|
162
|
+
headerPortal: expect.any(Element),
|
163
|
+
}),
|
164
|
+
{},
|
165
|
+
);
|
166
|
+
});
|
167
|
+
});
|
168
|
+
|
169
|
+
it('preselectedResource: should pass expected params to plugin UI generator functional component', async () => {
|
170
|
+
const mockFunctionalComponent = jest.fn().mockReturnValue(<div>Source UI has been rendered</div>);
|
171
|
+
const mockSourceBrowserComponent = jest.fn().mockReturnValue(mockFunctionalComponent);
|
172
|
+
const plugin = {
|
173
|
+
type: 'dam',
|
174
|
+
createHeaderPortal: true,
|
175
|
+
sourceBrowserComponent: mockSourceBrowserComponent,
|
176
|
+
renderSelectedResource: jest.fn(),
|
177
|
+
useResolveResource: jest.fn(),
|
178
|
+
sourceSearchComponent: jest.fn(),
|
179
|
+
renderResourceLauncher: jest.fn(),
|
180
|
+
} as ResourceBrowserPlugin;
|
181
|
+
|
182
|
+
const resource = mockResource();
|
183
|
+
const props = {
|
184
|
+
allowedTypes: ['image'],
|
185
|
+
preselectedResource: resource,
|
186
|
+
};
|
187
|
+
|
188
|
+
render(<MainContainer {...defaultProps} selectedSource={source} plugin={plugin} {...props} />);
|
189
|
+
expect(mockSourceBrowserComponent).toHaveBeenCalled();
|
190
|
+
|
191
|
+
await waitFor(() => {
|
192
|
+
expect(mockFunctionalComponent).toHaveBeenCalledWith(
|
193
|
+
expect.objectContaining({
|
194
|
+
source: source,
|
195
|
+
allowedTypes: props.allowedTypes,
|
196
|
+
headerPortal: expect.any(Element),
|
197
|
+
preselectedResource: resource,
|
198
|
+
}),
|
199
|
+
{},
|
200
|
+
);
|
201
|
+
});
|
202
|
+
});
|
203
|
+
|
204
|
+
it('browseTo: should pass expected params to plugin UI generator functional component', async () => {
|
205
|
+
const mockFunctionalComponent = jest.fn().mockReturnValue(<div>Source UI has been rendered</div>);
|
206
|
+
const mockSourceBrowserComponent = jest.fn().mockReturnValue(mockFunctionalComponent);
|
207
|
+
const plugin = {
|
208
|
+
type: 'dam',
|
209
|
+
createHeaderPortal: true,
|
210
|
+
sourceBrowserComponent: mockSourceBrowserComponent,
|
211
|
+
renderSelectedResource: jest.fn(),
|
212
|
+
useResolveResource: jest.fn(),
|
213
|
+
sourceSearchComponent: jest.fn(),
|
214
|
+
renderResourceLauncher: jest.fn(),
|
215
|
+
} as ResourceBrowserPlugin;
|
216
|
+
|
217
|
+
const resource = mockResource();
|
218
|
+
const browseTo = { resourceId: resource.id, sourceId: resource.source.id };
|
219
|
+
const props = {
|
220
|
+
allowedTypes: ['image'],
|
221
|
+
};
|
222
|
+
|
223
|
+
render(
|
224
|
+
<MainContainer
|
225
|
+
{...defaultProps}
|
226
|
+
selectedSource={source}
|
227
|
+
plugin={plugin}
|
228
|
+
pluginMode={{
|
229
|
+
type: PluginLaunchModeType.Browse,
|
230
|
+
args: { browseTo },
|
231
|
+
}}
|
232
|
+
{...props}
|
233
|
+
/>,
|
133
234
|
);
|
235
|
+
expect(mockSourceBrowserComponent).toHaveBeenCalled();
|
236
|
+
|
237
|
+
await waitFor(() => {
|
238
|
+
expect(mockFunctionalComponent).toHaveBeenCalledWith(
|
239
|
+
expect.objectContaining({
|
240
|
+
source: source,
|
241
|
+
allowedTypes: props.allowedTypes,
|
242
|
+
headerPortal: expect.any(Element),
|
243
|
+
browseTo,
|
244
|
+
}),
|
245
|
+
{},
|
246
|
+
);
|
247
|
+
});
|
134
248
|
});
|
135
|
-
});
|
136
249
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
250
|
+
it('should pass onSelected to plugin UI generator FC when invoked calls change and close functions', async () => {
|
251
|
+
const mockFunctionalComponent = jest.fn().mockReturnValue(<div>Source UI has been rendered</div>);
|
252
|
+
const mockSourceBrowserComponent = jest.fn().mockReturnValue(mockFunctionalComponent);
|
253
|
+
const plugin = {
|
254
|
+
type: 'dam',
|
255
|
+
createHeaderPortal: true,
|
256
|
+
sourceBrowserComponent: mockSourceBrowserComponent,
|
257
|
+
renderSelectedResource: jest.fn(),
|
258
|
+
useResolveResource: jest.fn(),
|
259
|
+
sourceSearchComponent: jest.fn(),
|
260
|
+
renderResourceLauncher: jest.fn(),
|
261
|
+
} as ResourceBrowserPlugin;
|
262
|
+
|
263
|
+
const onChange = jest.fn();
|
264
|
+
const onClose = jest.fn();
|
147
265
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
await waitFor(() => {
|
158
|
-
expect(mockFunctionalComponent).toHaveBeenCalledWith(
|
159
|
-
expect.objectContaining({
|
160
|
-
source: source,
|
161
|
-
allowedTypes: props.allowedTypes,
|
162
|
-
headerPortal: expect.any(Element),
|
163
|
-
preselectedResource: resource,
|
164
|
-
}),
|
165
|
-
{},
|
266
|
+
render(
|
267
|
+
<MainContainer
|
268
|
+
{...defaultProps}
|
269
|
+
searchEnabled={true}
|
270
|
+
selectedSource={source}
|
271
|
+
plugin={plugin}
|
272
|
+
onChange={onChange}
|
273
|
+
onClose={onClose}
|
274
|
+
/>,
|
166
275
|
);
|
276
|
+
expect(mockSourceBrowserComponent).toHaveBeenCalled();
|
277
|
+
|
278
|
+
await waitFor(() => {
|
279
|
+
expect(mockFunctionalComponent).toHaveBeenCalledWith(
|
280
|
+
expect.objectContaining({
|
281
|
+
onSelected: expect.any(Function),
|
282
|
+
searchEnabled: true,
|
283
|
+
}),
|
284
|
+
{},
|
285
|
+
);
|
286
|
+
});
|
287
|
+
|
288
|
+
const resource = mockResource();
|
289
|
+
const { onSelected } = mockFunctionalComponent.mock.calls[0][0];
|
290
|
+
onSelected(resource);
|
291
|
+
expect(onChange).toHaveBeenCalledWith(resource);
|
292
|
+
expect(onClose).toHaveBeenCalled();
|
167
293
|
});
|
168
294
|
});
|
169
295
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
296
|
+
describe('Search mode', () => {
|
297
|
+
it('should create header portal for plugin', async () => {
|
298
|
+
const mockFunctionalComponent = jest.fn().mockReturnValue(<div>Source search UI has been rendered</div>);
|
299
|
+
const mockSourceSearchComponent = jest.fn().mockReturnValue(mockFunctionalComponent);
|
300
|
+
const plugin = {
|
301
|
+
type: 'dam',
|
302
|
+
createHeaderPortal: true,
|
303
|
+
sourceBrowserComponent: jest.fn,
|
304
|
+
renderSelectedResource: jest.fn(),
|
305
|
+
useResolveResource: jest.fn(),
|
306
|
+
sourceSearchComponent: mockSourceSearchComponent,
|
307
|
+
renderResourceLauncher: jest.fn(),
|
308
|
+
} as ResourceBrowserPlugin;
|
309
|
+
render(
|
310
|
+
<MainContainer
|
311
|
+
{...defaultProps}
|
312
|
+
selectedSource={source}
|
313
|
+
plugin={plugin}
|
314
|
+
pluginMode={{ type: PluginLaunchModeType.Search }}
|
315
|
+
/>,
|
316
|
+
);
|
317
|
+
expect(mockSourceSearchComponent).toHaveBeenCalled();
|
180
318
|
|
181
|
-
|
182
|
-
|
319
|
+
await waitFor(() => {
|
320
|
+
expect(mockFunctionalComponent).toHaveBeenCalledWith(
|
321
|
+
expect.objectContaining({
|
322
|
+
headerPortal: expect.any(Element),
|
323
|
+
}),
|
324
|
+
{},
|
325
|
+
);
|
326
|
+
});
|
327
|
+
});
|
183
328
|
|
184
|
-
|
185
|
-
|
329
|
+
it('should pass expected params to plugin UI generator functional component', async () => {
|
330
|
+
const mockFunctionalComponent = jest.fn().mockReturnValue(<div>Source search UI has been rendered</div>);
|
331
|
+
const mockSourceSearchComponent = jest.fn().mockReturnValue(mockFunctionalComponent);
|
332
|
+
const plugin = {
|
333
|
+
type: 'dam',
|
334
|
+
createHeaderPortal: true,
|
335
|
+
sourceBrowserComponent: jest.fn(),
|
336
|
+
renderSelectedResource: jest.fn(),
|
337
|
+
useResolveResource: jest.fn(),
|
338
|
+
sourceSearchComponent: mockSourceSearchComponent,
|
339
|
+
renderResourceLauncher: jest.fn(),
|
340
|
+
} as ResourceBrowserPlugin;
|
186
341
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
342
|
+
const props = {
|
343
|
+
allowedTypes: ['image'],
|
344
|
+
};
|
345
|
+
|
346
|
+
render(
|
347
|
+
<MainContainer
|
348
|
+
{...defaultProps}
|
349
|
+
selectedSource={source}
|
350
|
+
plugin={plugin}
|
351
|
+
pluginMode={{ type: PluginLaunchModeType.Search, args: { query: 'myQuery' } }}
|
352
|
+
{...props}
|
353
|
+
/>,
|
193
354
|
);
|
355
|
+
expect(mockSourceSearchComponent).toHaveBeenCalled();
|
356
|
+
|
357
|
+
await waitFor(() => {
|
358
|
+
expect(mockFunctionalComponent).toHaveBeenCalledWith(
|
359
|
+
expect.objectContaining({
|
360
|
+
source: source,
|
361
|
+
allowedTypes: props.allowedTypes,
|
362
|
+
headerPortal: expect.any(Element),
|
363
|
+
query: 'myQuery',
|
364
|
+
}),
|
365
|
+
{},
|
366
|
+
);
|
367
|
+
});
|
194
368
|
});
|
195
369
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
370
|
+
it('should pass onSelected to plugin UI generator FC when invoked calls change and close functions', async () => {
|
371
|
+
const mockFunctionalComponent = jest.fn().mockReturnValue(<div>Source search UI has been rendered</div>);
|
372
|
+
const mockSourceSearchComponent = jest.fn().mockReturnValue(mockFunctionalComponent);
|
373
|
+
const plugin = {
|
374
|
+
type: 'dam',
|
375
|
+
createHeaderPortal: true,
|
376
|
+
sourceBrowserComponent: jest.fn(),
|
377
|
+
renderSelectedResource: jest.fn(),
|
378
|
+
useResolveResource: jest.fn(),
|
379
|
+
sourceSearchComponent: mockSourceSearchComponent,
|
380
|
+
renderResourceLauncher: jest.fn(),
|
381
|
+
} as ResourceBrowserPlugin;
|
382
|
+
|
383
|
+
const onChange = jest.fn();
|
384
|
+
const onClose = jest.fn();
|
385
|
+
|
386
|
+
render(
|
387
|
+
<MainContainer
|
388
|
+
{...defaultProps}
|
389
|
+
selectedSource={source}
|
390
|
+
searchEnabled={true}
|
391
|
+
plugin={plugin}
|
392
|
+
pluginMode={{ type: PluginLaunchModeType.Search, args: { query: 'myQuery' } }}
|
393
|
+
onChange={onChange}
|
394
|
+
onClose={onClose}
|
395
|
+
/>,
|
396
|
+
);
|
397
|
+
expect(mockSourceSearchComponent).toHaveBeenCalled();
|
398
|
+
|
399
|
+
await waitFor(() => {
|
400
|
+
expect(mockFunctionalComponent).toHaveBeenCalledWith(
|
401
|
+
expect.objectContaining({
|
402
|
+
onSelected: expect.any(Function),
|
403
|
+
searchEnabled: true,
|
404
|
+
}),
|
405
|
+
{},
|
406
|
+
);
|
407
|
+
});
|
408
|
+
|
409
|
+
const resource = mockResource();
|
410
|
+
const { onSelected } = mockFunctionalComponent.mock.calls[0][0];
|
411
|
+
onSelected(resource);
|
412
|
+
expect(onChange).toHaveBeenCalledWith(resource);
|
413
|
+
expect(onClose).toHaveBeenCalled();
|
414
|
+
});
|
201
415
|
});
|
202
416
|
});
|
203
417
|
});
|
@@ -1,21 +1,34 @@
|
|
1
1
|
import React, { useCallback, useState } from 'react';
|
2
2
|
import { DOMAttributes, FocusableElement } from '@react-types/shared';
|
3
3
|
|
4
|
-
import SourceDropdown from '../SourceDropdown/SourceDropdown';
|
5
4
|
import SourceList from '../SourceList/SourceList';
|
6
|
-
import
|
5
|
+
import ResourceLauncher from '../ResourceLauncher/ResourceLauncher';
|
6
|
+
import SourceDropdownContainer from '../SourceDropdownContainer/SourceDropdownContainer';
|
7
|
+
import SourceDropdown from '../SourceDropdown/SourceDropdown';
|
8
|
+
import {
|
9
|
+
ResourceBrowserPlugin,
|
10
|
+
ResourceBrowserSource,
|
11
|
+
ResourceBrowserResource,
|
12
|
+
ResourceBrowserSourceWithPlugin,
|
13
|
+
PluginLaunchMode,
|
14
|
+
PluginLaunchModeType,
|
15
|
+
ResourceBrowserSearchUIArgs,
|
16
|
+
ResourceBrowserUIArgs,
|
17
|
+
} from '../types';
|
7
18
|
|
8
19
|
interface MainContainerProps {
|
9
20
|
title: string;
|
10
21
|
titleAriaProps: DOMAttributes<FocusableElement>;
|
11
22
|
allowedTypes: string[] | undefined;
|
12
|
-
sources:
|
23
|
+
sources: ResourceBrowserSourceWithPlugin[];
|
13
24
|
selectedSource: ResourceBrowserSource | null;
|
14
|
-
onSourceSelect(source: ResourceBrowserSource
|
25
|
+
onSourceSelect(source: ResourceBrowserSource, mode?: PluginLaunchMode): void;
|
15
26
|
onChange(resource: ResourceBrowserResource | null): void;
|
16
27
|
onClose: () => void;
|
17
28
|
preselectedResource?: ResourceBrowserResource | null;
|
18
29
|
plugin: ResourceBrowserPlugin | null;
|
30
|
+
pluginMode: PluginLaunchMode | null;
|
31
|
+
searchEnabled: boolean;
|
19
32
|
}
|
20
33
|
|
21
34
|
function MainContainer({
|
@@ -29,9 +42,13 @@ function MainContainer({
|
|
29
42
|
onClose,
|
30
43
|
preselectedResource,
|
31
44
|
plugin,
|
45
|
+
pluginMode,
|
46
|
+
searchEnabled,
|
32
47
|
}: MainContainerProps) {
|
33
48
|
const [headerPortal, setHeaderPortal] = useState<HTMLDivElement | null>(null);
|
49
|
+
const pluginToRender = pluginMode ? pluginMode.type : PluginLaunchModeType.Browse; // Cant default a 'null' property in signature so set it here
|
34
50
|
const SourceBrowser = plugin?.sourceBrowserComponent();
|
51
|
+
const SourceSearch = plugin?.sourceSearchComponent();
|
35
52
|
|
36
53
|
// Can't use a useRef as it wont update on change when a source is selected, so need to use a ref callback to store in state
|
37
54
|
const setHeaderPortalRef = useCallback(
|
@@ -46,21 +63,22 @@ function MainContainer({
|
|
46
63
|
// MainContainer will either render the source list view if no source is set or the plugins UI if a source has been selected
|
47
64
|
return (
|
48
65
|
<div className="relative flex flex-col h-full text-gray-800">
|
49
|
-
<div className="flex items-center py-
|
66
|
+
<div className="flex items-center py-3 pl-6 pr-10 min-h-[68px]">
|
50
67
|
<h2 {...titleAriaProps} className="text-xl leading-6 text-gray-800 font-semibold mr-6">
|
51
|
-
{
|
52
|
-
{plugin && title}
|
68
|
+
{title}
|
53
69
|
</h2>
|
54
70
|
|
55
71
|
{plugin && selectedSource && (
|
56
72
|
<>
|
57
|
-
{
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
73
|
+
{/* Custom header if plugin supports it */}
|
74
|
+
{plugin.createHeaderPortal && <div ref={setHeaderPortalRef} className="squiz-rb-plugin"></div>}
|
75
|
+
{/* Default header if plugin does not support */}
|
76
|
+
{!plugin.createHeaderPortal && sources.length > 1 && (
|
77
|
+
<SourceDropdownContainer isCollapsed={false} onExpand={() => {}}>
|
78
|
+
<div className={`border-l border-blue-200 pl-1 w-[204px]`}>
|
79
|
+
<SourceDropdown sources={sources} selectedSource={selectedSource} onSourceSelect={onSourceSelect} />
|
80
|
+
</div>
|
81
|
+
</SourceDropdownContainer>
|
64
82
|
)}
|
65
83
|
</>
|
66
84
|
)}
|
@@ -80,21 +98,43 @@ function MainContainer({
|
|
80
98
|
</button>
|
81
99
|
</div>
|
82
100
|
<div className="border-t border-gray-300 overflow-y-hidden">
|
83
|
-
{plugin && selectedSource &&
|
84
|
-
<div className=
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
101
|
+
{plugin && selectedSource && (
|
102
|
+
<div className={`squiz-rb-plugin squiz-rb-plugin--${pluginMode?.type}`}>
|
103
|
+
{pluginToRender === PluginLaunchModeType.Browse && SourceBrowser && (
|
104
|
+
<SourceBrowser
|
105
|
+
source={selectedSource}
|
106
|
+
sources={sources}
|
107
|
+
onSourceSelect={onSourceSelect}
|
108
|
+
allowedTypes={allowedTypes}
|
109
|
+
headerPortal={plugin.createHeaderPortal && headerPortal ? headerPortal : undefined}
|
110
|
+
searchEnabled={searchEnabled}
|
111
|
+
preselectedResource={preselectedResource || undefined}
|
112
|
+
browseTo={(pluginMode?.args as ResourceBrowserUIArgs)?.browseTo}
|
113
|
+
onSelected={(resource: ResourceBrowserResource) => {
|
114
|
+
onChange(resource);
|
115
|
+
onClose();
|
116
|
+
}}
|
117
|
+
/>
|
118
|
+
)}
|
119
|
+
{pluginToRender === PluginLaunchModeType.Search && SourceSearch && (
|
120
|
+
<SourceSearch
|
121
|
+
source={selectedSource}
|
122
|
+
sources={sources}
|
123
|
+
onSourceSelect={onSourceSelect}
|
124
|
+
allowedTypes={allowedTypes}
|
125
|
+
headerPortal={plugin.createHeaderPortal && headerPortal ? headerPortal : undefined}
|
126
|
+
searchEnabled={searchEnabled}
|
127
|
+
query={(pluginMode?.args as ResourceBrowserSearchUIArgs)?.query}
|
128
|
+
onSelected={(resource: ResourceBrowserResource) => {
|
129
|
+
onChange(resource);
|
130
|
+
onClose();
|
131
|
+
}}
|
132
|
+
/>
|
133
|
+
)}
|
95
134
|
</div>
|
96
135
|
)}
|
97
|
-
{!selectedSource && <
|
136
|
+
{!selectedSource && searchEnabled && <ResourceLauncher sources={sources} onSourceSelect={onSourceSelect} />}
|
137
|
+
{!selectedSource && !searchEnabled && <SourceList sources={sources} onSourceSelect={onSourceSelect} />}
|
98
138
|
</div>
|
99
139
|
</div>
|
100
140
|
);
|