@squiz/resource-browser 3.2.0 → 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Change Log
2
2
 
3
+ ## 3.2.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 168c317: Pass all preselected info to resolvers, dam resolve variant
8
+ - 168c317: Added optional extra fields to pass dam variant information
9
+
10
+ ## 3.2.1
11
+
12
+ ### Patch Changes
13
+
14
+ - a64f8ae: Derive plugin synchronously from source to avoid stale state
15
+
3
16
  ## 3.2.0
4
17
 
5
18
  ### Minor Changes
@@ -12,7 +12,7 @@ const ImageInline_1 = require("../Icons/ImageInline");
12
12
  const LinkInline_1 = require("../Icons/LinkInline");
13
13
  const ResourceBrowserInlineButton = ({ inlineType, modalTitle, allowedTypes, onChange, onRetry, value, useResource, isDisabled, plugin, pluginMode, searchEnabled, source, sources, isLoading, error, setSource, isModalOpen, onModalStateChange, }) => {
14
14
  // If an error happens loading the resource the inline browser just opens without it as if no preselectedResource is provided
15
- const { data: resource, isLoading: isResourceLoading } = useResource(value?.resourceId || null, source);
15
+ const { data: resource, isLoading: isResourceLoading } = useResource(value || null, source);
16
16
  const isImagePicker = inlineType === 'image';
17
17
  return (react_1.default.createElement("div", { className: "inline-launch-button" },
18
18
  react_1.default.createElement(resource_browser_ui_lib_1.ModalTrigger, { overlayTriggerState: {
@@ -5,7 +5,7 @@ export type ResourceBrowserInputProps = {
5
5
  allowedTypes?: string[];
6
6
  isDisabled?: boolean;
7
7
  value: ResourceBrowserUnresolvedResource | null;
8
- useResource(referenceId: string | null, source: ResourceBrowserSource | null): useResolveResourceResponse;
8
+ useResource(reference: ResourceBrowserUnresolvedResource | null, source: ResourceBrowserSource | null): useResolveResourceResponse;
9
9
  onChange(resource: ResourceBrowserResource | null): void;
10
10
  onClear?(): void;
11
11
  plugin: ResourceBrowserPlugin | null;
@@ -8,7 +8,7 @@ const react_1 = __importDefault(require("react"));
8
8
  const MainContainer_1 = __importDefault(require("../MainContainer/MainContainer"));
9
9
  const ResourcePicker_1 = require("../ResourcePicker/ResourcePicker");
10
10
  const ResourceBrowserInput = ({ modalTitle, allowedTypes, onChange, value, useResource, isDisabled, onClear, plugin, pluginMode, searchEnabled, source, sources, isLoading, isOtherSourceValue, error, setSource, isModalOpen, onModalStateChange, }) => {
11
- const { data: resource, error: resourceError, isLoading: isResourceLoading } = useResource(value?.resourceId || null, source);
11
+ const { data: resource, error: resourceError, isLoading: isResourceLoading } = useResource(value || null, source);
12
12
  const defaultOnClear = () => onChange(null);
13
13
  const onClearFunction = onClear ?? defaultOnClear;
14
14
  return (react_1.default.createElement(ResourcePicker_1.ResourcePicker, { resource: resource, plugin: plugin, allowedTypes: allowedTypes, error: resourceError || error, isLoading: isResourceLoading || isLoading, isOtherSourceValue: isOtherSourceValue, isDisabled: isDisabled, onClear: onClearFunction, isModalOpen: isModalOpen, onModalStateChange: onModalStateChange }, (onClose, titleProps) => (react_1.default.createElement(MainContainer_1.default, { selectedSource: source, sources: sources, preselectedResource: resource, plugin: plugin, pluginMode: pluginMode, searchEnabled: searchEnabled, title: modalTitle, titleAriaProps: titleProps, allowedTypes: allowedTypes, onSourceSelect: setSource, onClose: onClose, onChange: onChange }))));
package/lib/index.css CHANGED
@@ -6127,20 +6127,22 @@
6127
6127
  }
6128
6128
  .squiz-rb-scope .image-info:not(.squiz-rb-plugin *) {
6129
6129
  align-items: center;
6130
- border-top: 1px solid #e0e0e0;
6131
6130
  color: #707070;
6132
6131
  display: flex;
6133
6132
  flex-direction: column;
6134
- height: calc(50vh - 60px);
6135
- overflow-y: auto;
6136
6133
  }
6137
6134
  .squiz-rb-scope .image-info__image-container:not(.squiz-rb-plugin *) {
6138
6135
  position: relative;
6139
6136
  width: 250px;
6140
6137
  height: 150px;
6141
- margin: 1.25rem;
6138
+ min-height: 150px;
6139
+ margin: 1.25rem 0;
6142
6140
  background-color: #ededed;
6143
6141
  }
6142
+ .squiz-rb-scope .image-info__divider:not(.squiz-rb-plugin *) {
6143
+ width: 100%;
6144
+ padding: 0 1.25rem;
6145
+ }
6144
6146
  .squiz-rb-scope .checkered-bg:not(.squiz-rb-plugin *) {
6145
6147
  --tw-bg-opacity: 1;
6146
6148
  background-color: rgb(255 255 255 / var(--tw-bg-opacity));
@@ -6186,10 +6188,13 @@
6186
6188
  display: flex;
6187
6189
  flex-direction: column;
6188
6190
  margin: 1rem 0;
6191
+ width: 100%;
6192
+ padding: 0 1.25rem;
6189
6193
  }
6190
6194
  .squiz-rb-scope .image-info__details-item:not(.squiz-rb-plugin *) {
6191
6195
  display: flex;
6192
6196
  margin-bottom: 0.5rem;
6197
+ padding-left: 0.25rem;
6193
6198
  }
6194
6199
  .squiz-rb-scope .image-info__details-label:not(.squiz-rb-plugin *) {
6195
6200
  color: #707070;
@@ -6201,7 +6206,8 @@
6201
6206
  color: #3d3d3d;
6202
6207
  }
6203
6208
  .squiz-rb-scope .image-variant__list:not(.squiz-rb-plugin *) {
6204
- width: 280px;
6209
+ width: 100%;
6210
+ padding: 0 1.25rem;
6205
6211
  margin-bottom: 1rem;
6206
6212
  }
6207
6213
  .squiz-rb-scope .image-variant__item:not(.squiz-rb-plugin *) {
@@ -6257,21 +6263,6 @@
6257
6263
  .squiz-rb-scope .image-info__loading:not(.squiz-rb-plugin *) {
6258
6264
  margin-top: 80px;
6259
6265
  }
6260
- @media (min-width: 768px) {
6261
- .squiz-rb-scope .image-info:not(.squiz-rb-plugin *) {
6262
- height: calc(100vh - 60px);
6263
- }
6264
- }
6265
- @media (min-width: 1024px) {
6266
- .squiz-rb-scope .image-info:not(.squiz-rb-plugin *) {
6267
- height: calc(100vh - 14rem);
6268
- overflow-y: auto;
6269
- }
6270
- }
6271
- .squiz-rb-scope .divider-container:not(.squiz-rb-plugin *) {
6272
- padding: 0 1.25rem;
6273
- width: 100%;
6274
- }
6275
6266
  .squiz-rb-scope .resource-picker:not(.squiz-rb-plugin *) {
6276
6267
  display: grid;
6277
6268
  grid-template-columns: 24px 1fr;
package/lib/index.js CHANGED
@@ -55,7 +55,7 @@ const ResourceBrowser = (props) => {
55
55
  const [source, setSource] = (0, react_1.useState)(null);
56
56
  const [mode, setMode] = (0, react_1.useState)(null);
57
57
  const { data: sources, isLoading, error: sourcesError, reload: reloadSources } = (0, useSources_1.useSources)({ onRequestSources, plugins });
58
- const [plugin, setPlugin] = (0, react_1.useState)(null);
58
+ const plugin = source?.plugin || null;
59
59
  // MainContainer will render a list of sources of one is not provided to it, callback to allow it to set the source once a user selects
60
60
  const handleSourceSelect = (0, react_1.useCallback)((source, mode) => {
61
61
  setSource(source);
@@ -82,16 +82,6 @@ const ResourceBrowser = (props) => {
82
82
  setSource(source);
83
83
  setMode(null); // Passed in resource will always use the default mode
84
84
  }, [value, isLoading, sources, setSource, setError]);
85
- // When a source is selected update our plugin reference to match (legacy support)
86
- // the plugin is now attached to the source directly when fetched from the context so use that instead when possible
87
- (0, react_1.useEffect)(() => {
88
- if (source?.plugin) {
89
- setPlugin(source.plugin);
90
- }
91
- else {
92
- setPlugin(null);
93
- }
94
- }, [plugins, source]);
95
85
  // The modal has some control over it own open/closed state (for WCAG reasons) so keep this in sync with our state
96
86
  const handleModalStateChange = (0, react_1.useCallback)((isOpen) => {
97
87
  setIsModalOpen(isOpen);
package/lib/types.d.ts CHANGED
@@ -31,6 +31,7 @@ export type PluginLaunchMode = {
31
31
  export type ResourceBrowserUnresolvedResource = {
32
32
  sourceId: string;
33
33
  resourceId: string;
34
+ [k: string]: unknown;
34
35
  };
35
36
  export type ResourceBrowserResource = {
36
37
  id: string;
@@ -93,6 +94,6 @@ export interface ResourceBrowserPlugin {
93
94
  /** Function to provde the the summary information to show what resource is currently selected */
94
95
  renderSelectedResource: (resource: ResourceBrowserResource) => Promise<ResourceBrowserSelectedState>;
95
96
  /** Function to resolve a resource and source id into a fully resolved output reference */
96
- useResolveResource: (resourceId: string | null, source: ResourceBrowserSource | null) => useResolveResourceResponse;
97
+ useResolveResource: (resource: ResourceBrowserUnresolvedResource | null, source: ResourceBrowserSource | null) => useResolveResourceResponse;
97
98
  }
98
99
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/resource-browser",
3
- "version": "3.2.0",
3
+ "version": "3.2.2",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "private": false,
@@ -33,7 +33,7 @@ export const ResourceBrowserInlineButton = ({
33
33
  onModalStateChange,
34
34
  }: ResourceBrowserInlineButtonProps) => {
35
35
  // If an error happens loading the resource the inline browser just opens without it as if no preselectedResource is provided
36
- const { data: resource, isLoading: isResourceLoading } = useResource(value?.resourceId || null, source);
36
+ const { data: resource, isLoading: isResourceLoading } = useResource(value || null, source);
37
37
 
38
38
  const isImagePicker = inlineType === 'image';
39
39
 
@@ -16,7 +16,7 @@ export type ResourceBrowserInputProps = {
16
16
  allowedTypes?: string[];
17
17
  isDisabled?: boolean;
18
18
  value: ResourceBrowserUnresolvedResource | null;
19
- useResource(referenceId: string | null, source: ResourceBrowserSource | null): useResolveResourceResponse;
19
+ useResource(reference: ResourceBrowserUnresolvedResource | null, source: ResourceBrowserSource | null): useResolveResourceResponse;
20
20
  onChange(resource: ResourceBrowserResource | null): void;
21
21
  onClear?(): void;
22
22
  plugin: ResourceBrowserPlugin | null;
@@ -52,7 +52,7 @@ export const ResourceBrowserInput = ({
52
52
  isModalOpen,
53
53
  onModalStateChange,
54
54
  }: ResourceBrowserInputProps) => {
55
- const { data: resource, error: resourceError, isLoading: isResourceLoading } = useResource(value?.resourceId || null, source);
55
+ const { data: resource, error: resourceError, isLoading: isResourceLoading } = useResource(value || null, source);
56
56
 
57
57
  const defaultOnClear = () => onChange(null);
58
58
  const onClearFunction = onClear ?? defaultOnClear;
@@ -222,7 +222,7 @@ describe('Resource browser input', () => {
222
222
  });
223
223
 
224
224
  await waitFor(() => {
225
- expect(mockUseResolveResource).toHaveBeenCalledWith(resource.resourceId, calculateExpectedSource(source));
225
+ expect(mockUseResolveResource).toHaveBeenCalledWith(resource, calculateExpectedSource(source));
226
226
  });
227
227
  });
228
228
 
@@ -666,5 +666,53 @@ describe('Resource browser input', () => {
666
666
  );
667
667
  });
668
668
  });
669
+
670
+ it('switching source updates plugin and does not use the old plugin', async () => {
671
+ const damSource = mockSource({ type: 'dam', id: '1' });
672
+ const matrixSource = mockSource({ type: 'matrix', id: '2' });
673
+
674
+ const pluginA = { ...mockDamPlugin, type: 'dam' };
675
+ const pluginB = { ...mockDamPlugin, type: 'matrix' };
676
+
677
+ mockRequestSources.mockResolvedValueOnce([damSource, matrixSource]);
678
+
679
+ renderComponent();
680
+
681
+ const { setSource } = (RBI.ResourceBrowserInput as jest.Mock).mock.calls[0][0];
682
+
683
+ act(() => {
684
+ setSource({ ...damSource, plugin: pluginA });
685
+ });
686
+
687
+ await waitFor(() => {
688
+ expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(
689
+ expect.objectContaining({
690
+ plugin: pluginA,
691
+ }),
692
+ expect.any(Object),
693
+ );
694
+ });
695
+
696
+ (Plugin.PluginRender as jest.Mock).mockClear();
697
+
698
+ act(() => {
699
+ setSource({ ...matrixSource, plugin: pluginB });
700
+ });
701
+
702
+ await waitFor(() => {
703
+ expect(Plugin.PluginRender).toHaveBeenCalledWith(
704
+ expect.objectContaining({
705
+ plugin: pluginB,
706
+ }),
707
+ expect.any(Object),
708
+ );
709
+ expect(Plugin.PluginRender).not.toHaveBeenCalledWith(
710
+ expect.objectContaining({
711
+ plugin: pluginA,
712
+ }),
713
+ expect.any(Object),
714
+ );
715
+ });
716
+ });
669
717
  });
670
718
  });
package/src/index.tsx CHANGED
@@ -6,7 +6,6 @@ import {
6
6
  PluginLaunchMode,
7
7
  ResourceBrowserUnresolvedResource,
8
8
  ResourceBrowserResource,
9
- ResourceBrowserPlugin,
10
9
  ResourceBrowserSourceWithPlugin,
11
10
  } from './types';
12
11
  import { useSources } from './Hooks/useSources';
@@ -49,7 +48,7 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
49
48
  const [source, setSource] = useState<ResourceBrowserSourceWithPlugin | null>(null);
50
49
  const [mode, setMode] = useState<PluginLaunchMode | null>(null);
51
50
  const { data: sources, isLoading, error: sourcesError, reload: reloadSources } = useSources({ onRequestSources, plugins });
52
- const [plugin, setPlugin] = useState<ResourceBrowserPlugin | null>(null);
51
+ const plugin = source?.plugin || null;
53
52
 
54
53
  // MainContainer will render a list of sources of one is not provided to it, callback to allow it to set the source once a user selects
55
54
  const handleSourceSelect = useCallback(
@@ -83,16 +82,6 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
83
82
  setMode(null); // Passed in resource will always use the default mode
84
83
  }, [value, isLoading, sources, setSource, setError]);
85
84
 
86
- // When a source is selected update our plugin reference to match (legacy support)
87
- // the plugin is now attached to the source directly when fetched from the context so use that instead when possible
88
- useEffect(() => {
89
- if (source?.plugin) {
90
- setPlugin(source.plugin);
91
- } else {
92
- setPlugin(null);
93
- }
94
- }, [plugins, source]);
95
-
96
85
  // The modal has some control over it own open/closed state (for WCAG reasons) so keep this in sync with our state
97
86
  const handleModalStateChange = useCallback(
98
87
  (isOpen: boolean) => {
package/src/types.ts CHANGED
@@ -43,6 +43,7 @@ export type PluginLaunchMode = {
43
43
  export type ResourceBrowserUnresolvedResource = {
44
44
  sourceId: string;
45
45
  resourceId: string;
46
+ [k: string]: unknown; // Additional props as needed by the plugin type to resolve the asset
46
47
  };
47
48
 
48
49
  export type ResourceBrowserResource = {
@@ -120,5 +121,8 @@ export interface ResourceBrowserPlugin {
120
121
  renderSelectedResource: (resource: ResourceBrowserResource) => Promise<ResourceBrowserSelectedState>;
121
122
 
122
123
  /** Function to resolve a resource and source id into a fully resolved output reference */
123
- useResolveResource: (resourceId: string | null, source: ResourceBrowserSource | null) => useResolveResourceResponse;
124
+ useResolveResource: (
125
+ resource: ResourceBrowserUnresolvedResource | null,
126
+ source: ResourceBrowserSource | null,
127
+ ) => useResolveResourceResponse;
124
128
  }