@squiz/resource-browser 1.69.2 → 2.1.9-rc.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.
Files changed (141) hide show
  1. package/CHANGELOG.md +89 -38
  2. package/LICENSE.md +15 -0
  3. package/README.md +9 -0
  4. package/jest.config.ts +22 -21
  5. package/lib/Hooks/useSelectedState.d.ts +15 -0
  6. package/lib/Hooks/useSelectedState.js +16 -0
  7. package/lib/Hooks/useSources.d.ts +6 -6
  8. package/lib/Hooks/useSources.js +26 -1
  9. package/lib/MainContainer/MainContainer.d.ts +17 -0
  10. package/lib/MainContainer/MainContainer.js +61 -0
  11. package/lib/Plugin/Plugin.d.ts +13 -0
  12. package/lib/Plugin/Plugin.js +17 -0
  13. package/lib/ResourceBrowserContext/ResourceBrowserContext.d.ts +2 -3
  14. package/lib/ResourceBrowserContext/ResourceBrowserContext.js +4 -17
  15. package/lib/ResourceBrowserInput/ResourceBrowserInput.d.ts +24 -0
  16. package/lib/ResourceBrowserInput/ResourceBrowserInput.js +16 -0
  17. package/lib/ResourcePicker/ResourcePicker.d.ts +6 -4
  18. package/lib/ResourcePicker/ResourcePicker.js +14 -8
  19. package/lib/ResourcePicker/States/Selected.d.ts +10 -4
  20. package/lib/ResourcePicker/States/Selected.js +11 -32
  21. package/lib/SourceDropdown/SourceDropdown.d.ts +5 -11
  22. package/lib/SourceDropdown/SourceDropdown.js +20 -99
  23. package/lib/SourceList/SourceList.d.ts +5 -16
  24. package/lib/SourceList/SourceList.js +14 -75
  25. package/lib/index.css +42 -202
  26. package/lib/index.d.ts +7 -7
  27. package/lib/index.js +69 -13
  28. package/lib/types.d.ts +41 -59
  29. package/package.json +82 -80
  30. package/src/Hooks/useSelectedState.spec.ts +46 -0
  31. package/src/Hooks/useSelectedState.ts +22 -0
  32. package/src/Hooks/useSources.spec.ts +60 -13
  33. package/src/Hooks/useSources.ts +35 -5
  34. package/src/Icons/CircledLoopIcon.tsx +8 -8
  35. package/src/MainContainer/MainContainer.spec.tsx +203 -0
  36. package/src/MainContainer/MainContainer.stories.tsx +62 -0
  37. package/src/MainContainer/MainContainer.tsx +101 -0
  38. package/src/Plugin/Plugin.spec.tsx +46 -0
  39. package/src/Plugin/Plugin.tsx +20 -0
  40. package/src/ResourceBrowserContext/ResourceBrowserContext.spec.tsx +65 -106
  41. package/src/ResourceBrowserContext/ResourceBrowserContext.tsx +24 -39
  42. package/src/ResourceBrowserInput/ResourceBrowserInput.spec.tsx +192 -0
  43. package/src/ResourceBrowserInput/ResourceBrowserInput.tsx +81 -0
  44. package/src/ResourcePicker/ResourcePicker.spec.tsx +159 -116
  45. package/src/ResourcePicker/ResourcePicker.stories.tsx +28 -24
  46. package/src/ResourcePicker/ResourcePicker.tsx +79 -59
  47. package/src/ResourcePicker/States/Error.tsx +8 -8
  48. package/src/ResourcePicker/States/Loading.tsx +3 -3
  49. package/src/ResourcePicker/States/Selected.tsx +66 -73
  50. package/src/ResourcePicker/mock-image-resource.json +25 -47
  51. package/src/ResourcePicker/mock-resource.json +11 -13
  52. package/src/ResourcePicker/resource-picker.scss +13 -13
  53. package/src/SourceDropdown/SourceDropdown.spec.tsx +65 -391
  54. package/src/SourceDropdown/SourceDropdown.stories.tsx +21 -24
  55. package/src/SourceDropdown/SourceDropdown.tsx +80 -258
  56. package/src/SourceList/SourceList.spec.tsx +37 -430
  57. package/src/SourceList/SourceList.stories.tsx +17 -37
  58. package/src/SourceList/SourceList.tsx +28 -155
  59. package/src/__mocks__/MockModels.ts +56 -25
  60. package/src/__mocks__/PluginExample.tsx +98 -0
  61. package/src/__mocks__/StorybookHelpers.tsx +141 -0
  62. package/src/__mocks__/renderWithContext.tsx +14 -18
  63. package/src/__mocks__/sample-sources.json +32 -0
  64. package/src/index.scss +18 -8
  65. package/src/index.spec.tsx +277 -99
  66. package/src/index.stories.tsx +65 -39
  67. package/src/index.tsx +119 -57
  68. package/src/types.ts +54 -63
  69. package/tailwind.config.cjs +92 -92
  70. package/vite.config.js +12 -12
  71. package/lib/Hooks/useCategorisedSources.d.ts +0 -14
  72. package/lib/Hooks/useCategorisedSources.js +0 -38
  73. package/lib/Hooks/useChildResources.d.ts +0 -16
  74. package/lib/Hooks/useChildResources.js +0 -13
  75. package/lib/Hooks/usePreselectedResourcePath.d.ts +0 -20
  76. package/lib/Hooks/usePreselectedResourcePath.js +0 -31
  77. package/lib/Hooks/useRecentLocations.d.ts +0 -5
  78. package/lib/Hooks/useRecentLocations.js +0 -38
  79. package/lib/Hooks/useRecentResourcesPaths.d.ts +0 -20
  80. package/lib/Hooks/useRecentResourcesPaths.js +0 -30
  81. package/lib/Hooks/useResource.d.ts +0 -28
  82. package/lib/Hooks/useResource.js +0 -25
  83. package/lib/Hooks/useResourcePath.d.ts +0 -16
  84. package/lib/Hooks/useResourcePath.js +0 -64
  85. package/lib/Icons/HistoryIcon.d.ts +0 -4
  86. package/lib/Icons/HistoryIcon.js +0 -13
  87. package/lib/PreviewPanel/PreviewPanel.d.ts +0 -5
  88. package/lib/PreviewPanel/PreviewPanel.js +0 -8
  89. package/lib/PreviewPanel/details/MatrixResource.d.ts +0 -7
  90. package/lib/PreviewPanel/details/MatrixResource.js +0 -35
  91. package/lib/ResourceBreadcrumb/ResourceBreadcrumb.d.ts +0 -9
  92. package/lib/ResourceBreadcrumb/ResourceBreadcrumb.js +0 -54
  93. package/lib/ResourceList/ResourceList.d.ts +0 -18
  94. package/lib/ResourceList/ResourceList.js +0 -49
  95. package/lib/ResourcePickerContainer/ResourcePickerContainer.d.ts +0 -17
  96. package/lib/ResourcePickerContainer/ResourcePickerContainer.js +0 -166
  97. package/lib/StatusIndicator/StatusIndicator.d.ts +0 -8
  98. package/lib/StatusIndicator/StatusIndicator.js +0 -27
  99. package/lib/utils/findBestMatchLineage.d.ts +0 -2
  100. package/lib/utils/findBestMatchLineage.js +0 -28
  101. package/lib/utils/uuid.d.ts +0 -1
  102. package/lib/utils/uuid.js +0 -6
  103. package/src/Hooks/useCategorisedSources.spec.ts +0 -39
  104. package/src/Hooks/useCategorisedSources.ts +0 -46
  105. package/src/Hooks/useChildResources.spec.ts +0 -29
  106. package/src/Hooks/useChildResources.ts +0 -21
  107. package/src/Hooks/usePreselectedResourcePath.ts +0 -54
  108. package/src/Hooks/useRecentLocations.spec.ts +0 -81
  109. package/src/Hooks/useRecentLocations.ts +0 -44
  110. package/src/Hooks/useRecentResourcesPaths.ts +0 -54
  111. package/src/Hooks/useResource.spec.ts +0 -61
  112. package/src/Hooks/useResource.ts +0 -40
  113. package/src/Hooks/useResourcePath.spec.ts +0 -120
  114. package/src/Hooks/useResourcePath.ts +0 -76
  115. package/src/Icons/HistoryIcon.tsx +0 -17
  116. package/src/PreviewPanel/PreviewPanel.spec.tsx +0 -198
  117. package/src/PreviewPanel/PreviewPanel.stories.tsx +0 -76
  118. package/src/PreviewPanel/PreviewPanel.tsx +0 -6
  119. package/src/PreviewPanel/details/MatrixResource.tsx +0 -54
  120. package/src/PreviewPanel/details/matrix-resource.scss +0 -16
  121. package/src/ResourceBreadcrumb/ResourceBreadcrumb.spec.tsx +0 -133
  122. package/src/ResourceBreadcrumb/ResourceBreadcrumb.stories.tsx +0 -24
  123. package/src/ResourceBreadcrumb/ResourceBreadcrumb.tsx +0 -79
  124. package/src/ResourceBreadcrumb/resource-breadcrumb.scss +0 -28
  125. package/src/ResourceBreadcrumb/sample-hierarchy.json +0 -27
  126. package/src/ResourceList/ResourceList.spec.tsx +0 -202
  127. package/src/ResourceList/ResourceList.stories.tsx +0 -40
  128. package/src/ResourceList/ResourceList.tsx +0 -83
  129. package/src/ResourceList/sample-resources.json +0 -851
  130. package/src/ResourcePickerContainer/ResourcePickerContainer.spec.tsx +0 -780
  131. package/src/ResourcePickerContainer/ResourcePickerContainer.stories.tsx +0 -45
  132. package/src/ResourcePickerContainer/ResourcePickerContainer.tsx +0 -290
  133. package/src/SourceList/sample-sources.json +0 -251
  134. package/src/StatusIndicator/StatusIndicator.stories.tsx +0 -83
  135. package/src/StatusIndicator/StatusIndicator.tsx +0 -38
  136. package/src/__mocks__/JestHelpers.ts +0 -65
  137. package/src/__mocks__/StorybookHelpers.ts +0 -128
  138. package/src/__mocks__/jestHelpers.spec.ts +0 -38
  139. package/src/utils/findBestMatchLineage.spec.ts +0 -81
  140. package/src/utils/findBestMatchLineage.ts +0 -30
  141. package/src/utils/uuid.ts +0 -5
@@ -1,104 +1,282 @@
1
1
  import React from 'react';
2
- import { act, fireEvent, screen, waitFor } from '@testing-library/react';
3
- import { ResourceBrowserInput, ResourceBrowserInputProps, ResourceReference } from './index';
4
- import { mockResource } from './__mocks__/MockModels';
2
+ import { waitFor, act } from '@testing-library/react';
3
+ import { ResourceBrowser, ResourceBrowserProps } from './index';
4
+ import { mockSource, mockResource } from './__mocks__/MockModels';
5
5
  import { renderWithContext } from './__mocks__/renderWithContext';
6
+ import { ResourceBrowserPlugin } from './types';
7
+
8
+ import * as RBI from './ResourceBrowserInput/ResourceBrowserInput';
9
+ jest.spyOn(RBI, 'ResourceBrowserInput');
6
10
 
7
11
  describe('Resource browser input', () => {
8
- const mockRequestSources = jest.fn().mockResolvedValue([]);
9
- const mockRequestChildren = jest.fn().mockResolvedValue([]);
10
- const mockRequestResource = jest.fn();
11
- const mockChange = jest.fn();
12
- const renderComponent = (props: Partial<ResourceBrowserInputProps> = {}) => {
13
- return renderWithContext(
14
- <ResourceBrowserInput
15
- modalTitle="Asset picker"
16
- allowedTypes={['site', 'image', 'physical_file']}
17
- onChange={mockChange}
18
- value={null}
19
- {...props}
20
- />,
21
- {
22
- onRequestSources: mockRequestSources,
23
- onRequestChildren: mockRequestChildren,
24
- onRequestResource: mockRequestResource,
25
- },
26
- );
27
- };
28
-
29
- it('should render the related asset picker with the default label', () => {
30
- renderComponent();
31
- const pickerLabel = screen.getByText('Choose asset');
32
-
33
- expect(pickerLabel).toBeInTheDocument();
34
- });
35
-
36
- it('should display the generic asset picking icon', () => {
37
- renderComponent();
38
- const pickerLabel = screen.getByText('Choose asset');
39
- const pickerIcon = screen.getByTestId('AdsClickRoundedIcon');
40
-
41
- expect(pickerLabel).toBeInTheDocument();
42
- expect(pickerIcon).toBeInTheDocument();
43
- });
44
-
45
- it('should display the image icon when only images are allowed', () => {
46
- renderComponent({ allowedTypes: ['image'] });
47
- const pickerLabel = screen.getByText('Choose image');
48
- const pickerIcon = screen.getByTestId('PhotoLibraryRoundedIcon');
49
-
50
- expect(pickerLabel).toBeInTheDocument();
51
- expect(pickerIcon).toBeInTheDocument();
52
- });
53
-
54
- it('should disallow removing and replacing selection if the input is disabled', async () => {
55
- mockRequestResource.mockResolvedValue(
56
- mockResource({
57
- id: '100',
58
- name: 'Mocked resource',
59
- }),
60
- );
61
- renderComponent({ value: { source: '1', resource: '100' }, isDisabled: true });
62
-
63
- await waitFor(() => expect(screen.queryByLabelText('Loading selection')).not.toBeInTheDocument());
64
-
65
- expect(screen.getByRole('button', { name: 'Remove selection' })).toBeDisabled();
66
- expect(screen.getByRole('button', { name: 'Replace selection' })).toBeDisabled();
67
- });
68
-
69
- it('clicking the replace selection button should open the resource browser', async () => {
70
- mockRequestResource.mockResolvedValue(
71
- mockResource({
72
- id: '100',
73
- name: 'Mocked resource',
74
- }),
75
- );
76
- renderComponent({ value: { source: '1', resource: '100' } });
77
-
78
- await waitFor(() => expect(screen.queryByLabelText('Loading selection')).not.toBeInTheDocument());
79
- await act(() => fireEvent.click(screen.getByRole('button', { name: 'Replace selection' })));
80
- expect(screen.getByRole('button', { name: 'Close Asset picker dialog' })).toBeInTheDocument();
81
- });
82
-
83
- it.each([
84
- ['resource selected', { source: '1', resource: '100' }, 1],
85
- ['resource not selected', null, 0],
86
- ])(
87
- 'should only display the "replace" button if a resource has already been selected - %s',
88
- async (description: string, value: ResourceReference | null, expectedReplaceSelectionButtons: number) => {
89
- mockRequestResource.mockResolvedValue(
90
- mockResource({
91
- id: '100',
92
- name: 'Mocked resource',
93
- }),
94
- );
95
- renderComponent({ value });
96
-
97
- await waitFor(() => expect(screen.queryByLabelText('Loading selection')).not.toBeInTheDocument());
98
-
99
- expect(screen.queryAllByRole('button', { name: 'Replace selection' })).toHaveLength(
100
- expectedReplaceSelectionButtons,
101
- );
102
- },
103
- );
12
+ const mockChange = jest.fn();
13
+ const mockOnClear = jest.fn();
14
+
15
+ const mockRequestSources = jest.fn().mockResolvedValue([]);
16
+ const mockRenderSelectedResource = jest.fn();
17
+ const mockSourceBrowserComponent = jest.fn().mockReturnValue(() => <div>Source UI has been rendered</div>);
18
+ const mockResolveResource = jest.fn();
19
+ const mockUseResolveResource = jest.fn().mockReturnValue({
20
+ data: null,
21
+ error: null,
22
+ isLoading: false,
23
+ });
24
+ const mockDamPlugin = {
25
+ type: 'dam',
26
+ resolveResource: mockResolveResource,
27
+ renderSelectedResource: mockRenderSelectedResource,
28
+ sourceBrowserComponent: mockSourceBrowserComponent,
29
+ useResolveResource: mockUseResolveResource,
30
+ } as unknown as ResourceBrowserPlugin;
31
+
32
+ const renderComponent = (props: Partial<ResourceBrowserProps> = {}) => {
33
+ return renderWithContext(
34
+ <ResourceBrowser modalTitle="Asset picker" value={null} onChange={mockChange} onClear={mockOnClear} {...props} />,
35
+ {
36
+ onRequestSources: mockRequestSources,
37
+ plugins: [mockDamPlugin],
38
+ },
39
+ );
40
+ };
41
+
42
+ it('If only one valid source is provided will default to its Source and Plugin', async () => {
43
+ const source = mockSource({ type: 'dam' });
44
+ mockRequestSources.mockResolvedValueOnce([source]);
45
+ renderComponent();
46
+
47
+ await waitFor(() => {
48
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
49
+ expect.objectContaining({
50
+ source,
51
+ plugin: mockDamPlugin,
52
+ }),
53
+ {},
54
+ );
55
+ });
56
+ });
57
+
58
+ it('If multiple valid source is provided will not default to its Source', async () => {
59
+ const sources = [mockSource(), mockSource()];
60
+ mockRequestSources.mockResolvedValueOnce(sources);
61
+ renderComponent();
62
+
63
+ await waitFor(() => {
64
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
65
+ expect.objectContaining({
66
+ sources,
67
+ plugin: null,
68
+ }),
69
+ {},
70
+ );
71
+ });
72
+ });
73
+
74
+ it('If a resource is provided will default to its Source and Plugin to match', async () => {
75
+ const source = mockSource({ type: 'dam' });
76
+ mockRequestSources.mockResolvedValueOnce([source, mockSource({ type: 'matrix' })]);
77
+ mockResolveResource.mockResolvedValueOnce(mockResource({ source }));
78
+ renderComponent({ value: { sourceId: source.id, resourceId: '100' } });
79
+
80
+ await waitFor(() => {
81
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
82
+ expect.objectContaining({
83
+ source,
84
+ plugin: mockDamPlugin,
85
+ }),
86
+ {},
87
+ );
88
+ });
89
+ });
90
+
91
+ it('If a resource is provided but its Source cannot be found it will error', async () => {
92
+ const source = mockSource({ type: 'dam' });
93
+ mockRequestSources.mockResolvedValueOnce([source, mockSource({ type: 'matrix' })]);
94
+ mockResolveResource.mockResolvedValueOnce(mockResource({ source }));
95
+ renderComponent({ value: { sourceId: 'not a real id', resourceId: '100' } });
96
+
97
+ await waitFor(() => {
98
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
99
+ expect.objectContaining({
100
+ error: expect.any(Error),
101
+ source: null,
102
+ plugin: null,
103
+ }),
104
+ {},
105
+ );
106
+ });
107
+ });
108
+
109
+ it('onSourceSelect will alter source passed to ResourceBrowserInput', async () => {
110
+ const sources = [mockSource({ type: 'dam' }), mockSource()];
111
+ mockRequestSources.mockResolvedValueOnce(sources);
112
+ renderComponent();
113
+
114
+ // Expect no source or plugin
115
+ await waitFor(() => {
116
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
117
+ expect.objectContaining({
118
+ sources,
119
+ source: null,
120
+ plugin: null,
121
+ }),
122
+ {},
123
+ );
124
+ });
125
+
126
+ // Get the provided callback
127
+ const { setSource } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
128
+ // Invoke it
129
+ act(() => {
130
+ setSource(sources[0]);
131
+ });
132
+
133
+ // Expect the source and plugin to be loaded
134
+ await waitFor(() => {
135
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
136
+ expect.objectContaining({
137
+ source: sources[0],
138
+ plugin: mockDamPlugin,
139
+ }),
140
+ {},
141
+ );
142
+ });
143
+ });
144
+
145
+ it("When source is selected it will pass down its plugin's useResolveResource to maintain hook rules", async () => {
146
+ const source = mockSource({ type: 'dam' });
147
+ const resource = { resourceId: '12', sourceId: source.id };
148
+ mockRequestSources.mockResolvedValueOnce([source]);
149
+ renderComponent({ value: resource });
150
+
151
+ await waitFor(() => {
152
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
153
+ expect.objectContaining({
154
+ value: resource,
155
+ source,
156
+ plugin: mockDamPlugin,
157
+ }),
158
+ {},
159
+ );
160
+ });
161
+
162
+ await waitFor(() => {
163
+ expect(mockUseResolveResource).toHaveBeenCalledWith(resource.resourceId, source);
164
+ });
165
+ });
166
+
167
+ it('onModalStateChange called with false will reset the Source and Plugin', async () => {
168
+ const sources = [mockSource({ type: 'dam' }), mockSource()];
169
+ mockRequestSources.mockResolvedValueOnce(sources);
170
+ renderComponent();
171
+
172
+ // Expect no source or plugin
173
+ await waitFor(() => {
174
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
175
+ expect.objectContaining({
176
+ sources,
177
+ source: null,
178
+ plugin: null,
179
+ }),
180
+ {},
181
+ );
182
+ });
183
+
184
+ // Get the provided callback
185
+ const { setSource } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
186
+ // Invoke it
187
+ act(() => {
188
+ setSource(sources[0]);
189
+ });
190
+
191
+ // Expect the source and plugin to be loaded
192
+ await waitFor(() => {
193
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
194
+ expect.objectContaining({
195
+ source: sources[0],
196
+ plugin: mockDamPlugin,
197
+ }),
198
+ {},
199
+ );
200
+ });
201
+
202
+ // Invoke modal close callback
203
+ const { onModalStateChange } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
204
+ onModalStateChange(false);
205
+
206
+ // Expect no source or plugin
207
+ await waitFor(() => {
208
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
209
+ expect.objectContaining({
210
+ sources,
211
+ source: null,
212
+ plugin: null,
213
+ }),
214
+ {},
215
+ );
216
+ });
217
+ });
218
+
219
+ it('onModalStateChange called with false will not reset the Source and Plugin if only one source exists', async () => {
220
+ const sources = [mockSource({ type: 'dam' })];
221
+ mockRequestSources.mockResolvedValueOnce(sources);
222
+ renderComponent();
223
+
224
+ // Expect the source and plugin to be loaded
225
+ await waitFor(() => {
226
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
227
+ expect.objectContaining({
228
+ source: sources[0],
229
+ plugin: mockDamPlugin,
230
+ }),
231
+ {},
232
+ );
233
+ });
234
+
235
+ // Invoke modal close callback
236
+ const { onModalStateChange } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
237
+ act(() => {
238
+ onModalStateChange(false);
239
+ });
240
+
241
+ await waitFor(() => {
242
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
243
+ expect.objectContaining({
244
+ source: sources[0],
245
+ plugin: mockDamPlugin,
246
+ }),
247
+ {},
248
+ );
249
+ });
250
+ });
251
+
252
+ it('onModalStateChange called with false will not reset the Source and Plugin if a value exists', async () => {
253
+ const sources = [mockSource({ type: 'dam' }), mockSource()];
254
+ mockRequestSources.mockResolvedValueOnce(sources);
255
+ renderComponent({ value: { sourceId: sources[0].id, resourceId: '123456' } });
256
+
257
+ await waitFor(() => {
258
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
259
+ expect.objectContaining({
260
+ sources,
261
+ source: sources[0],
262
+ plugin: mockDamPlugin,
263
+ }),
264
+ {},
265
+ );
266
+ });
267
+
268
+ // Invoke modal close callback
269
+ const { onModalStateChange } = (RBI.ResourceBrowserInput as unknown as jest.SpyInstance).mock.calls[0][0];
270
+ onModalStateChange(false);
271
+
272
+ await waitFor(() => {
273
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
274
+ expect.objectContaining({
275
+ source: sources[0],
276
+ plugin: mockDamPlugin,
277
+ }),
278
+ {},
279
+ );
280
+ });
281
+ });
104
282
  });
@@ -1,53 +1,79 @@
1
+ import React, { useState } from 'react';
1
2
  import { StoryFn, Meta } from '@storybook/react';
2
- import { ResourceBrowserInput, ResourceBrowserContextProvider } from './index';
3
- import { createResourceBrowserCallbacks } from './__mocks__/StorybookHelpers';
4
-
3
+ import { ResourceBrowser, ResourceBrowserContextProvider } from './index';
4
+ import { createResourceBrowserCallbacks, createPlugins } from './__mocks__/StorybookHelpers';
5
+ import { ResourceBrowserPlugin, ResourceBrowserSource, ResourceBrowserUnresolvedResource, ResourceBrowserResource } from './types';
5
6
  export default {
6
- title: 'Resource browser input',
7
- component: ResourceBrowserInput,
8
- } as Meta<typeof ResourceBrowserInput>;
9
-
10
- const Template: StoryFn<typeof ResourceBrowserInput> = (props) => {
11
- const { sourceIsLoading, resourceIsLoading, error, ...other } = props;
12
- const { onRequestSources, onRequestChildren, onRequestResource, onChange } = createResourceBrowserCallbacks({
13
- sourceIsLoading: !!sourceIsLoading,
14
- resourceIsLoading: !!resourceIsLoading,
15
- error,
16
- });
17
-
18
- return (
19
- <div className="w-[400px] m-3">
20
- <ResourceBrowserContextProvider value={{ onRequestSources, onRequestChildren, onRequestResource }}>
21
- <ResourceBrowserInput {...other} onChange={onChange} />
22
- </ResourceBrowserContextProvider>
23
- </div>
24
- );
7
+ title: 'Resource browser',
8
+ component: ResourceBrowser,
9
+ } as Meta<typeof ResourceBrowser>;
10
+
11
+ const Template: StoryFn<typeof ResourceBrowser> = (props) => {
12
+ const [resource, setResource] = useState<ResourceBrowserUnresolvedResource | null>(props?.value || null);
13
+ const { onRequestSources } = createResourceBrowserCallbacks({
14
+ sourceIsLoading: false,
15
+ singleSource: props.singleSource,
16
+ });
17
+
18
+ const onChange = (resource: ResourceBrowserResource | null) => {
19
+ if (resource) {
20
+ const { source } = resource;
21
+ setResource({
22
+ resourceId: resource.id,
23
+ sourceId: source.id,
24
+ });
25
+
26
+ console.log(
27
+ `Resource Browser has selected resource #${resource.id} (${resource.name}) from source #${source.id} (${source.name}).`,
28
+ );
29
+ } else {
30
+ setResource(null);
31
+ console.log(`Resource Browser has cleared selected resource.`);
32
+ }
33
+ };
34
+
35
+ const plugins: ResourceBrowserPlugin[] = createPlugins(props.callbackWait, props.headerPortal);
36
+
37
+ return (
38
+ <div className="w-[400px] m-3">
39
+ <ResourceBrowserContextProvider
40
+ value={{ onRequestSources: onRequestSources as () => Promise<ResourceBrowserSource[]>, plugins }}
41
+ >
42
+ <ResourceBrowser {...props} value={resource} onChange={onChange} onClear={undefined} />
43
+ </ResourceBrowserContextProvider>
44
+ </div>
45
+ );
25
46
  };
26
47
 
27
48
  export const Primary = Template.bind({});
28
49
 
29
50
  Primary.args = {
30
- modalTitle: 'Choose asset',
31
- sourceIsLoading: false,
32
- resourceIsLoading: false,
33
- error: '',
51
+ modalTitle: 'Choose asset',
52
+ sourceIsLoading: false,
53
+ resourceIsLoading: false,
54
+ error: '',
55
+ callbackWait: 0,
56
+ singleSource: false,
57
+ headerPortal: false,
34
58
  };
35
59
 
36
- export const SourceSelected = Template.bind({});
37
- SourceSelected.args = {
38
- ...Primary.args,
39
- value: { resource: '9999', source: '1' },
40
- };
41
-
42
- export const ResourceSelected = Template.bind({});
43
- ResourceSelected.args = {
44
- ...Primary.args,
45
- value: { resource: '33', source: '1' },
60
+ export const Selected = Template.bind({});
61
+ Selected.args = {
62
+ ...Primary.args,
63
+ value: {
64
+ resourceId: '1f7a25b4-380f-4540-9555-8be2dcab4019',
65
+ sourceId: 'c90feac1-55f3-4e1f-9b56-c22829e3f510',
66
+ },
46
67
  };
47
68
 
48
69
  export const ImagesOnly = Template.bind({});
49
-
50
70
  ImagesOnly.args = {
51
- ...Primary.args,
52
- allowedTypes: ['image'],
71
+ ...Primary.args,
72
+ allowedTypes: ['image'],
73
+ };
74
+
75
+ export const SingleSource = Template.bind({});
76
+ SingleSource.args = {
77
+ ...Primary.args,
78
+ singleSource: true,
53
79
  };