@squiz/resource-browser 1.69.0 → 1.69.2
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 +12 -0
- package/lib/Hooks/useRecentLocations.d.ts +3 -8
- package/lib/Hooks/useRecentLocations.js +5 -1
- package/lib/Hooks/useRecentResourcesPaths.d.ts +20 -0
- package/lib/Hooks/useRecentResourcesPaths.js +30 -0
- package/lib/Hooks/useResource.d.ts +13 -0
- package/lib/Hooks/useResource.js +14 -1
- package/lib/ResourcePickerContainer/ResourcePickerContainer.js +30 -14
- package/lib/SourceDropdown/SourceDropdown.d.ts +3 -1
- package/lib/SourceDropdown/SourceDropdown.js +24 -22
- package/lib/SourceList/SourceList.d.ts +3 -1
- package/lib/SourceList/SourceList.js +15 -13
- package/lib/index.css +3 -0
- package/package.json +1 -1
- package/src/Hooks/useRecentLocations.spec.ts +36 -40
- package/src/Hooks/useRecentLocations.ts +10 -11
- package/src/Hooks/useRecentResourcesPaths.ts +54 -0
- package/src/Hooks/useResource.spec.ts +30 -1
- package/src/Hooks/useResource.ts +21 -0
- package/src/ResourcePicker/ResourcePicker.spec.tsx +18 -0
- package/src/ResourcePickerContainer/ResourcePickerContainer.spec.tsx +17 -2
- package/src/ResourcePickerContainer/ResourcePickerContainer.tsx +40 -15
- package/src/SourceDropdown/SourceDropdown.spec.tsx +92 -27
- package/src/SourceDropdown/SourceDropdown.tsx +33 -29
- package/src/SourceList/SourceList.spec.tsx +89 -72
- package/src/SourceList/SourceList.tsx +34 -29
@@ -2,35 +2,39 @@
|
|
2
2
|
import React from 'react';
|
3
3
|
import { screen, render, waitFor, within } from '@testing-library/react';
|
4
4
|
import userEvent from '@testing-library/user-event';
|
5
|
-
import { mockSource } from '../__mocks__/MockModels';
|
5
|
+
import { mockResource, mockSource } from '../__mocks__/MockModels';
|
6
6
|
import { useOverlayTriggerState, OverlayTriggerState } from 'react-stately';
|
7
7
|
import SourceList from './SourceList';
|
8
8
|
import { Source } from '../types';
|
9
|
+
import { RecentResourcesPaths } from '../Hooks/useRecentResourcesPaths';
|
9
10
|
|
10
|
-
const
|
11
|
+
const mockRecentSources: RecentResourcesPaths[] = [
|
11
12
|
{
|
12
|
-
|
13
|
-
source: {
|
13
|
+
source: mockSource({
|
14
14
|
id: '1',
|
15
|
-
name: '
|
16
|
-
nodes: [
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
15
|
+
name: 'Source 1',
|
16
|
+
nodes: [
|
17
|
+
{
|
18
|
+
id: '1',
|
19
|
+
type: {
|
20
|
+
code: 'site',
|
21
|
+
name: 'Site',
|
22
|
+
},
|
23
|
+
name: 'Node 1',
|
24
|
+
childCount: 21,
|
25
|
+
},
|
26
|
+
{
|
27
|
+
id: '2',
|
28
|
+
type: {
|
29
|
+
code: 'site',
|
30
|
+
name: 'Site',
|
31
|
+
},
|
32
|
+
name: 'Node 2',
|
33
|
+
childCount: 13,
|
34
|
+
},
|
35
|
+
],
|
36
|
+
}),
|
37
|
+
path: [mockResource()],
|
34
38
|
},
|
35
39
|
];
|
36
40
|
|
@@ -111,6 +115,7 @@ describe('SourceList', () => {
|
|
111
115
|
error={null}
|
112
116
|
handleReload={reload}
|
113
117
|
setSource={() => {}}
|
118
|
+
recentSources={mockRecentSources}
|
114
119
|
/>
|
115
120
|
);
|
116
121
|
}}
|
@@ -138,6 +143,7 @@ describe('SourceList', () => {
|
|
138
143
|
error={null}
|
139
144
|
handleReload={reload}
|
140
145
|
setSource={() => {}}
|
146
|
+
recentSources={mockRecentSources}
|
141
147
|
/>
|
142
148
|
);
|
143
149
|
}}
|
@@ -166,6 +172,7 @@ describe('SourceList', () => {
|
|
166
172
|
error={null}
|
167
173
|
handleReload={reload}
|
168
174
|
setSource={() => {}}
|
175
|
+
recentSources={mockRecentSources}
|
169
176
|
/>
|
170
177
|
);
|
171
178
|
}}
|
@@ -193,6 +200,7 @@ describe('SourceList', () => {
|
|
193
200
|
error={null}
|
194
201
|
handleReload={reload}
|
195
202
|
setSource={() => {}}
|
203
|
+
recentSources={mockRecentSources}
|
196
204
|
/>
|
197
205
|
);
|
198
206
|
}}
|
@@ -227,6 +235,7 @@ describe('SourceList', () => {
|
|
227
235
|
error={null}
|
228
236
|
handleReload={reload}
|
229
237
|
setSource={() => {}}
|
238
|
+
recentSources={mockRecentSources}
|
230
239
|
/>
|
231
240
|
);
|
232
241
|
}}
|
@@ -266,6 +275,7 @@ describe('SourceList', () => {
|
|
266
275
|
error={null}
|
267
276
|
handleReload={reload}
|
268
277
|
setSource={() => {}}
|
278
|
+
recentSources={mockRecentSources}
|
269
279
|
/>
|
270
280
|
);
|
271
281
|
}}
|
@@ -301,6 +311,7 @@ describe('SourceList', () => {
|
|
301
311
|
error={new Error('Source list error!')}
|
302
312
|
handleReload={reload}
|
303
313
|
setSource={() => {}}
|
314
|
+
recentSources={mockRecentSources}
|
304
315
|
/>
|
305
316
|
);
|
306
317
|
}}
|
@@ -317,8 +328,6 @@ describe('SourceList', () => {
|
|
317
328
|
const reload = jest.fn();
|
318
329
|
const setSource = jest.fn();
|
319
330
|
|
320
|
-
localStorage.setItem('rb_recent_locations', JSON.stringify(mockLocalStorageData));
|
321
|
-
|
322
331
|
render(
|
323
332
|
<SourceListTestWrapper
|
324
333
|
constructFunction={(previewModalState) => {
|
@@ -332,6 +341,7 @@ describe('SourceList', () => {
|
|
332
341
|
error={null}
|
333
342
|
handleReload={reload}
|
334
343
|
setSource={setSource}
|
344
|
+
recentSources={mockRecentSources}
|
335
345
|
/>
|
336
346
|
);
|
337
347
|
}}
|
@@ -348,8 +358,6 @@ describe('SourceList', () => {
|
|
348
358
|
const reload = jest.fn();
|
349
359
|
const setSource = jest.fn();
|
350
360
|
|
351
|
-
localStorage.setItem('rb_recent_locations', JSON.stringify(mockLocalStorageData));
|
352
|
-
|
353
361
|
render(
|
354
362
|
<SourceListTestWrapper
|
355
363
|
constructFunction={(previewModalState) => {
|
@@ -363,6 +371,7 @@ describe('SourceList', () => {
|
|
363
371
|
error={null}
|
364
372
|
handleReload={reload}
|
365
373
|
setSource={setSource}
|
374
|
+
recentSources={mockRecentSources}
|
366
375
|
/>
|
367
376
|
);
|
368
377
|
}}
|
@@ -377,55 +386,63 @@ describe('SourceList', () => {
|
|
377
386
|
// Provides the item that was clicked and an id reference to the button that was clicked
|
378
387
|
expect(setSource).toHaveBeenCalledWith(
|
379
388
|
{
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
path: [],
|
389
|
+
resource: {
|
390
|
+
childCount: 0,
|
391
|
+
id: '1',
|
392
|
+
lineages: [],
|
393
|
+
name: 'Test resource',
|
394
|
+
status: {
|
395
|
+
code: 'live',
|
396
|
+
name: 'Live',
|
397
|
+
},
|
398
|
+
type: {
|
399
|
+
code: 'folder',
|
400
|
+
name: 'Folder',
|
401
|
+
},
|
402
|
+
url: 'https://no-where.com',
|
403
|
+
urls: [],
|
404
|
+
},
|
397
405
|
source: {
|
398
406
|
id: '1',
|
399
|
-
name: '
|
400
|
-
nodes: [
|
407
|
+
name: 'Source 1',
|
408
|
+
nodes: [
|
409
|
+
{
|
410
|
+
childCount: 21,
|
411
|
+
id: '1',
|
412
|
+
lineages: [],
|
413
|
+
name: 'Node 1',
|
414
|
+
status: {
|
415
|
+
code: 'live',
|
416
|
+
name: 'Live',
|
417
|
+
},
|
418
|
+
type: {
|
419
|
+
code: 'site',
|
420
|
+
name: 'Site',
|
421
|
+
},
|
422
|
+
url: 'https://no-where.com',
|
423
|
+
urls: [],
|
424
|
+
},
|
425
|
+
{
|
426
|
+
childCount: 13,
|
427
|
+
id: '2',
|
428
|
+
lineages: [],
|
429
|
+
name: 'Node 2',
|
430
|
+
status: {
|
431
|
+
code: 'live',
|
432
|
+
name: 'Live',
|
433
|
+
},
|
434
|
+
type: {
|
435
|
+
code: 'site',
|
436
|
+
name: 'Site',
|
437
|
+
},
|
438
|
+
url: 'https://no-where.com',
|
439
|
+
urls: [],
|
440
|
+
},
|
441
|
+
],
|
401
442
|
},
|
402
|
-
rootNode: null,
|
403
443
|
},
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
render(
|
408
|
-
<SourceListTestWrapper
|
409
|
-
constructFunction={(previewModalState) => {
|
410
|
-
return (
|
411
|
-
<SourceList
|
412
|
-
sources={sources}
|
413
|
-
previewModalState={previewModalState}
|
414
|
-
isLoading={false}
|
415
|
-
onSourceSelect={() => {}}
|
416
|
-
onSourceDrilldown={() => {}}
|
417
|
-
error={null}
|
418
|
-
handleReload={reload}
|
419
|
-
setSource={setSource}
|
420
|
-
/>
|
421
|
-
);
|
422
|
-
}}
|
423
|
-
/>,
|
424
|
-
);
|
425
|
-
|
426
|
-
await waitFor(() => {
|
427
|
-
expect(screen.getByText('Recent locations')).toBeInTheDocument();
|
428
|
-
expect(screen.getByText('Test source')).toBeInTheDocument();
|
444
|
+
[],
|
445
|
+
);
|
429
446
|
});
|
430
447
|
});
|
431
448
|
});
|
@@ -6,8 +6,8 @@ import clsx from 'clsx';
|
|
6
6
|
|
7
7
|
import { Source, ScopedSource, Resource } from '../types';
|
8
8
|
import { useCategorisedSources } from '../Hooks/useCategorisedSources';
|
9
|
-
import { useRecentLocations } from '../Hooks/useRecentLocations';
|
10
9
|
import { HistoryIcon } from '../Icons/HistoryIcon';
|
10
|
+
import { RecentResourcesPaths } from '../Hooks/useRecentResourcesPaths';
|
11
11
|
|
12
12
|
export interface SourceListProps {
|
13
13
|
sources: Source[];
|
@@ -18,6 +18,7 @@ export interface SourceListProps {
|
|
18
18
|
onSourceDrilldown: (source: ScopedSource) => void;
|
19
19
|
handleReload: () => void;
|
20
20
|
setSource: (source: ScopedSource | null, path?: Resource[]) => void;
|
21
|
+
recentSources: RecentResourcesPaths[];
|
21
22
|
error: Error | null;
|
22
23
|
}
|
23
24
|
|
@@ -30,11 +31,12 @@ const SourceList = function ({
|
|
30
31
|
onSourceDrilldown,
|
31
32
|
handleReload,
|
32
33
|
setSource,
|
34
|
+
recentSources,
|
33
35
|
error,
|
34
36
|
}: SourceListProps) {
|
35
37
|
const categorisedSources = useCategorisedSources(sources);
|
36
38
|
const listRef = useRef<HTMLUListElement>(null);
|
37
|
-
const
|
39
|
+
const filteredRecentSources = recentSources.filter((item) => item.path?.length);
|
38
40
|
|
39
41
|
useEffect(() => {
|
40
42
|
if (listRef.current) {
|
@@ -62,7 +64,7 @@ const SourceList = function ({
|
|
62
64
|
>
|
63
65
|
{error && <ResourceState state="error" message={error.message} handleReload={handleReload} />}
|
64
66
|
|
65
|
-
{!error &&
|
67
|
+
{!error && filteredRecentSources.length > 0 && (
|
66
68
|
<li className={`flex flex-col text-sm font-semibold text-grey-800`}>
|
67
69
|
<div className="relative flex justify-center before:w-full before:h-px before:bg-gray-300 before:absolute before:top-2/4 before:z-0">
|
68
70
|
<span className="z-10 bg-gray-100 px-2.5 flex gap-1 items-center">
|
@@ -71,31 +73,34 @@ const SourceList = function ({
|
|
71
73
|
</span>
|
72
74
|
</div>
|
73
75
|
<ul aria-label={`recent location nodes`} className="flex flex-col">
|
74
|
-
{
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
76
|
+
{filteredRecentSources.map((item, index) => {
|
77
|
+
if (item.path) {
|
78
|
+
const lastResource = item.path[item.path.length - 1];
|
79
|
+
const [rootNode, ...path] = item.path;
|
80
|
+
return (
|
81
|
+
<ResourceItem
|
82
|
+
key={`${index}-${item.source?.id}-${lastResource?.id}`}
|
83
|
+
item={{ source: item.source, resource: lastResource }}
|
84
|
+
label={lastResource?.name || item.source?.name || ''}
|
85
|
+
type={lastResource?.type?.code || 'folder'}
|
86
|
+
previewModalState={previewModalState}
|
87
|
+
onSelect={() => {
|
88
|
+
setSource(
|
89
|
+
{
|
90
|
+
source: item.source as Source,
|
91
|
+
resource: rootNode,
|
92
|
+
},
|
93
|
+
path,
|
94
|
+
);
|
95
|
+
}}
|
96
|
+
className={clsx(
|
97
|
+
index === 0 && 'rounded-t-lg mt-3',
|
98
|
+
index === filteredRecentSources.length - 1 && 'rounded-b-lg',
|
99
|
+
)}
|
100
|
+
showChevron
|
101
|
+
/>
|
102
|
+
);
|
103
|
+
}
|
99
104
|
})}
|
100
105
|
</ul>
|
101
106
|
</li>
|
@@ -107,7 +112,7 @@ const SourceList = function ({
|
|
107
112
|
<li
|
108
113
|
key={key}
|
109
114
|
className={`flex flex-col text-sm font-semibold text-grey-800 ${
|
110
|
-
index > 0 ||
|
115
|
+
index > 0 || filteredRecentSources.length > 0 ? 'mt-3' : ''
|
111
116
|
}`}
|
112
117
|
>
|
113
118
|
<div className="relative flex justify-center before:w-full before:h-px before:bg-gray-300 before:absolute before:top-2/4 before:z-0">
|