@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,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
- });
@@ -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,6 +0,0 @@
1
- import { PreviewPanelHOC, PreviewPanelProps } from '@squiz/generic-browser-lib';
2
- import MatrixResource from './details/MatrixResource';
3
-
4
- export type { PreviewPanelProps };
5
-
6
- export default PreviewPanelHOC(MatrixResource);
@@ -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
- }