@squiz/resource-browser 1.69.1 → 2.1.8-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 +88 -35
  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 +5 -4
  8. package/lib/Hooks/useSources.js +25 -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 +30 -12
  33. package/src/Hooks/useSources.ts +33 -4
  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 -23
  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 -38
  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,163 +1,36 @@
1
- import React, { useEffect, useRef } from 'react';
2
- import { OverlayTriggerState } from 'react-stately';
3
- import { DOMAttributes, FocusableElement } from '@react-types/shared';
4
- import { SkeletonList, ResourceItem, ResourceState } from '@squiz/generic-browser-lib';
5
- import clsx from 'clsx';
1
+ import React from 'react';
2
+ import { Icon, IconOptions } from '@squiz/generic-browser-lib';
3
+ import { ResourceBrowserSource } from '../types';
6
4
 
7
- import { Source, ScopedSource, Resource } from '../types';
8
- import { useCategorisedSources } from '../Hooks/useCategorisedSources';
9
- import { HistoryIcon } from '../Icons/HistoryIcon';
10
- import { RecentResourcesPaths } from '../Hooks/useRecentResourcesPaths';
11
-
12
- export interface SourceListProps {
13
- sources: Source[];
14
- selectedResource?: Resource | null;
15
- previewModalState: OverlayTriggerState;
16
- isLoading: boolean;
17
- onSourceSelect: (node: ScopedSource, overlayProps: DOMAttributes<FocusableElement>) => void;
18
- onSourceDrilldown: (source: ScopedSource) => void;
19
- handleReload: () => void;
20
- setSource: (source: ScopedSource | null, path?: Resource[]) => void;
21
- recentSources: RecentResourcesPaths[];
22
- error: Error | null;
5
+ interface SourceListProps {
6
+ sources: ResourceBrowserSource[];
7
+ onSourceSelect(source: ResourceBrowserSource): void;
23
8
  }
24
9
 
25
- const SourceList = function ({
26
- sources,
27
- selectedResource,
28
- previewModalState,
29
- isLoading,
30
- onSourceSelect,
31
- onSourceDrilldown,
32
- handleReload,
33
- setSource,
34
- recentSources,
35
- error,
36
- }: SourceListProps) {
37
- const categorisedSources = useCategorisedSources(sources);
38
- const listRef = useRef<HTMLUListElement>(null);
39
- const filteredRecentSources = recentSources.filter((item) => item.path?.length);
40
-
41
- useEffect(() => {
42
- if (listRef.current) {
43
- listRef.current?.focus({
44
- preventScroll: true,
45
- });
46
- }
47
- }, []);
48
-
49
- if (isLoading) {
10
+ function SourceList({ sources, onSourceSelect }: SourceListProps) {
50
11
  return (
51
- <div className="flex flex-col bg-gray-100 min-h-full" aria-label="loading Source list">
52
- <SkeletonList itemCount={3} />
53
- <SkeletonList itemCount={3} />
54
- </div>
55
- );
56
- }
57
-
58
- return (
59
- <ul
60
- ref={listRef}
61
- tabIndex={-1}
62
- aria-label={`Source list`}
63
- className={clsx('flex flex-col bg-gray-100 min-h-full focus-visible:outline-0 px-7 py-4')}
64
- >
65
- {error && <ResourceState state="error" message={error.message} handleReload={handleReload} />}
66
-
67
- {!error && filteredRecentSources.length > 0 && (
68
- <li className={`flex flex-col text-sm font-semibold text-grey-800`}>
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">
70
- <span className="z-10 bg-gray-100 px-2.5 flex gap-1 items-center">
71
- <HistoryIcon />
72
- Recent locations
73
- </span>
74
- </div>
75
- <ul aria-label={`recent location nodes`} className="flex flex-col">
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
- }
104
- })}
105
- </ul>
106
- </li>
107
- )}
108
-
109
- {!error &&
110
- categorisedSources.map(({ key, label, sources }, index) => {
111
- return (
112
- <li
113
- key={key}
114
- className={`flex flex-col text-sm font-semibold text-grey-800 ${
115
- index > 0 || filteredRecentSources.length > 0 ? 'mt-3' : ''
116
- }`}
117
- >
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">
119
- <span className="z-10 bg-gray-100 px-2.5">{label}</span>
120
- </div>
121
- {sources.length > 0 && (
122
- <ul aria-label={`${label} nodes`} className="flex flex-col">
123
- {sources.map(({ source, resource }) => {
124
- if (!resource || resource.childCount === 0) {
125
- return (
126
- <ResourceItem
127
- key={`${source.id}-${resource?.id}`}
128
- item={{ source, resource }}
129
- label={resource?.name || source.name}
130
- type={resource?.type.code || 'folder'}
131
- previewModalState={previewModalState}
132
- onSelect={onSourceDrilldown}
133
- className="mt-3 rounded-lg"
134
- showChevron
135
- />
136
- );
137
- }
12
+ <div className="overflow-y-scroll w-screen max-w-[400px] flex-1 grow-[3] border-r border-gray-300 bg-gray-100 pl-4.5 pr-4.5 pb-4.5 pt-3">
13
+ <div className="text-md font-semibold">Select an environment to use</div>
14
+ <ul tabIndex={-1} aria-label={`environment list`} className="flex flex-col bg-gray-100 min-h-full focus-visible:outline-0">
15
+ {sources.map((source, index) => {
138
16
  return (
139
- <ResourceItem
140
- key={`${source.id}-${resource?.id}`}
141
- item={{ source, resource }}
142
- selected={resource?.id == selectedResource?.id && resource != null}
143
- label={resource?.name || source.name}
144
- type={resource?.type.code || 'folder'}
145
- childCount={resource?.childCount || undefined}
146
- previewModalState={previewModalState}
147
- onSelect={onSourceSelect}
148
- onDrillDown={onSourceDrilldown}
149
- className="mt-3 rounded-lg"
150
- showChevron
151
- />
17
+ <li key={index} className="flex items-stretch relative">
18
+ <button
19
+ onClick={() => {
20
+ onSourceSelect(source);
21
+ }}
22
+ className="w-full p-1 mt-3 bg-white border-1 border-grey-200 min-h-[64px] rounded-lg flex items-center text-md font-semibold"
23
+ >
24
+ <Icon icon={source.type as IconOptions} className="ml-4" />
25
+ <span className="line-clamp-2 text-left break-word ml-4">{source.name || source.id}</span>
26
+ <Icon icon={'arrow-right' as IconOptions} className="absolute ml-1 right-4" />
27
+ </button>
28
+ </li>
152
29
  );
153
- })}
154
- </ul>
155
- )}
156
- </li>
157
- );
158
- })}
159
- </ul>
160
- );
161
- };
30
+ })}
31
+ </ul>
32
+ </div>
33
+ );
34
+ }
162
35
 
163
36
  export default SourceList;
@@ -1,31 +1,62 @@
1
- import { DeepPartial, Resource, ScopedSource, Source } from '../types';
1
+ import { ResourceBrowserSource, ResourceBrowserResource, ResourceBrowserPlugin } from '../types';
2
2
 
3
- export const mockSource = (properties: DeepPartial<Source> = {}): Source => {
4
- return {
5
- id: '1',
6
- name: 'Test source',
7
- ...properties,
8
- nodes: properties?.nodes?.map((node) => mockResource(node as Partial<Resource>)) || [],
9
- };
3
+ export type DeepPartial<T> = {
4
+ [P in keyof T]?: T[P] extends Array<infer U>
5
+ ? Array<DeepPartial<U>>
6
+ : T[P] extends ReadonlyArray<infer U>
7
+ ? ReadonlyArray<DeepPartial<U>>
8
+ : DeepPartial<T[P]>;
10
9
  };
11
10
 
12
- export const mockResource = (properties: Partial<Resource> = {}): Resource => {
13
- return {
14
- id: '1',
15
- name: 'Test resource',
16
- type: { code: 'folder', name: 'Folder' },
17
- status: { code: 'live', name: 'Live' },
18
- url: 'https://no-where.com',
19
- urls: [],
20
- childCount: 0,
21
- lineages: [],
22
- ...properties,
23
- };
11
+ export const mockSource = (properties: DeepPartial<ResourceBrowserSource> = {}): ResourceBrowserSource => {
12
+ return {
13
+ id: '1',
14
+ name: 'Test source',
15
+ type: 'dam',
16
+ ...properties,
17
+ };
24
18
  };
25
19
 
26
- export const mockScopedSource = (properties: DeepPartial<ScopedSource> = {}): ScopedSource => {
27
- return {
28
- source: mockSource(properties?.source),
29
- resource: properties?.resource ? mockResource(properties?.resource as Partial<Resource>) : null,
30
- };
20
+ export const mockPlugin = (properties: DeepPartial<ResourceBrowserPlugin> = {}): ResourceBrowserPlugin => {
21
+ return {
22
+ type: 'dam',
23
+ // @ts-ignore
24
+ sourceBrowserComponent: jest.fn(),
25
+ // @ts-ignore
26
+ renderSelectedResource: jest.fn(),
27
+ // @ts-ignore
28
+ resolveResource: jest.fn(),
29
+ ...properties,
30
+ };
31
+ };
32
+
33
+ export const mockResource = (properties: Partial<ResourceBrowserResource> = {}): ResourceBrowserResource => {
34
+ return {
35
+ id: '1',
36
+ name: 'Resource 1',
37
+ url: '',
38
+ source: {
39
+ id: '1',
40
+ type: 'dam',
41
+ },
42
+ type: {
43
+ code: 'jpeg',
44
+ name: 'JPEG image',
45
+ },
46
+ squizImage: {
47
+ name: 'an-image-name',
48
+ imageVariations: {
49
+ original: {
50
+ width: 1024,
51
+ height: 900,
52
+ url: 'an-image-url',
53
+ mimeType: 'image/jpeg',
54
+ byteSize: 1068,
55
+ sha1Hash: '',
56
+ aspectRatio: '',
57
+ },
58
+ },
59
+ },
60
+ ...properties,
61
+ };
31
62
  };
@@ -0,0 +1,98 @@
1
+ import React from 'react';
2
+ import {
3
+ ResourceBrowserPlugin,
4
+ ResourceBrowserUIProps,
5
+ ResourceBrowserResource,
6
+ ResourceBrowserSource,
7
+ ResourceBrowserSelectedState,
8
+ useResolveResourceResponse,
9
+ } from '../types';
10
+
11
+ // The plugin must conform to the Typescript interface ResourceBrowserPlugin
12
+ export default (): ResourceBrowserPlugin => {
13
+ return {
14
+ /**
15
+ * The type of source this plugin handles. Plugins can only handle a single ResourceBrowserSource type, export multiple
16
+ * plugins from your package with different types if you have a single codebase that can handle more than one type.
17
+ *
18
+ * This value needs to match the ResourceBrowserSource.type field that is passed to the ResourceBrowser via OnRequestSources.
19
+ */
20
+ type: 'exampleType',
21
+
22
+ /**
23
+ * Enables a div in the header of the ResourceBrowser modal for a plugin to insert content using React Portals.
24
+ * DOM element is passed into the Functional Component created using the sourceBrowserComponent function.
25
+ */
26
+ headerPortal: false,
27
+
28
+ /**
29
+ * Provide a React Functional Component for browsing the ResourceBrowserSource type; this will be rendered within the
30
+ * ResourceBrowers modal window when the source type is selected or if a pre-existing resource is being changed.
31
+ *
32
+ * The input props should be of the type ResourceBrowserUIProps so the ResourceBrowser can provide the following information:
33
+ * @param source will contain the source information provided to the ResourceBrowser.
34
+ * @param headerPortal (optional) may contain a DOM element for insertion of content using React Portals.
35
+ * @param preselectedResource (optional) may contain a resource that is being changed to start open in your UI (optional).
36
+ * @param onSelected should be called when a selection has been made and the ResourceBrowser should be closed.
37
+ *
38
+ * @returns ReactElement
39
+ */
40
+ sourceBrowserComponent: () => {
41
+ // This should return a fucntion component, probably from another file.
42
+ return (props: ResourceBrowserUIProps) => {
43
+ return <></>;
44
+ };
45
+ },
46
+
47
+ /**
48
+ * Provide params to assist Resource Browser in rendering a preview of the selected resource to the end user.
49
+ *
50
+ * @param resource The resource which is to be rendered to the frontend UI
51
+ *
52
+ * @returns { showThumbnail } if true will render a preview thumbnail using the SquizImage data or the resource data.
53
+ * @returns { icon } if showThumbnail evaluates to false this will be rendered to the left side of the resource label.
54
+ * @returns { label } Label for the selected resource.
55
+ * @returns { description } Description lines to provide additional information about the selected resource.
56
+ */
57
+ renderSelectedResource: (resource: ResourceBrowserResource): Promise<ResourceBrowserSelectedState> => {
58
+ // ...Fetch information from source for resource here
59
+ return Promise.resolve({
60
+ showThumbnail: true, // If the resource is an image that can be previewed set this to true
61
+ icon: undefined, // (optionally) otherwise provide an icon for render
62
+ label: '', // Label or name of resource
63
+ description: [], // (optional) extra details on the resource e.g. size, mime type etc
64
+ });
65
+ },
66
+
67
+ /**
68
+ * Provide a function to resolve a partial resource reference into a full data structure. Hooks may be used within.
69
+ *
70
+ * @param resourceId The id of the resource to resolve, this may be null if no resource is currently selected
71
+ * @param source The source structure of the resource to resolve, this may be null before the source retrieval has completed
72
+ *
73
+ * @returns { ResourceBrowserResource } Full data structure needed by the Resource Browser and its consumers.
74
+ *
75
+ */
76
+ useResolveResource: (resourceId: string | null, source: ResourceBrowserSource | null): useResolveResourceResponse => {
77
+ // ...Fetch information from source for resource here
78
+ return {
79
+ data:
80
+ resourceId && source
81
+ ? {
82
+ id: resourceId,
83
+ name: '',
84
+ url: '',
85
+ source: source,
86
+ type: {
87
+ code: '',
88
+ name: '',
89
+ },
90
+ squizImage: undefined,
91
+ }
92
+ : null,
93
+ error: null,
94
+ isLoading: false,
95
+ };
96
+ },
97
+ };
98
+ };
@@ -0,0 +1,141 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { Icon } from '@squiz/generic-browser-lib';
4
+
5
+ import sampleSources from './sample-sources.json';
6
+ import { ResourceBrowserPlugin, ResourceBrowserResource } from '../types';
7
+ type CreateCallbacksProps = Partial<{
8
+ delay: number;
9
+ sourceIsLoading: boolean;
10
+ singleSource?: boolean;
11
+ error?: string;
12
+ }>;
13
+
14
+ export const createPlugins = (callbackWait: number, headerPortal = false): ResourceBrowserPlugin[] => {
15
+ const types = ['dam', 'matrix'];
16
+ return types.map((type): ResourceBrowserPlugin => {
17
+ return {
18
+ //@ts-ignore
19
+ type,
20
+ createHeaderPortal: headerPortal,
21
+ sourceBrowserComponent: () => {
22
+ return (props) => {
23
+ return (
24
+ <div className="h-screen lg:h-[calc(100vh-9rem)] w-screen max-w-[50rem]">
25
+ <div>THIS IS A {type} PLUGIN</div>
26
+ <button
27
+ onClick={() => {
28
+ props.onSelected({
29
+ id: '1f7a25b4-380f-4540-9555-8be2dcab4019',
30
+ source: props.source,
31
+ } as ResourceBrowserResource);
32
+ }}
33
+ >
34
+ Press to set resource
35
+ </button>
36
+ {headerPortal && props.headerPortal && createPortal(<div>HeadPortal</div>, props.headerPortal)}
37
+ </div>
38
+ );
39
+ };
40
+ },
41
+ renderSelectedResource: (resource) => {
42
+ return new Promise((resolve, reject) => {
43
+ const fileSize = resource.squizImage?.imageVariations?.original?.byteSize;
44
+ const fileWidth = resource.squizImage?.imageVariations?.original?.width;
45
+ const fileHeight = resource.squizImage?.imageVariations?.original?.height;
46
+
47
+ setTimeout(() => {
48
+ resolve({
49
+ showThumbnail: resource.squizImage ? true : false,
50
+ icon: <Icon icon={resource.type.code} resourceSource="matrix" className="w-4 h-4" />,
51
+ label: resource.name || resource.squizImage?.name || resource.id,
52
+ description: [
53
+ <dl
54
+ key="1"
55
+ className="col-start-2 col-end-2 flex flex-row gap-1 justify-self-start items-center font-normal text-sm"
56
+ >
57
+ <div>
58
+ <dt className="hidden">Asset size</dt>
59
+ <dd className="text-gray-600">
60
+ {resource.squizImage && (
61
+ <>
62
+ {fileSize} kB , {fileWidth} x {fileHeight}px
63
+ </>
64
+ )}
65
+ </dd>
66
+ </div>
67
+ </dl>,
68
+ ],
69
+ });
70
+ }, callbackWait);
71
+ });
72
+ },
73
+ useResolveResource: (unresolvedResource) => {
74
+ const [resource, setResource] = useState<ResourceBrowserResource | null>(null);
75
+ const [error, setError] = useState<Error | null>(null);
76
+ const [isLoading, setIsLoading] = useState<boolean>(false);
77
+
78
+ useEffect(() => {
79
+ if (unresolvedResource !== null) {
80
+ setTimeout(() => {
81
+ setIsLoading(false);
82
+ setResource({
83
+ id: unresolvedResource.resource,
84
+ name: 'An image from Bynder',
85
+ url: 'https://cdn.mediavalet.com/aunsw/squiz/KfIjcjUJz0O6kWoInBys5A/XpBxg3Hj-0qOXFWnAAWvXQ/Large/RX-7.jpeg',
86
+ source: unresolvedResource.source,
87
+ type: {
88
+ code: 'jpeg',
89
+ name: 'JPEG image',
90
+ },
91
+ squizImage: {
92
+ name: 'An image from Bynder',
93
+ imageVariations: {
94
+ original: {
95
+ width: 1024,
96
+ height: 900,
97
+ url: 'https://cdn.mediavalet.com/aunsw/squiz/KfIjcjUJz0O6kWoInBys5A/XpBxg3Hj-0qOXFWnAAWvXQ/Large/RX-7.jpeg',
98
+ mimeType: 'image/jpeg',
99
+ byteSize: 1068,
100
+ sha1Hash: '',
101
+ aspectRatio: '',
102
+ },
103
+ },
104
+ },
105
+ });
106
+ }, callbackWait);
107
+ }
108
+ }, [unresolvedResource, setResource, setIsLoading]);
109
+
110
+ return {
111
+ data: resource,
112
+ error: error,
113
+ isLoading: isLoading,
114
+ };
115
+ },
116
+ };
117
+ });
118
+ };
119
+
120
+ export const createResourceBrowserCallbacks = ({
121
+ delay = 500,
122
+ sourceIsLoading = false,
123
+ error,
124
+ singleSource = false,
125
+ }: CreateCallbacksProps = {}) => {
126
+ return {
127
+ onRequestSources: () => {
128
+ return new Promise((resolve, reject) => {
129
+ if (!sourceIsLoading) {
130
+ setTimeout(() => {
131
+ if (error && Math.random() > 0.5) {
132
+ reject(new Error(error));
133
+ } else {
134
+ resolve(singleSource ? [sampleSources[0]] : sampleSources);
135
+ }
136
+ }, delay);
137
+ }
138
+ });
139
+ },
140
+ };
141
+ };
@@ -1,23 +1,19 @@
1
1
  import React, { ReactElement, ReactNode } from 'react';
2
- import {
3
- ResourceBrowserContextProps,
4
- ResourceBrowserContextProvider,
5
- } from '../ResourceBrowserContext/ResourceBrowserContext';
2
+ import { ResourceBrowserContextProps, ResourceBrowserContextProvider } from '../ResourceBrowserContext/ResourceBrowserContext';
6
3
  import { render } from '@testing-library/react';
7
4
 
8
5
  export const renderWithContext = (ui: ReactElement, context: Partial<ResourceBrowserContextProps> = {}) => {
9
- return render(ui, {
10
- wrapper: ({ children }: { children: ReactNode }): ReactElement => (
11
- <ResourceBrowserContextProvider
12
- value={{
13
- onRequestSources: jest.fn(),
14
- onRequestChildren: jest.fn(),
15
- onRequestResource: jest.fn(),
16
- ...context,
17
- }}
18
- >
19
- {children}
20
- </ResourceBrowserContextProvider>
21
- ),
22
- });
6
+ return render(ui, {
7
+ wrapper: ({ children }: { children: ReactNode }): ReactElement => (
8
+ <ResourceBrowserContextProvider
9
+ value={{
10
+ onRequestSources: jest.fn(),
11
+ plugins: [],
12
+ ...context,
13
+ }}
14
+ >
15
+ {children}
16
+ </ResourceBrowserContextProvider>
17
+ ),
18
+ });
23
19
  };
@@ -0,0 +1,32 @@
1
+ [
2
+ {
3
+ "name": "Bynder #1",
4
+ "id": "c90feac1-55f3-4e1f-9b56-c22829e3f510",
5
+ "type": "dam",
6
+ "group": "DAM"
7
+ },
8
+ {
9
+ "name": "Bynder #2",
10
+ "id": "6a238a04-b5bd-44d5-940f-e235b3b352ab",
11
+ "type": "dam",
12
+ "group": "DAM"
13
+ },
14
+ {
15
+ "name": "MediaValet",
16
+ "id": "747b81ac-adce-455e-ac5a-bce7ac738115",
17
+ "type": "dam",
18
+ "group": "DAM"
19
+ },
20
+ {
21
+ "name": "Matrix site #1",
22
+ "id": "123",
23
+ "type": "matrix",
24
+ "group": "Matrix DAM UAT system"
25
+ },
26
+ {
27
+ "name": "Matrix site #2",
28
+ "id": "321",
29
+ "type": "matrix",
30
+ "group": "Matrix DAM UAT system"
31
+ }
32
+ ]
package/src/index.scss CHANGED
@@ -4,30 +4,40 @@
4
4
  @import 'tailwindcss/utilities';
5
5
 
6
6
  // Components
7
- @import './ResourceBreadcrumb/resource-breadcrumb';
8
7
  @import './ResourcePicker/resource-picker';
9
- @import './PreviewPanel/details/matrix-resource';
10
8
  @import '@squiz/generic-browser-lib/src/Spinner/spinner';
11
9
  @import '@squiz/generic-browser-lib/src/Skeleton/skeleton';
12
10
 
13
11
  *,
14
12
  button {
15
- @apply antialiased;
16
- letter-spacing: -0.02em;
13
+ @apply antialiased;
14
+ letter-spacing: -0.02em;
17
15
  }
18
16
 
19
17
  svg {
20
- @apply text-gray-600;
18
+ @apply text-gray-600;
21
19
  }
22
20
 
23
21
  .p-4\.5 {
24
- padding: 18px;
22
+ padding: 18px;
23
+ }
24
+
25
+ .pl-4\.5 {
26
+ padding-left: 18px;
27
+ }
28
+
29
+ .pr-4\.5 {
30
+ padding-right: 18px;
31
+ }
32
+
33
+ .pb-4\.5 {
34
+ padding-bottom: 18px;
25
35
  }
26
36
 
27
37
  // In tailwind there is no break-word as it is deprecated, but break-words which is slightly different does not work here, so I have added the suggested combination here
28
38
  // https://v1.tailwindcss.com/docs/word-break
29
39
  // https://github.com/tailwindlabs/tailwindcss/discussions/2213
30
40
  .break-word {
31
- word-break: break-word;
32
- overflow-wrap: anywhere;
41
+ word-break: break-word;
42
+ overflow-wrap: anywhere;
33
43
  }