@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.
- package/CHANGELOG.md +89 -38
- package/LICENSE.md +15 -0
- package/README.md +9 -0
- package/jest.config.ts +22 -21
- package/lib/Hooks/useSelectedState.d.ts +15 -0
- package/lib/Hooks/useSelectedState.js +16 -0
- package/lib/Hooks/useSources.d.ts +6 -6
- package/lib/Hooks/useSources.js +26 -1
- package/lib/MainContainer/MainContainer.d.ts +17 -0
- package/lib/MainContainer/MainContainer.js +61 -0
- package/lib/Plugin/Plugin.d.ts +13 -0
- package/lib/Plugin/Plugin.js +17 -0
- package/lib/ResourceBrowserContext/ResourceBrowserContext.d.ts +2 -3
- package/lib/ResourceBrowserContext/ResourceBrowserContext.js +4 -17
- package/lib/ResourceBrowserInput/ResourceBrowserInput.d.ts +24 -0
- package/lib/ResourceBrowserInput/ResourceBrowserInput.js +16 -0
- package/lib/ResourcePicker/ResourcePicker.d.ts +6 -4
- package/lib/ResourcePicker/ResourcePicker.js +14 -8
- package/lib/ResourcePicker/States/Selected.d.ts +10 -4
- package/lib/ResourcePicker/States/Selected.js +11 -32
- package/lib/SourceDropdown/SourceDropdown.d.ts +5 -11
- package/lib/SourceDropdown/SourceDropdown.js +20 -99
- package/lib/SourceList/SourceList.d.ts +5 -16
- package/lib/SourceList/SourceList.js +14 -75
- package/lib/index.css +42 -202
- package/lib/index.d.ts +7 -7
- package/lib/index.js +69 -13
- package/lib/types.d.ts +41 -59
- package/package.json +82 -80
- package/src/Hooks/useSelectedState.spec.ts +46 -0
- package/src/Hooks/useSelectedState.ts +22 -0
- package/src/Hooks/useSources.spec.ts +60 -13
- package/src/Hooks/useSources.ts +35 -5
- package/src/Icons/CircledLoopIcon.tsx +8 -8
- package/src/MainContainer/MainContainer.spec.tsx +203 -0
- package/src/MainContainer/MainContainer.stories.tsx +62 -0
- package/src/MainContainer/MainContainer.tsx +101 -0
- package/src/Plugin/Plugin.spec.tsx +46 -0
- package/src/Plugin/Plugin.tsx +20 -0
- package/src/ResourceBrowserContext/ResourceBrowserContext.spec.tsx +65 -106
- package/src/ResourceBrowserContext/ResourceBrowserContext.tsx +24 -39
- package/src/ResourceBrowserInput/ResourceBrowserInput.spec.tsx +192 -0
- package/src/ResourceBrowserInput/ResourceBrowserInput.tsx +81 -0
- package/src/ResourcePicker/ResourcePicker.spec.tsx +159 -116
- package/src/ResourcePicker/ResourcePicker.stories.tsx +28 -24
- package/src/ResourcePicker/ResourcePicker.tsx +79 -59
- package/src/ResourcePicker/States/Error.tsx +8 -8
- package/src/ResourcePicker/States/Loading.tsx +3 -3
- package/src/ResourcePicker/States/Selected.tsx +66 -73
- package/src/ResourcePicker/mock-image-resource.json +25 -47
- package/src/ResourcePicker/mock-resource.json +11 -13
- package/src/ResourcePicker/resource-picker.scss +13 -13
- package/src/SourceDropdown/SourceDropdown.spec.tsx +65 -391
- package/src/SourceDropdown/SourceDropdown.stories.tsx +21 -24
- package/src/SourceDropdown/SourceDropdown.tsx +80 -258
- package/src/SourceList/SourceList.spec.tsx +37 -430
- package/src/SourceList/SourceList.stories.tsx +17 -37
- package/src/SourceList/SourceList.tsx +28 -155
- package/src/__mocks__/MockModels.ts +56 -25
- package/src/__mocks__/PluginExample.tsx +98 -0
- package/src/__mocks__/StorybookHelpers.tsx +141 -0
- package/src/__mocks__/renderWithContext.tsx +14 -18
- package/src/__mocks__/sample-sources.json +32 -0
- package/src/index.scss +18 -8
- package/src/index.spec.tsx +277 -99
- package/src/index.stories.tsx +65 -39
- package/src/index.tsx +119 -57
- package/src/types.ts +54 -63
- package/tailwind.config.cjs +92 -92
- package/vite.config.js +12 -12
- package/lib/Hooks/useCategorisedSources.d.ts +0 -14
- package/lib/Hooks/useCategorisedSources.js +0 -38
- package/lib/Hooks/useChildResources.d.ts +0 -16
- package/lib/Hooks/useChildResources.js +0 -13
- package/lib/Hooks/usePreselectedResourcePath.d.ts +0 -20
- package/lib/Hooks/usePreselectedResourcePath.js +0 -31
- package/lib/Hooks/useRecentLocations.d.ts +0 -5
- package/lib/Hooks/useRecentLocations.js +0 -38
- package/lib/Hooks/useRecentResourcesPaths.d.ts +0 -20
- package/lib/Hooks/useRecentResourcesPaths.js +0 -30
- package/lib/Hooks/useResource.d.ts +0 -28
- package/lib/Hooks/useResource.js +0 -25
- package/lib/Hooks/useResourcePath.d.ts +0 -16
- package/lib/Hooks/useResourcePath.js +0 -64
- package/lib/Icons/HistoryIcon.d.ts +0 -4
- package/lib/Icons/HistoryIcon.js +0 -13
- package/lib/PreviewPanel/PreviewPanel.d.ts +0 -5
- package/lib/PreviewPanel/PreviewPanel.js +0 -8
- package/lib/PreviewPanel/details/MatrixResource.d.ts +0 -7
- package/lib/PreviewPanel/details/MatrixResource.js +0 -35
- package/lib/ResourceBreadcrumb/ResourceBreadcrumb.d.ts +0 -9
- package/lib/ResourceBreadcrumb/ResourceBreadcrumb.js +0 -54
- package/lib/ResourceList/ResourceList.d.ts +0 -18
- package/lib/ResourceList/ResourceList.js +0 -49
- package/lib/ResourcePickerContainer/ResourcePickerContainer.d.ts +0 -17
- package/lib/ResourcePickerContainer/ResourcePickerContainer.js +0 -166
- package/lib/StatusIndicator/StatusIndicator.d.ts +0 -8
- package/lib/StatusIndicator/StatusIndicator.js +0 -27
- package/lib/utils/findBestMatchLineage.d.ts +0 -2
- package/lib/utils/findBestMatchLineage.js +0 -28
- package/lib/utils/uuid.d.ts +0 -1
- package/lib/utils/uuid.js +0 -6
- package/src/Hooks/useCategorisedSources.spec.ts +0 -39
- package/src/Hooks/useCategorisedSources.ts +0 -46
- package/src/Hooks/useChildResources.spec.ts +0 -29
- package/src/Hooks/useChildResources.ts +0 -21
- package/src/Hooks/usePreselectedResourcePath.ts +0 -54
- package/src/Hooks/useRecentLocations.spec.ts +0 -81
- package/src/Hooks/useRecentLocations.ts +0 -44
- package/src/Hooks/useRecentResourcesPaths.ts +0 -54
- package/src/Hooks/useResource.spec.ts +0 -61
- package/src/Hooks/useResource.ts +0 -40
- package/src/Hooks/useResourcePath.spec.ts +0 -120
- package/src/Hooks/useResourcePath.ts +0 -76
- package/src/Icons/HistoryIcon.tsx +0 -17
- package/src/PreviewPanel/PreviewPanel.spec.tsx +0 -198
- package/src/PreviewPanel/PreviewPanel.stories.tsx +0 -76
- package/src/PreviewPanel/PreviewPanel.tsx +0 -6
- package/src/PreviewPanel/details/MatrixResource.tsx +0 -54
- package/src/PreviewPanel/details/matrix-resource.scss +0 -16
- package/src/ResourceBreadcrumb/ResourceBreadcrumb.spec.tsx +0 -133
- package/src/ResourceBreadcrumb/ResourceBreadcrumb.stories.tsx +0 -24
- package/src/ResourceBreadcrumb/ResourceBreadcrumb.tsx +0 -79
- package/src/ResourceBreadcrumb/resource-breadcrumb.scss +0 -28
- package/src/ResourceBreadcrumb/sample-hierarchy.json +0 -27
- package/src/ResourceList/ResourceList.spec.tsx +0 -202
- package/src/ResourceList/ResourceList.stories.tsx +0 -40
- package/src/ResourceList/ResourceList.tsx +0 -83
- package/src/ResourceList/sample-resources.json +0 -851
- package/src/ResourcePickerContainer/ResourcePickerContainer.spec.tsx +0 -780
- package/src/ResourcePickerContainer/ResourcePickerContainer.stories.tsx +0 -45
- package/src/ResourcePickerContainer/ResourcePickerContainer.tsx +0 -290
- package/src/SourceList/sample-sources.json +0 -251
- package/src/StatusIndicator/StatusIndicator.stories.tsx +0 -83
- package/src/StatusIndicator/StatusIndicator.tsx +0 -38
- package/src/__mocks__/JestHelpers.ts +0 -65
- package/src/__mocks__/StorybookHelpers.ts +0 -128
- package/src/__mocks__/jestHelpers.spec.ts +0 -38
- package/src/utils/findBestMatchLineage.spec.ts +0 -81
- package/src/utils/findBestMatchLineage.ts +0 -30
- package/src/utils/uuid.ts +0 -5
@@ -1,61 +0,0 @@
|
|
1
|
-
import { renderHook, waitFor } from '@testing-library/react';
|
2
|
-
import { mockResource } from '../__mocks__/MockModels';
|
3
|
-
import { useResource, useResources } from './useResource';
|
4
|
-
|
5
|
-
describe('useResource', () => {
|
6
|
-
it('Should load the resource', async () => {
|
7
|
-
const resource = mockResource();
|
8
|
-
const reference = { source: 'source-id', resource: 'resource-id' };
|
9
|
-
const onRequestResource = jest.fn().mockResolvedValue(resource);
|
10
|
-
const { result } = renderHook(() => useResource({ onRequestResource, reference }));
|
11
|
-
|
12
|
-
expect(result.current.isLoading).toBe(true);
|
13
|
-
expect(result.current.error).toBe(null);
|
14
|
-
expect(result.current.data).toEqual(null);
|
15
|
-
|
16
|
-
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
17
|
-
|
18
|
-
expect(result.current.isLoading).toBe(false);
|
19
|
-
expect(result.current.data).toBe(resource);
|
20
|
-
});
|
21
|
-
|
22
|
-
it('Should not load the resource if no reference provided', async () => {
|
23
|
-
const reference = null;
|
24
|
-
const onRequestResource = jest.fn();
|
25
|
-
const { result } = renderHook(() => useResource({ onRequestResource, reference }));
|
26
|
-
|
27
|
-
expect(result.current.isLoading).toBe(false);
|
28
|
-
expect(result.current.error).toBe(null);
|
29
|
-
expect(result.current.data).toEqual(null);
|
30
|
-
expect(onRequestResource).not.toBeCalled();
|
31
|
-
});
|
32
|
-
});
|
33
|
-
|
34
|
-
describe('useResources', () => {
|
35
|
-
it('Should load the resources', async () => {
|
36
|
-
const resources = mockResource();
|
37
|
-
const references = [{ source: 'source-id', resource: 'resource-id' }];
|
38
|
-
const onRequestResource = jest.fn().mockResolvedValue(resources);
|
39
|
-
const { result } = renderHook(() => useResources({ onRequestResource, references }));
|
40
|
-
|
41
|
-
expect(result.current.isLoading).toBe(true);
|
42
|
-
expect(result.current.error).toBe(null);
|
43
|
-
expect(result.current.data).toEqual(null);
|
44
|
-
|
45
|
-
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
46
|
-
|
47
|
-
expect(result.current.isLoading).toBe(false);
|
48
|
-
expect(result.current.data).toStrictEqual([resources]);
|
49
|
-
});
|
50
|
-
|
51
|
-
it('Should not load the resources if no references are provided', async () => {
|
52
|
-
const references = null;
|
53
|
-
const onRequestResource = jest.fn();
|
54
|
-
const { result } = renderHook(() => useResources({ onRequestResource, references }));
|
55
|
-
|
56
|
-
expect(result.current.isLoading).toBe(false);
|
57
|
-
expect(result.current.error).toBe(null);
|
58
|
-
expect(result.current.data).toEqual(null);
|
59
|
-
expect(onRequestResource).not.toBeCalled();
|
60
|
-
});
|
61
|
-
});
|
package/src/Hooks/useResource.ts
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
import { useAsync } from '@squiz/generic-browser-lib';
|
2
|
-
import { Resource, ResourceReference } from '../types';
|
3
|
-
|
4
|
-
type UseResourceProps = {
|
5
|
-
onRequestResource: (reference: ResourceReference) => Promise<Resource | null>;
|
6
|
-
reference?: ResourceReference | null;
|
7
|
-
};
|
8
|
-
|
9
|
-
type UseResourcesProps = {
|
10
|
-
onRequestResource: (reference: ResourceReference) => Promise<Resource | null>;
|
11
|
-
references?: ResourceReference[] | null;
|
12
|
-
};
|
13
|
-
|
14
|
-
/**
|
15
|
-
* Loads the resource indicated by the provided reference.
|
16
|
-
*/
|
17
|
-
export const useResource = ({ onRequestResource, reference }: UseResourceProps) =>
|
18
|
-
useAsync(
|
19
|
-
{
|
20
|
-
callback: () => (reference ? onRequestResource(reference) : null),
|
21
|
-
defaultValue: null,
|
22
|
-
// Avoid the race condition bug found in FEAAS-891
|
23
|
-
ignorePrevious: true,
|
24
|
-
},
|
25
|
-
[reference?.source, reference?.resource],
|
26
|
-
);
|
27
|
-
|
28
|
-
/**
|
29
|
-
* Loads the resources indicated by the provided reference.
|
30
|
-
*/
|
31
|
-
export const useResources = ({ onRequestResource, references }: UseResourcesProps) => {
|
32
|
-
const callbackArray = references?.map((item) => () => onRequestResource(item));
|
33
|
-
return useAsync(
|
34
|
-
{
|
35
|
-
callback: callbackArray ? callbackArray : () => null,
|
36
|
-
defaultValue: null,
|
37
|
-
},
|
38
|
-
[],
|
39
|
-
);
|
40
|
-
};
|
@@ -1,120 +0,0 @@
|
|
1
|
-
import { act, renderHook } from '@testing-library/react';
|
2
|
-
import { mockScopedSource, mockResource } from '../__mocks__/MockModels';
|
3
|
-
import { useResourcePath } from './useResourcePath';
|
4
|
-
import { Resource, Hierarchy, ScopedSource } from '../types';
|
5
|
-
|
6
|
-
describe('useResourcePath', () => {
|
7
|
-
it('Should return expected initial state', () => {
|
8
|
-
const {
|
9
|
-
result: { current: result },
|
10
|
-
} = renderHook(() => useResourcePath());
|
11
|
-
|
12
|
-
expect(result).toEqual({
|
13
|
-
source: null,
|
14
|
-
currentResource: null,
|
15
|
-
hierarchy: [],
|
16
|
-
setSource: expect.any(Function),
|
17
|
-
push: expect.any(Function),
|
18
|
-
popUntil: expect.any(Function),
|
19
|
-
});
|
20
|
-
});
|
21
|
-
|
22
|
-
it('setSource should update the source and clear the current hierarchy', () => {
|
23
|
-
const { result } = renderHook(() => useResourcePath());
|
24
|
-
const source = mockScopedSource({
|
25
|
-
source: { id: '1', name: 'Test source' },
|
26
|
-
resource: { id: '2', name: 'Test resource' },
|
27
|
-
});
|
28
|
-
const resource = mockResource({ id: '3', name: 'Test resource 2' });
|
29
|
-
|
30
|
-
act(() => result.current.setSource(source));
|
31
|
-
act(() => result.current.push(resource));
|
32
|
-
|
33
|
-
// make sure the current resource/hierarchy is populated
|
34
|
-
expect(result.current.currentResource).toBe(resource);
|
35
|
-
expect(result.current.hierarchy).toHaveLength(2);
|
36
|
-
|
37
|
-
// set the source again
|
38
|
-
act(() => result.current.setSource(source));
|
39
|
-
|
40
|
-
// make sure the current resource and hierarchy are reset
|
41
|
-
expect(result.current.source).toBe(source);
|
42
|
-
expect(result.current.currentResource).toBe(null);
|
43
|
-
expect(result.current.hierarchy).toEqual([{ key: 'source:1-resource:2', label: 'Test resource', node: source }]);
|
44
|
-
});
|
45
|
-
|
46
|
-
it('push should add the node to the hierarchy', () => {
|
47
|
-
const { result } = renderHook(() => useResourcePath());
|
48
|
-
const source = mockScopedSource({ source: { id: '1' } });
|
49
|
-
const site = mockResource({ id: '1', type: { code: 'site', name: 'Site' }, name: 'My site' });
|
50
|
-
const page = mockResource({ id: '2', type: { code: 'page', name: 'Page' }, name: 'My page' });
|
51
|
-
|
52
|
-
act(() => result.current.setSource(source));
|
53
|
-
act(() => result.current.push(site));
|
54
|
-
act(() => result.current.push(page));
|
55
|
-
|
56
|
-
expect(result.current.source).toBe(source);
|
57
|
-
expect(result.current.currentResource).toBe(page);
|
58
|
-
expect(result.current.hierarchy).toEqual([
|
59
|
-
{ key: 'source:1-resource:unscoped', label: source.source.name, node: source },
|
60
|
-
{ key: '1', label: site.name, node: site },
|
61
|
-
{ key: '2', label: page.name, node: page },
|
62
|
-
]);
|
63
|
-
});
|
64
|
-
|
65
|
-
it.each([
|
66
|
-
[
|
67
|
-
'popping to the last node should remove nothing',
|
68
|
-
mockResource({ id: '3' }),
|
69
|
-
[
|
70
|
-
{ key: 'source:1-resource:unscoped', label: 'Test source', node: mockScopedSource({ source: { id: '1' } }) },
|
71
|
-
{ key: '1', label: 'Resource 1', node: mockResource({ id: '1', name: 'Resource 1' }) },
|
72
|
-
{ key: '2', label: 'Resource 2', node: mockResource({ id: '2', name: 'Resource 2' }) },
|
73
|
-
{ key: '3', label: 'Resource 3', node: mockResource({ id: '3', name: 'Resource 3' }) },
|
74
|
-
],
|
75
|
-
],
|
76
|
-
[
|
77
|
-
'popping to a node not in the hierarchy should remove everything',
|
78
|
-
mockResource({ id: 'resource-not-in-hierarchy' }),
|
79
|
-
[],
|
80
|
-
],
|
81
|
-
[
|
82
|
-
'popping to the source should remove everything except the source',
|
83
|
-
mockScopedSource({ source: { id: '1' } }),
|
84
|
-
[{ key: 'source:1-resource:unscoped', label: 'Test source', node: mockScopedSource({ source: { id: '1' } }) }],
|
85
|
-
],
|
86
|
-
[
|
87
|
-
'popping to a resource node should remove the resources after it',
|
88
|
-
mockResource({ id: '2' }),
|
89
|
-
[
|
90
|
-
{ key: 'source:1-resource:unscoped', label: 'Test source', node: mockScopedSource({ source: { id: '1' } }) },
|
91
|
-
{ key: '1', label: 'Resource 1', node: mockResource({ id: '1', name: 'Resource 1' }) },
|
92
|
-
{ key: '2', label: 'Resource 2', node: mockResource({ id: '2', name: 'Resource 2' }) },
|
93
|
-
],
|
94
|
-
],
|
95
|
-
])(
|
96
|
-
'popUntil should remove from the hierarchy until the node being popped to is hit - %s',
|
97
|
-
(description: string, node: ScopedSource | Resource, expectedHierarchy: Hierarchy<ScopedSource | Resource>) => {
|
98
|
-
const { result } = renderHook(() => useResourcePath());
|
99
|
-
const source = mockScopedSource({ source: { id: '1' } });
|
100
|
-
const mockResources = [
|
101
|
-
mockResource({ id: '1', name: 'Resource 1' }),
|
102
|
-
mockResource({ id: '2', name: 'Resource 2' }),
|
103
|
-
mockResource({ id: '3', name: 'Resource 3' }),
|
104
|
-
];
|
105
|
-
|
106
|
-
act(() => result.current.setSource(source));
|
107
|
-
act(() => result.current.push(mockResources[0]));
|
108
|
-
act(() => result.current.push(mockResources[1]));
|
109
|
-
act(() => result.current.push(mockResources[2]));
|
110
|
-
|
111
|
-
// make sure hierarchy has expected length before popping
|
112
|
-
expect(result.current.source).toBe(source);
|
113
|
-
expect(result.current.hierarchy).toHaveLength(4);
|
114
|
-
|
115
|
-
act(() => result.current.popUntil(node));
|
116
|
-
|
117
|
-
expect(result.current.hierarchy).toEqual(expectedHierarchy);
|
118
|
-
},
|
119
|
-
);
|
120
|
-
});
|
@@ -1,76 +0,0 @@
|
|
1
|
-
import { useCallback, useMemo, useState } from 'react';
|
2
|
-
import { Hierarchy, ScopedSource, Resource } from '../types';
|
3
|
-
|
4
|
-
/**
|
5
|
-
* Retains the state relating to where in the resource tree we are.
|
6
|
-
*
|
7
|
-
* Including:
|
8
|
-
* * The source.
|
9
|
-
* * A full path from the source to the leaf resource being browser.
|
10
|
-
*/
|
11
|
-
export const useResourcePath = () => {
|
12
|
-
const [source, setSourceInternal] = useState<ScopedSource | null>(null);
|
13
|
-
const [resourceStack, setResourceStack] = useState<Array<Resource>>([]);
|
14
|
-
|
15
|
-
const setSource = useCallback((source: ScopedSource | null, path: Resource[] = []) => {
|
16
|
-
setSourceInternal(source);
|
17
|
-
setResourceStack(path);
|
18
|
-
}, []);
|
19
|
-
|
20
|
-
const push = useCallback(
|
21
|
-
(resource: Resource) => {
|
22
|
-
setResourceStack([...resourceStack, resource]);
|
23
|
-
},
|
24
|
-
[resourceStack],
|
25
|
-
);
|
26
|
-
|
27
|
-
const popUntil = useCallback(
|
28
|
-
(node: ScopedSource | Resource) => {
|
29
|
-
if ('source' in node) {
|
30
|
-
// source node selected, just switch to the source
|
31
|
-
setSource(node);
|
32
|
-
return;
|
33
|
-
}
|
34
|
-
|
35
|
-
// resource node selected
|
36
|
-
const newResourceStack = [...resourceStack];
|
37
|
-
|
38
|
-
// pop until we reach the node that was selected in the breadcrumb
|
39
|
-
while (newResourceStack.length > 0 && newResourceStack[newResourceStack.length - 1].id !== node?.id) {
|
40
|
-
newResourceStack.pop();
|
41
|
-
}
|
42
|
-
|
43
|
-
if (newResourceStack.length === 0) {
|
44
|
-
// if we didn't find the node clear the source to push the user back to the root
|
45
|
-
setSource(null);
|
46
|
-
} else {
|
47
|
-
setResourceStack(newResourceStack);
|
48
|
-
}
|
49
|
-
},
|
50
|
-
[source, resourceStack],
|
51
|
-
);
|
52
|
-
|
53
|
-
const hierarchy = useMemo<Hierarchy<ScopedSource | Resource>>(() => {
|
54
|
-
if (!source) {
|
55
|
-
return [];
|
56
|
-
}
|
57
|
-
|
58
|
-
return [
|
59
|
-
{
|
60
|
-
key: `source:${source.source.id}-resource:${source.resource?.id || 'unscoped'}`,
|
61
|
-
label: source.resource?.name || source.source.name,
|
62
|
-
node: source,
|
63
|
-
},
|
64
|
-
...resourceStack.map((resource) => ({ key: resource.id, node: resource, label: resource.name })),
|
65
|
-
];
|
66
|
-
}, [source, resourceStack]);
|
67
|
-
|
68
|
-
return {
|
69
|
-
source,
|
70
|
-
currentResource: (resourceStack[resourceStack.length - 1] || null) as Resource | null,
|
71
|
-
hierarchy,
|
72
|
-
setSource,
|
73
|
-
push,
|
74
|
-
popUntil,
|
75
|
-
};
|
76
|
-
};
|
@@ -1,17 +0,0 @@
|
|
1
|
-
import React, { SVGAttributes } from 'react';
|
2
|
-
|
3
|
-
type HistoryIconProps = SVGAttributes<SVGElement>;
|
4
|
-
|
5
|
-
export const HistoryIcon = (props: HistoryIconProps) => {
|
6
|
-
return (
|
7
|
-
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
8
|
-
<g id="icon">
|
9
|
-
<path
|
10
|
-
id="Vector"
|
11
|
-
d="M10.5531 2.25131C6.73555 2.14631 3.60805 5.21381 3.60805 9.00131H2.26556C1.92806 9.00131 1.76306 9.40631 2.00306 9.63881L4.09556 11.7388C4.24555 11.8888 4.47806 11.8888 4.62806 11.7388L6.72055 9.63881C6.95305 9.40631 6.78805 9.00131 6.45055 9.00131H5.10805C5.10805 6.07631 7.49305 3.71381 10.4331 3.75131C13.2231 3.78881 15.5706 6.1363 15.6081 8.9263C15.6456 11.8588 13.2831 14.2513 10.3581 14.2513C9.15055 14.2513 8.03305 13.8388 7.14806 13.1413C6.84805 12.9088 6.42805 12.9313 6.15805 13.2013C5.84305 13.5163 5.86555 14.0488 6.21805 14.3188C7.35805 15.2188 8.79055 15.7513 10.3581 15.7513C14.1456 15.7513 17.2131 12.6238 17.1081 8.8063C17.0106 5.28881 14.0706 2.34881 10.5531 2.25131ZM10.1706 6.0013C9.86305 6.0013 9.60805 6.2563 9.60805 6.56381V9.32381C9.60805 9.58631 9.75055 9.83381 9.97555 9.9688L12.3156 11.3563C12.5856 11.5138 12.9306 11.4238 13.0881 11.1613C13.2456 10.8913 13.1556 10.5463 12.8931 10.3888L10.7331 9.10631V6.5563C10.7331 6.2563 10.4781 6.0013 10.1706 6.0013Z"
|
12
|
-
fill="#4F4F4F"
|
13
|
-
/>
|
14
|
-
</g>
|
15
|
-
</svg>
|
16
|
-
);
|
17
|
-
};
|
@@ -1,198 +0,0 @@
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-empty-function */
|
2
|
-
import React from 'react';
|
3
|
-
import { screen, render, waitFor } from '@testing-library/react';
|
4
|
-
import userEvent from '@testing-library/user-event';
|
5
|
-
import { Context as ResponsiveContext } from 'react-responsive';
|
6
|
-
|
7
|
-
import { useOverlayTriggerState, OverlayTriggerState } from 'react-stately';
|
8
|
-
import { DOMAttributes, FocusableElement } from '@react-types/shared';
|
9
|
-
import { useOverlayTrigger } from 'react-aria';
|
10
|
-
|
11
|
-
import PreviewPanel from './PreviewPanel';
|
12
|
-
import { Resource } from '../types';
|
13
|
-
|
14
|
-
function PreviewPanelTestWrapper({
|
15
|
-
constructFunction,
|
16
|
-
}: {
|
17
|
-
constructFunction: (
|
18
|
-
previewModalState: OverlayTriggerState,
|
19
|
-
overlayProps: DOMAttributes<FocusableElement>,
|
20
|
-
) => JSX.Element;
|
21
|
-
}) {
|
22
|
-
const previewModalState = useOverlayTriggerState({});
|
23
|
-
const { overlayProps } = useOverlayTrigger({ type: 'dialog' }, previewModalState);
|
24
|
-
|
25
|
-
return constructFunction(previewModalState, overlayProps);
|
26
|
-
}
|
27
|
-
|
28
|
-
function mockResource(properties: Partial<Resource> = {}): Resource {
|
29
|
-
return {
|
30
|
-
id: 'test-resource',
|
31
|
-
name: 'Test resource',
|
32
|
-
type: { code: 'folder', name: 'Folder' },
|
33
|
-
status: { code: 'live', name: 'Live' },
|
34
|
-
url: 'https://no-where.com',
|
35
|
-
urls: [],
|
36
|
-
childCount: 0,
|
37
|
-
...properties,
|
38
|
-
};
|
39
|
-
}
|
40
|
-
|
41
|
-
function mockImageResource(properties: Partial<Resource> = {}): Resource {
|
42
|
-
return {
|
43
|
-
id: 'test-image-resource',
|
44
|
-
name: 'Test image resource',
|
45
|
-
type: { code: 'image', name: 'Image' },
|
46
|
-
status: { code: 'live', name: 'Live' },
|
47
|
-
url: 'https://www.rspcasa.org.au/wp-content/uploads/2020/10/Baby-goat-in-tree.jpg',
|
48
|
-
urls: [
|
49
|
-
'https://www.rspcasa.org.au/wp-content/uploads/2020/10/Baby-goat-in-tree.jpg',
|
50
|
-
'http://www.rspcasa.org.au/wp-content/uploads/2020/10/Baby-goat-in-tree.jpg',
|
51
|
-
],
|
52
|
-
childCount: 0,
|
53
|
-
...properties,
|
54
|
-
};
|
55
|
-
}
|
56
|
-
|
57
|
-
describe('PreviewPanel', () => {
|
58
|
-
it('Renders a message when no item selected', async () => {
|
59
|
-
const onSelect = jest.fn();
|
60
|
-
const onClose = jest.fn();
|
61
|
-
|
62
|
-
render(
|
63
|
-
<PreviewPanelTestWrapper
|
64
|
-
constructFunction={(previewModalState, overlayProps) => {
|
65
|
-
return (
|
66
|
-
<PreviewPanel
|
67
|
-
resource={null}
|
68
|
-
allowedTypes={undefined}
|
69
|
-
modalState={previewModalState}
|
70
|
-
previewModalOverlayProps={overlayProps}
|
71
|
-
onSelect={onSelect}
|
72
|
-
onClose={onClose}
|
73
|
-
/>
|
74
|
-
);
|
75
|
-
}}
|
76
|
-
/>,
|
77
|
-
);
|
78
|
-
|
79
|
-
await waitFor(() => {
|
80
|
-
expect(screen.getAllByText('Make a selection to see more info here.')).toBeTruthy();
|
81
|
-
});
|
82
|
-
});
|
83
|
-
|
84
|
-
it('Renders an image when an image is selected', async () => {
|
85
|
-
const onSelect = jest.fn();
|
86
|
-
const onClose = jest.fn();
|
87
|
-
const resource = mockImageResource();
|
88
|
-
|
89
|
-
render(
|
90
|
-
<PreviewPanelTestWrapper
|
91
|
-
constructFunction={(previewModalState, overlayProps) => {
|
92
|
-
return (
|
93
|
-
<PreviewPanel
|
94
|
-
resource={resource}
|
95
|
-
allowedTypes={undefined}
|
96
|
-
modalState={previewModalState}
|
97
|
-
previewModalOverlayProps={overlayProps}
|
98
|
-
onSelect={onSelect}
|
99
|
-
onClose={onClose}
|
100
|
-
/>
|
101
|
-
);
|
102
|
-
}}
|
103
|
-
/>,
|
104
|
-
);
|
105
|
-
|
106
|
-
await waitFor(() => {
|
107
|
-
expect(screen.queryByAltText(resource.name)).toBeTruthy();
|
108
|
-
});
|
109
|
-
});
|
110
|
-
|
111
|
-
it('Renders tablet / desktop very above 640px', async () => {
|
112
|
-
const onSelect = jest.fn();
|
113
|
-
const onClose = jest.fn();
|
114
|
-
|
115
|
-
render(
|
116
|
-
<ResponsiveContext.Provider value={{ width: 641 }}>
|
117
|
-
<PreviewPanelTestWrapper
|
118
|
-
constructFunction={(previewModalState, overlayProps) => {
|
119
|
-
return (
|
120
|
-
<PreviewPanel
|
121
|
-
resource={mockResource({ name: 'TestResource' })}
|
122
|
-
allowedTypes={undefined}
|
123
|
-
modalState={previewModalState}
|
124
|
-
previewModalOverlayProps={overlayProps}
|
125
|
-
onSelect={onSelect}
|
126
|
-
onClose={onClose}
|
127
|
-
/>
|
128
|
-
);
|
129
|
-
}}
|
130
|
-
/>
|
131
|
-
</ResponsiveContext.Provider>,
|
132
|
-
);
|
133
|
-
|
134
|
-
await waitFor(() => {
|
135
|
-
expect(screen.queryByRole('dialog')).toBeFalsy();
|
136
|
-
expect(screen.queryByText('TestResource')).toBeTruthy();
|
137
|
-
});
|
138
|
-
});
|
139
|
-
|
140
|
-
it('Renders mobile below 640px', async () => {
|
141
|
-
const onSelect = jest.fn();
|
142
|
-
const onClose = jest.fn();
|
143
|
-
|
144
|
-
render(
|
145
|
-
<ResponsiveContext.Provider value={{ width: 640 }}>
|
146
|
-
<PreviewPanelTestWrapper
|
147
|
-
constructFunction={(previewModalState, overlayProps) => {
|
148
|
-
return (
|
149
|
-
<PreviewPanel
|
150
|
-
resource={mockResource({ name: 'TestResource' })}
|
151
|
-
allowedTypes={undefined}
|
152
|
-
modalState={previewModalState}
|
153
|
-
previewModalOverlayProps={overlayProps}
|
154
|
-
onSelect={onSelect}
|
155
|
-
onClose={onClose}
|
156
|
-
/>
|
157
|
-
);
|
158
|
-
}}
|
159
|
-
/>
|
160
|
-
</ResponsiveContext.Provider>,
|
161
|
-
);
|
162
|
-
|
163
|
-
await waitFor(() => {
|
164
|
-
expect(screen.queryByRole('dialog')).toBeTruthy();
|
165
|
-
expect(screen.queryByText('TestResource')).toBeTruthy();
|
166
|
-
});
|
167
|
-
});
|
168
|
-
|
169
|
-
it('Clicking select button return source and id of resource being shown', async () => {
|
170
|
-
const onSelect = jest.fn();
|
171
|
-
const onClose = jest.fn();
|
172
|
-
const resource = mockResource();
|
173
|
-
|
174
|
-
render(
|
175
|
-
<PreviewPanelTestWrapper
|
176
|
-
constructFunction={(previewModalState, overlayProps) => {
|
177
|
-
return (
|
178
|
-
<PreviewPanel
|
179
|
-
resource={resource}
|
180
|
-
allowedTypes={undefined}
|
181
|
-
modalState={previewModalState}
|
182
|
-
previewModalOverlayProps={overlayProps}
|
183
|
-
onSelect={onSelect}
|
184
|
-
onClose={onClose}
|
185
|
-
/>
|
186
|
-
);
|
187
|
-
}}
|
188
|
-
/>,
|
189
|
-
);
|
190
|
-
|
191
|
-
const user = userEvent.setup();
|
192
|
-
user.click(screen.getByRole('button'));
|
193
|
-
|
194
|
-
await waitFor(() => {
|
195
|
-
expect(onSelect).toHaveBeenCalledWith(resource);
|
196
|
-
});
|
197
|
-
});
|
198
|
-
});
|
@@ -1,76 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import { StoryFn, Meta } from '@storybook/react';
|
3
|
-
import { useOverlayTriggerState } from 'react-stately';
|
4
|
-
import { useOverlayTrigger } from 'react-aria';
|
5
|
-
|
6
|
-
import PreviewPanel from './PreviewPanel';
|
7
|
-
|
8
|
-
export default {
|
9
|
-
title: 'Preview Panel',
|
10
|
-
component: PreviewPanel,
|
11
|
-
} as Meta<typeof PreviewPanel>;
|
12
|
-
|
13
|
-
const Template: StoryFn<typeof PreviewPanel> = ({ resource, allowedTypes }) => {
|
14
|
-
const previewModalState = useOverlayTriggerState({});
|
15
|
-
const { overlayProps } = useOverlayTrigger({ type: 'dialog' }, previewModalState);
|
16
|
-
|
17
|
-
return (
|
18
|
-
<PreviewPanel
|
19
|
-
resource={resource}
|
20
|
-
modalState={previewModalState}
|
21
|
-
previewModalOverlayProps={overlayProps}
|
22
|
-
allowedTypes={allowedTypes}
|
23
|
-
onSelect={({ id, name }) => alert(`Resource Selected: ${id} - ${name}`)}
|
24
|
-
onClose={() => alert(`OnClose Selected`)}
|
25
|
-
/>
|
26
|
-
);
|
27
|
-
};
|
28
|
-
|
29
|
-
export const Primary = Template.bind({});
|
30
|
-
Primary.args = {
|
31
|
-
resource: {
|
32
|
-
id: '1',
|
33
|
-
name: 'Products',
|
34
|
-
type: {
|
35
|
-
code: 'page_standard',
|
36
|
-
name: 'Standard Page',
|
37
|
-
},
|
38
|
-
status: {
|
39
|
-
code: 'live',
|
40
|
-
name: 'Live',
|
41
|
-
},
|
42
|
-
url: 'http://my-squiz.net/assets/1',
|
43
|
-
urls: [],
|
44
|
-
childCount: 0,
|
45
|
-
},
|
46
|
-
allowedTypes: undefined,
|
47
|
-
};
|
48
|
-
|
49
|
-
export const ImageAsset = Template.bind({});
|
50
|
-
ImageAsset.args = {
|
51
|
-
resource: {
|
52
|
-
id: '104',
|
53
|
-
name: 'Little goat face',
|
54
|
-
type: {
|
55
|
-
code: 'image',
|
56
|
-
name: 'Image',
|
57
|
-
},
|
58
|
-
status: {
|
59
|
-
code: 'live',
|
60
|
-
name: 'Live',
|
61
|
-
},
|
62
|
-
url: 'https://www.rspcasa.org.au/wp-content/uploads/2020/10/Baby-goat-in-tree.jpg',
|
63
|
-
urls: [
|
64
|
-
'https://www.rspcasa.org.au/wp-content/uploads/2020/10/Baby-goat-in-tree.jpg',
|
65
|
-
'http://www.rspcasa.org.au/wp-content/uploads/2020/10/Baby-goat-in-tree.jpg',
|
66
|
-
],
|
67
|
-
childCount: 0,
|
68
|
-
},
|
69
|
-
allowedTypes: undefined,
|
70
|
-
};
|
71
|
-
|
72
|
-
export const NoSelected = Template.bind({});
|
73
|
-
NoSelected.args = {
|
74
|
-
...Primary.args,
|
75
|
-
resource: null,
|
76
|
-
};
|
@@ -1,54 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
|
3
|
-
import { Resource } from '../../types';
|
4
|
-
import { Icon, IconOptions } from '@squiz/generic-browser-lib';
|
5
|
-
import StatusIndicator from '../../StatusIndicator/StatusIndicator';
|
6
|
-
|
7
|
-
type MatrixResourceProps = {
|
8
|
-
resource: Resource;
|
9
|
-
};
|
10
|
-
|
11
|
-
const ImageThumb = ({ url, name }: { url: string; name: string }) => {
|
12
|
-
return (
|
13
|
-
<div className="checkered-bg w-full h-[164px] overflow-hidden flex justify-center items-center">
|
14
|
-
<img src={url} className="max-w-full max-h-full" alt={name} />
|
15
|
-
</div>
|
16
|
-
);
|
17
|
-
};
|
18
|
-
|
19
|
-
const MatrixResource = ({ resource: { id, type, name, status, url } }: MatrixResourceProps) => {
|
20
|
-
return (
|
21
|
-
<div>
|
22
|
-
<div className="flex flex-col items-center text-gray-800 mt-7 mx-5 pb-4 border-b border-gray-300">
|
23
|
-
{type.code === 'image' && url ? (
|
24
|
-
<ImageThumb url={url} name={name} />
|
25
|
-
) : (
|
26
|
-
<Icon icon={type.code as IconOptions} resourceSource="matrix" className="w-14 h-14" />
|
27
|
-
)}
|
28
|
-
<div className="mt-4 font-semibold text-base break-word">{name}</div>
|
29
|
-
</div>
|
30
|
-
<div className="text-gray-800 mx-6 mt-4">
|
31
|
-
<dl className="flex flex-col text-sm">
|
32
|
-
<div className="flex mb-2">
|
33
|
-
<dt className="w-[60px] mr-4 text-gray-600">Type</dt>
|
34
|
-
<dd className="font-semibold">{type.name}</dd>
|
35
|
-
</div>
|
36
|
-
|
37
|
-
<div className="flex mb-2">
|
38
|
-
<dt className="w-[60px] mr-4 text-gray-600">Asset ID</dt>
|
39
|
-
<dd className="font-semibold">#{id}</dd>
|
40
|
-
</div>
|
41
|
-
|
42
|
-
<div className="flex mb-2">
|
43
|
-
<dt className="w-[60px] mr-4 text-gray-600">Status</dt>
|
44
|
-
<dd className="flex items-center font-semibold">
|
45
|
-
<StatusIndicator className="mr-1" status={status} /> {status.name}
|
46
|
-
</dd>
|
47
|
-
</div>
|
48
|
-
</dl>
|
49
|
-
</div>
|
50
|
-
</div>
|
51
|
-
);
|
52
|
-
};
|
53
|
-
|
54
|
-
export default MatrixResource;
|
@@ -1,16 +0,0 @@
|
|
1
|
-
$sds-grey-300: #e0e0e0;
|
2
|
-
|
3
|
-
.checkered-bg {
|
4
|
-
@apply bg-white;
|
5
|
-
background-size: 24px 24px;
|
6
|
-
background-position: 0 0, 12px 12px;
|
7
|
-
background-image: linear-gradient(
|
8
|
-
45deg,
|
9
|
-
$sds-grey-300 25%,
|
10
|
-
transparent 25%,
|
11
|
-
transparent 75%,
|
12
|
-
$sds-grey-300 75%,
|
13
|
-
$sds-grey-300
|
14
|
-
),
|
15
|
-
linear-gradient(45deg, $sds-grey-300 25%, transparent 25%, transparent 75%, $sds-grey-300 75%, $sds-grey-300);
|
16
|
-
}
|