@squiz/resource-browser 3.0.1-rc.0 → 3.0.1-rc.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
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [3.0.1-rc.2](https://gitlab.squiz.net/dxp/dxp-shared-ui/resource-browser/compare/@squiz/resource-browser@3.0.1-rc.1...@squiz/resource-browser@3.0.1-rc.2) (2024-12-05)
7
+
8
+ **Note:** Version bump only for package @squiz/resource-browser
9
+
10
+ ## [3.0.1-rc.1](https://gitlab.squiz.net/dxp/dxp-shared-ui/resource-browser/compare/@squiz/resource-browser@3.0.1-rc.0...@squiz/resource-browser@3.0.1-rc.1) (2024-11-29)
11
+
12
+ **Note:** Version bump only for package @squiz/resource-browser
13
+
6
14
  ## [3.0.1-rc.0](https://gitlab.squiz.net/dxp/dxp-shared-ui/resource-browser/compare/@squiz/resource-browser@2.4.12...@squiz/resource-browser@3.0.1-rc.0) (2024-11-25)
7
15
 
8
16
  **Note:** Version bump only for package @squiz/resource-browser
@@ -0,0 +1,2 @@
1
+ import React, { SVGAttributes } from 'react';
2
+ export declare const ImageInline: (props: SVGAttributes<SVGElement>) => React.JSX.Element;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ImageInline = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const ImageInline = (props) => {
9
+ return (react_1.default.createElement("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props },
10
+ react_1.default.createElement("path", { d: "M15.75 14.2499V3.74988C15.75 2.92488 15.075 2.24988 14.25 2.24988H3.75C2.925 2.24988 2.25 2.92488 2.25 3.74988V14.2499C2.25 15.0749 2.925 15.7499 3.75 15.7499H14.25C15.075 15.7499 15.75 15.0749 15.75 14.2499ZM6.675 10.4849L8.25 12.3824L10.575 9.38988C10.725 9.19488 11.025 9.19488 11.175 9.39738L13.8075 12.9074C13.995 13.1549 13.815 13.5074 13.5075 13.5074H4.515C4.2 13.5074 4.0275 13.1474 4.2225 12.8999L6.09 10.4999C6.2325 10.3049 6.5175 10.2974 6.675 10.4849Z", fill: "#707070" })));
11
+ };
12
+ exports.ImageInline = ImageInline;
@@ -0,0 +1,2 @@
1
+ import React, { SVGAttributes } from 'react';
2
+ export declare const LinkInline: (props: SVGAttributes<SVGElement>) => React.JSX.Element;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LinkInline = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const LinkInline = (props) => {
9
+ return (react_1.default.createElement("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props },
10
+ react_1.default.createElement("path", { d: "M12.75 5.50403H10.5C10.0875 5.50403 9.75 5.84153 9.75 6.25403C9.75 6.66653 10.0875 7.00403 10.5 7.00403H12.75C13.9875 7.00403 15 8.01653 15 9.25403C15 10.4915 13.9875 11.504 12.75 11.504H10.5C10.0875 11.504 9.75 11.8415 9.75 12.254C9.75 12.6665 10.0875 13.004 10.5 13.004H12.75C14.82 13.004 16.5 11.324 16.5 9.25403C16.5 7.18403 14.82 5.50403 12.75 5.50403ZM6 9.25403C6 9.66653 6.3375 10.004 6.75 10.004H11.25C11.6625 10.004 12 9.66653 12 9.25403C12 8.84153 11.6625 8.50403 11.25 8.50403H6.75C6.3375 8.50403 6 8.84153 6 9.25403ZM7.5 11.504H5.25C4.0125 11.504 3 10.4915 3 9.25403C3 8.01653 4.0125 7.00403 5.25 7.00403H7.5C7.9125 7.00403 8.25 6.66653 8.25 6.25403C8.25 5.84153 7.9125 5.50403 7.5 5.50403H5.25C3.18 5.50403 1.5 7.18403 1.5 9.25403C1.5 11.324 3.18 13.004 5.25 13.004H7.5C7.9125 13.004 8.25 12.6665 8.25 12.254C8.25 11.8415 7.9125 11.504 7.5 11.504Z", fill: "#707070" })));
11
+ };
12
+ exports.LinkInline = LinkInline;
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { ResourceBrowserInputProps } from '../ResourceBrowserInput/ResourceBrowserInput';
3
+ import { InlineType } from '../types';
3
4
  /**
4
5
  * This plugin component exsits to deal with React rules of Hooks stupidity.
5
6
  *
@@ -9,5 +10,7 @@ import { ResourceBrowserInputProps } from '../ResourceBrowserInput/ResourceBrows
9
10
  */
10
11
  export type PluginRenderType = ResourceBrowserInputProps & {
11
12
  render: boolean;
13
+ inline: boolean;
14
+ inlineType?: InlineType;
12
15
  };
13
- export declare const PluginRender: ({ render, ...props }: PluginRenderType) => React.JSX.Element;
16
+ export declare const PluginRender: ({ render, inline, inlineType, ...props }: PluginRenderType) => React.JSX.Element;
@@ -6,11 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PluginRender = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const ResourceBrowserInput_1 = require("../ResourceBrowserInput/ResourceBrowserInput");
9
+ const ResourceBrowserInlineButton_1 = require("../ResourceBrowserInlineButton/ResourceBrowserInlineButton");
9
10
  const AuthProvider_1 = require("../ResourceBrowserContext/AuthProvider");
10
- const PluginRender = ({ render, ...props }) => {
11
+ const PluginRender = ({ render, inline, inlineType, ...props }) => {
11
12
  if (render) {
12
- return (react_1.default.createElement(AuthProvider_1.AuthProvider, { authConfig: props.source },
13
- react_1.default.createElement(ResourceBrowserInput_1.ResourceBrowserInput, { ...props })));
13
+ return (react_1.default.createElement(AuthProvider_1.AuthProvider, { authConfig: props.source }, inline && inlineType ? (react_1.default.createElement(ResourceBrowserInlineButton_1.ResourceBrowserInlineButton, { inlineType: inlineType, ...props })) : (react_1.default.createElement(ResourceBrowserInput_1.ResourceBrowserInput, { ...props }))));
14
14
  }
15
15
  else {
16
16
  return react_1.default.createElement(react_1.default.Fragment, null);
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import { ResourceBrowserInputProps } from '../ResourceBrowserInput/ResourceBrowserInput';
3
+ import { InlineType } from '../types';
4
+ export type ResourceBrowserInlineButtonProps = ResourceBrowserInputProps & {
5
+ inlineType: InlineType;
6
+ };
7
+ export declare const ResourceBrowserInlineButton: ({ inlineType, modalTitle, allowedTypes, onChange, value, useResource, isDisabled, plugin, pluginMode, searchEnabled, source, sources, isLoading, error, setSource, isModalOpen, onModalStateChange, }: ResourceBrowserInlineButtonProps) => React.JSX.Element;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ResourceBrowserInlineButton = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const MainContainer_1 = __importDefault(require("../MainContainer/MainContainer"));
9
+ const resource_browser_ui_lib_1 = require("@squiz/resource-browser-ui-lib");
10
+ const ImageInline_1 = require("../Icons/ImageInline");
11
+ const LinkInline_1 = require("../Icons/LinkInline");
12
+ const ResourceBrowserInlineButton = ({ inlineType, modalTitle, allowedTypes, onChange, value, useResource, isDisabled, plugin, pluginMode, searchEnabled, source, sources, isLoading, error, setSource, isModalOpen, onModalStateChange, }) => {
13
+ const { data: resource, error: resourceError, isLoading: isResourceLoading } = useResource(value?.resourceId || null, source);
14
+ const isImagePicker = inlineType === 'image';
15
+ return (react_1.default.createElement("div", { className: "inline-launch-button" },
16
+ react_1.default.createElement(resource_browser_ui_lib_1.ModalTrigger, { overlayTriggerState: {
17
+ isOpen: isModalOpen,
18
+ onOpenChange: onModalStateChange,
19
+ }, showLabel: false, label: "", icon: isImagePicker ? react_1.default.createElement(ImageInline_1.ImageInline, { "aria-label": "change image" }) : react_1.default.createElement(LinkInline_1.LinkInline, { "aria-label": "change link" }), isDisabled: isDisabled, scope: "squiz-rb-scope", containerClasses: "inline-launch-button__button" }, (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 }))),
20
+ react_1.default.createElement("div", { className: "inline-launch-button__label" }, isImagePicker ? `Change image` : `Change link`)));
21
+ };
22
+ exports.ResourceBrowserInlineButton = ResourceBrowserInlineButton;
package/lib/index.css CHANGED
@@ -417,6 +417,9 @@
417
417
  .squiz-rb-scope .m-2:not(.squiz-rb-plugin *) {
418
418
  margin: 0.5rem;
419
419
  }
420
+ .squiz-rb-scope .m-20:not(.squiz-rb-plugin *) {
421
+ margin: 5rem;
422
+ }
420
423
  .squiz-rb-scope .m-3:not(.squiz-rb-plugin *) {
421
424
  margin: 0.75rem;
422
425
  }
@@ -512,6 +515,12 @@
512
515
  .squiz-rb-scope .inline-block:not(.squiz-rb-plugin *) {
513
516
  display: inline-block;
514
517
  }
518
+ .squiz-rb-scope .\!inline:not(.squiz-rb-plugin *) {
519
+ display: inline !important;
520
+ }
521
+ .squiz-rb-scope .inline:not(.squiz-rb-plugin *) {
522
+ display: inline;
523
+ }
515
524
  .squiz-rb-scope .flex:not(.squiz-rb-plugin *) {
516
525
  display: flex;
517
526
  }
@@ -1025,6 +1034,17 @@
1025
1034
  .squiz-rb-scope .filter:not(.squiz-rb-plugin *) {
1026
1035
  filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1027
1036
  }
1037
+ @keyframes skeleton-pulse {
1038
+ 0% {
1039
+ opacity: 1;
1040
+ }
1041
+ 50% {
1042
+ opacity: 0.45;
1043
+ }
1044
+ 100% {
1045
+ opacity: 1;
1046
+ }
1047
+ }
1028
1048
  .squiz-rb-scope .resource-picker:not(.squiz-rb-plugin *) {
1029
1049
  display: grid;
1030
1050
  grid-template-columns: 24px 1fr;
@@ -1062,6 +1082,44 @@
1062
1082
  --tw-bg-opacity: 1;
1063
1083
  background-color: rgb(247 247 247 / var(--tw-bg-opacity));
1064
1084
  }
1085
+ .squiz-rb-scope .inline-launch-button:not(.squiz-rb-plugin *) {
1086
+ display: inline-block;
1087
+ position: relative;
1088
+ }
1089
+ .squiz-rb-scope .inline-launch-button__button:not(.squiz-rb-plugin *) {
1090
+ all: unset;
1091
+ border: 2px solid;
1092
+ border-color: rgba(0, 0, 0, 0.16);
1093
+ border-radius: 8px;
1094
+ padding: 2px;
1095
+ cursor: pointer;
1096
+ }
1097
+ .squiz-rb-scope .inline-launch-button__label:not(.squiz-rb-plugin *) {
1098
+ display: none;
1099
+ position: absolute;
1100
+ box-sizing: border-box;
1101
+ top: calc(100% + 2px);
1102
+ white-space: nowrap;
1103
+ background: #2b2b2b;
1104
+ color: #fff;
1105
+ padding: 2px 8px;
1106
+ border-radius: 4px;
1107
+ font-size: 0.875rem;
1108
+ transform: translateX(calc(-50% + 13px));
1109
+ }
1110
+ .squiz-rb-scope .inline-launch-button:hover .inline-launch-button__label:not(.squiz-rb-plugin *),
1111
+ .squiz-rb-scope .inline-launch-button:focus-within .inline-launch-button__label:not(.squiz-rb-plugin *) {
1112
+ display: flex;
1113
+ }
1114
+ .squiz-rb-scope .inline-launch-button:hover .inline-launch-button__button:not(.squiz-rb-plugin *),
1115
+ .squiz-rb-scope .inline-launch-button:focus-within .inline-launch-button__button:not(.squiz-rb-plugin *) {
1116
+ border-color: #0774d2;
1117
+ background: #e6f1fa;
1118
+ }
1119
+ .squiz-rb-scope .inline-launch-button:hover .inline-launch-button__button svg path:not(.squiz-rb-plugin *),
1120
+ .squiz-rb-scope .inline-launch-button:focus-within .inline-launch-button__button svg path:not(.squiz-rb-plugin *) {
1121
+ fill: #044985;
1122
+ }
1065
1123
  .squiz-rb-scope .spinner:not(.squiz-rb-plugin *) {
1066
1124
  display: inline-block;
1067
1125
  border-radius: 9999px;
@@ -1268,3 +1326,7 @@
1268
1326
  height: calc(100vh - 9rem);
1269
1327
  }
1270
1328
  }
1329
+ /*!
1330
+ * @license
1331
+ * Copyright Squiz Australia Pty Ltd. All Rights Reserved.
1332
+ */
package/lib/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { ResourceBrowserContext, ResourceBrowserContextProvider } from './ResourceBrowserContext/ResourceBrowserContext';
3
- import { ResourceBrowserUnresolvedResource, ResourceBrowserResource } from './types';
3
+ import { InlineType, ResourceBrowserUnresolvedResource, ResourceBrowserResource } from './types';
4
4
  import { AuthProvider, useAuthContext, AuthContext } from './ResourceBrowserContext/AuthProvider';
5
5
  import BrowseToSource from './BrowseToSource/BrowseToSource';
6
6
  import SourceDropdown from './SourceDropdown/SourceDropdown';
@@ -12,6 +12,8 @@ export type ResourceBrowserProps = {
12
12
  allowedTypes?: string[];
13
13
  isDisabled?: boolean;
14
14
  value: ResourceBrowserUnresolvedResource | null;
15
+ inline?: boolean;
16
+ inlineType?: InlineType;
15
17
  onChange(resource: ResourceBrowserResource | null): void;
16
18
  onClear?(): void;
17
19
  };
package/lib/index.js CHANGED
@@ -48,7 +48,7 @@ const SourceDropdownContainer_1 = __importDefault(require("./SourceDropdownConta
48
48
  exports.SourceDropdownContainer = SourceDropdownContainer_1.default;
49
49
  __exportStar(require("./types"), exports);
50
50
  const ResourceBrowser = (props) => {
51
- const { value } = props;
51
+ const { value, inline, inlineType } = props;
52
52
  const [error, setError] = (0, react_1.useState)(null);
53
53
  const { onRequestSources, searchEnabled, plugins } = (0, react_1.useContext)(ResourceBrowserContext_1.ResourceBrowserContext);
54
54
  const [isModalOpen, setIsModalOpen] = (0, react_1.useState)(false);
@@ -105,7 +105,7 @@ const ResourceBrowser = (props) => {
105
105
  }, [sources, isModalOpen]);
106
106
  // Render a default "plugin" and one for each item in the plugins array. They are conditionally rendered based on what is selected
107
107
  return (react_1.default.createElement("div", { className: "squiz-rb-scope" },
108
- react_1.default.createElement(Plugin_1.PluginRender, { key: "default", render: plugin === null, ...props, source: source, sources: sources, setSource: handleSourceSelect, isLoading: isLoading, error: sourcesError || error, plugin: plugin, pluginMode: mode, searchEnabled: searchEnabled, useResource: () => {
108
+ react_1.default.createElement(Plugin_1.PluginRender, { key: "default", render: plugin === null, inline: !!inline, inlineType: inlineType, ...props, source: source, sources: sources, setSource: handleSourceSelect, isLoading: isLoading, error: sourcesError || error, plugin: plugin, pluginMode: mode, searchEnabled: searchEnabled, useResource: () => {
109
109
  return {
110
110
  data: null,
111
111
  error: null,
@@ -113,7 +113,7 @@ const ResourceBrowser = (props) => {
113
113
  };
114
114
  }, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange }),
115
115
  plugins.map((thisPlugin) => {
116
- return (react_1.default.createElement(Plugin_1.PluginRender, { key: thisPlugin.type, render: thisPlugin === plugin, ...props, source: source, sources: sources, setSource: handleSourceSelect, isLoading: isLoading, error: error, plugin: plugin, pluginMode: mode, searchEnabled: searchEnabled, useResource: thisPlugin.useResolveResource, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange }));
116
+ return (react_1.default.createElement(Plugin_1.PluginRender, { key: thisPlugin.type, render: thisPlugin === plugin, inline: !!inline, inlineType: inlineType, ...props, source: source, sources: sources, setSource: handleSourceSelect, isLoading: isLoading, error: error, plugin: plugin, pluginMode: mode, searchEnabled: searchEnabled, useResource: thisPlugin.useResolveResource, isModalOpen: isModalOpen, onModalStateChange: handleModalStateChange }));
117
117
  })));
118
118
  };
119
119
  exports.ResourceBrowser = ResourceBrowser;
package/lib/types.d.ts CHANGED
@@ -2,6 +2,7 @@ import React, { ReactElement } from 'react';
2
2
  import { SquizImageType } from '@squiz/dx-json-schema-lib';
3
3
  export type OnRequestSources = () => Promise<ResourceBrowserSource[]>;
4
4
  export type ResourceBrowserPluginType = 'dam' | 'matrix';
5
+ export type InlineType = 'image' | 'link';
5
6
  export type AuthenticationConfiguration = {
6
7
  authUrl: string;
7
8
  redirectUrl: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/resource-browser",
3
- "version": "3.0.1-rc.0",
3
+ "version": "3.0.1-rc.2",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "private": false,
@@ -13,6 +13,8 @@
13
13
  "compile:styles": "node build.js",
14
14
  "storybook": "storybook dev -p 6006",
15
15
  "storybook:build": "storybook build",
16
+ "storybook:cjs-fix": "storybook-filestore-compile-fix",
17
+ "storybook:upload": "storybook-filestore-uploader",
16
18
  "test": "npm run test:unit && npm run test:eslint",
17
19
  "test:unit": "jest",
18
20
  "test:eslint": "eslint .",
@@ -26,7 +28,7 @@
26
28
  "@react-types/shared": "^3.23.1",
27
29
  "@squiz/dx-json-schema-lib": "^1.67.0",
28
30
  "@squiz/generic-browser-lib": "1.67.2",
29
- "@squiz/resource-browser-ui-lib": "^1.0.0-rc.0",
31
+ "@squiz/resource-browser-ui-lib": "^1.0.0-rc.2",
30
32
  "clsx": "^2.1.0",
31
33
  "expiry-map": "^2.0.0",
32
34
  "p-memoize": "^4.0.4",
@@ -86,5 +88,5 @@
86
88
  "volta": {
87
89
  "node": "18.18.0"
88
90
  },
89
- "gitHead": "df5f8e74af27b274781237cc0c09b8f0521b66af"
91
+ "gitHead": "e9d97605da9b8a0e5595afdc033d1669e09af530"
90
92
  }
@@ -0,0 +1,12 @@
1
+ import React, { SVGAttributes } from 'react';
2
+
3
+ export const ImageInline = (props: SVGAttributes<SVGElement>) => {
4
+ return (
5
+ <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
6
+ <path
7
+ d="M15.75 14.2499V3.74988C15.75 2.92488 15.075 2.24988 14.25 2.24988H3.75C2.925 2.24988 2.25 2.92488 2.25 3.74988V14.2499C2.25 15.0749 2.925 15.7499 3.75 15.7499H14.25C15.075 15.7499 15.75 15.0749 15.75 14.2499ZM6.675 10.4849L8.25 12.3824L10.575 9.38988C10.725 9.19488 11.025 9.19488 11.175 9.39738L13.8075 12.9074C13.995 13.1549 13.815 13.5074 13.5075 13.5074H4.515C4.2 13.5074 4.0275 13.1474 4.2225 12.8999L6.09 10.4999C6.2325 10.3049 6.5175 10.2974 6.675 10.4849Z"
8
+ fill="#707070"
9
+ />
10
+ </svg>
11
+ );
12
+ };
@@ -0,0 +1,12 @@
1
+ import React, { SVGAttributes } from 'react';
2
+
3
+ export const LinkInline = (props: SVGAttributes<SVGElement>) => {
4
+ return (
5
+ <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
6
+ <path
7
+ d="M12.75 5.50403H10.5C10.0875 5.50403 9.75 5.84153 9.75 6.25403C9.75 6.66653 10.0875 7.00403 10.5 7.00403H12.75C13.9875 7.00403 15 8.01653 15 9.25403C15 10.4915 13.9875 11.504 12.75 11.504H10.5C10.0875 11.504 9.75 11.8415 9.75 12.254C9.75 12.6665 10.0875 13.004 10.5 13.004H12.75C14.82 13.004 16.5 11.324 16.5 9.25403C16.5 7.18403 14.82 5.50403 12.75 5.50403ZM6 9.25403C6 9.66653 6.3375 10.004 6.75 10.004H11.25C11.6625 10.004 12 9.66653 12 9.25403C12 8.84153 11.6625 8.50403 11.25 8.50403H6.75C6.3375 8.50403 6 8.84153 6 9.25403ZM7.5 11.504H5.25C4.0125 11.504 3 10.4915 3 9.25403C3 8.01653 4.0125 7.00403 5.25 7.00403H7.5C7.9125 7.00403 8.25 6.66653 8.25 6.25403C8.25 5.84153 7.9125 5.50403 7.5 5.50403H5.25C3.18 5.50403 1.5 7.18403 1.5 9.25403C1.5 11.324 3.18 13.004 5.25 13.004H7.5C7.9125 13.004 8.25 12.6665 8.25 12.254C8.25 11.8415 7.9125 11.504 7.5 11.504Z"
8
+ fill="#707070"
9
+ />
10
+ </svg>
11
+ );
12
+ };
@@ -3,12 +3,14 @@ import { render, waitFor } from '@testing-library/react';
3
3
 
4
4
  import { PluginRender } from './Plugin';
5
5
  import * as RBI from '../ResourceBrowserInput/ResourceBrowserInput';
6
+ import * as RBInlineButton from '../ResourceBrowserInlineButton/ResourceBrowserInlineButton';
6
7
  jest.spyOn(RBI, 'ResourceBrowserInput');
8
+ jest.spyOn(RBInlineButton, 'ResourceBrowserInlineButton');
7
9
 
8
10
  describe('Plugin', () => {
9
11
  it('Does not render ResourceBrowserInput if render is false', async () => {
10
12
  //@ts-ignore
11
- render(<PluginRender render={false} />);
13
+ render(<PluginRender render={false} inline={false} />);
12
14
 
13
15
  await waitFor(() => {
14
16
  expect(RBI.ResourceBrowserInput).not.toHaveBeenCalled();
@@ -39,10 +41,41 @@ describe('Plugin', () => {
39
41
  isModalOpen: false,
40
42
  onModalStateChange: () => {},
41
43
  };
42
- render(<PluginRender render={true} {...props} />);
44
+ render(<PluginRender render={true} inline={false} {...props} />);
43
45
 
44
46
  await waitFor(() => {
45
47
  expect(RBI.ResourceBrowserInput).toHaveBeenCalledWith(props, {});
46
48
  });
47
49
  });
50
+
51
+ it('Does render ResourceBrowserInlineButton if inline is true', async () => {
52
+ const props = {
53
+ modalTitle: 'Asset picker',
54
+ value: null,
55
+ onChange: jest.fn(),
56
+ onClear: jest.fn(),
57
+ useResource: () => {
58
+ return {
59
+ data: null,
60
+ error: null,
61
+ isLoading: false,
62
+ };
63
+ },
64
+ plugin: null,
65
+ pluginMode: null,
66
+ searchEnabled: false,
67
+ source: null,
68
+ sources: [],
69
+ isLoading: false,
70
+ error: null,
71
+ setSource: () => {},
72
+ isModalOpen: false,
73
+ onModalStateChange: () => {},
74
+ };
75
+ render(<PluginRender render={true} inline={true} inlineType="image" {...props} />);
76
+
77
+ await waitFor(() => {
78
+ expect(RBInlineButton.ResourceBrowserInlineButton).toHaveBeenCalledWith({ ...props, inlineType: 'image' }, {});
79
+ });
80
+ });
48
81
  });
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
2
  import { ResourceBrowserInput, ResourceBrowserInputProps } from '../ResourceBrowserInput/ResourceBrowserInput';
3
+ import { ResourceBrowserInlineButton } from '../ResourceBrowserInlineButton/ResourceBrowserInlineButton';
3
4
  import { AuthProvider } from '../ResourceBrowserContext/AuthProvider';
5
+ import { InlineType } from '../types';
4
6
 
5
7
  /**
6
8
  * This plugin component exsits to deal with React rules of Hooks stupidity.
@@ -11,12 +13,18 @@ import { AuthProvider } from '../ResourceBrowserContext/AuthProvider';
11
13
  */
12
14
  export type PluginRenderType = ResourceBrowserInputProps & {
13
15
  render: boolean;
16
+ inline: boolean;
17
+ inlineType?: InlineType;
14
18
  };
15
- export const PluginRender = ({ render, ...props }: PluginRenderType) => {
19
+ export const PluginRender = ({ render, inline, inlineType, ...props }: PluginRenderType) => {
16
20
  if (render) {
17
21
  return (
18
22
  <AuthProvider authConfig={props.source}>
19
- <ResourceBrowserInput {...props} />
23
+ {inline && inlineType ? (
24
+ <ResourceBrowserInlineButton inlineType={inlineType} {...props} />
25
+ ) : (
26
+ <ResourceBrowserInput {...props} />
27
+ )}
20
28
  </AuthProvider>
21
29
  );
22
30
  } else {
@@ -0,0 +1,44 @@
1
+ .inline-launch-button {
2
+ display: inline-block;
3
+ position: relative;
4
+
5
+ &__button {
6
+ all: unset;
7
+ border: 2px solid;
8
+ border-color: rgba(0, 0, 0, 0.16);
9
+ border-radius: 8px;
10
+ padding: 2px;
11
+ cursor: pointer;
12
+ }
13
+
14
+ &__label {
15
+ display: none;
16
+ position: absolute;
17
+ box-sizing: border-box;
18
+ top: calc(100% + 2px);
19
+ white-space: nowrap;
20
+ background: $sds-grey-900;
21
+ color: $sds-white;
22
+ padding: 2px 8px;
23
+ border-radius: 4px;
24
+ font-size: $sds-text-md-regular-font-size;
25
+ transform: translateX(
26
+ calc(-50% + 13px)
27
+ ); // === half of self to the left, minus half the width of the button (13px) which it stars offset to the right
28
+ }
29
+
30
+ &:hover,
31
+ &:focus-within {
32
+ .inline-launch-button__label {
33
+ display: flex;
34
+ }
35
+
36
+ .inline-launch-button__button {
37
+ border-color: $sds-blue-300;
38
+ background: $sds-blue-100;
39
+ svg path {
40
+ fill: $sds-blue-400;
41
+ }
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { render, waitFor, screen } from '@testing-library/react';
3
+
4
+ import { ResourceBrowserInlineButton } from './ResourceBrowserInlineButton';
5
+
6
+ describe('ResourceBrowserInlineButton', () => {
7
+ const defaultProps = {
8
+ useResource: () => {
9
+ return {
10
+ data: null,
11
+ error: null,
12
+ isLoading: false,
13
+ };
14
+ },
15
+ };
16
+ it('Render image icon if inline type is "image"', async () => {
17
+ //@ts-ignore
18
+ render(<ResourceBrowserInlineButton {...defaultProps} inlineType={'image'} />);
19
+
20
+ await waitFor(() => {
21
+ expect(screen.getByLabelText('change image')).toBeTruthy();
22
+ });
23
+ });
24
+
25
+ it('Render image icon if inline type is "link"', async () => {
26
+ //@ts-ignore
27
+ render(<ResourceBrowserInlineButton {...defaultProps} inlineType={'link'} />);
28
+
29
+ await waitFor(() => {
30
+ expect(screen.getByLabelText('change link')).toBeTruthy();
31
+ });
32
+ });
33
+ });
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { StoryFn, Meta } from '@storybook/react';
3
+
4
+ import { ResourceBrowserInlineButton } from './ResourceBrowserInlineButton';
5
+ export default {
6
+ title: 'Compact Button',
7
+ component: ResourceBrowserInlineButton,
8
+ } as Meta<typeof ResourceBrowserInlineButton>;
9
+
10
+ const Template: StoryFn<typeof ResourceBrowserInlineButton> = (props) => {
11
+ const useResource = () => {
12
+ return { data: {}, error: false, isLoading: props.isLoading };
13
+ };
14
+ return (
15
+ <div className="w-[400px] m-20">
16
+ {/* @ts-ignore */}
17
+ <ResourceBrowserInlineButton {...props} useResource={useResource} />
18
+ </div>
19
+ );
20
+ };
21
+
22
+ export const Image = Template.bind({});
23
+
24
+ Image.args = {
25
+ modalTitle: 'Choose image',
26
+ isLoading: false,
27
+ inlineType: 'image',
28
+ value: {
29
+ resourceId: '1f7a25b4-380f-4540-9555-8be2dcab4019',
30
+ sourceId: 'c90feac1-55f3-4e1f-9b56-c22829e3f510',
31
+ },
32
+ };
33
+
34
+ export const Link = Template.bind({});
35
+ Link.args = {
36
+ ...Image.args,
37
+ modalTitle: 'Choose asset',
38
+ inlineType: 'link',
39
+ };
@@ -0,0 +1,70 @@
1
+ import React from 'react';
2
+ import MainContainer from '../MainContainer/MainContainer';
3
+ import { ResourceBrowserInputProps } from '../ResourceBrowserInput/ResourceBrowserInput';
4
+ import { ModalTrigger } from '@squiz/resource-browser-ui-lib';
5
+ import { ImageInline } from '../Icons/ImageInline';
6
+ import { LinkInline } from '../Icons/LinkInline';
7
+ import { InlineType } from '../types';
8
+
9
+ export type ResourceBrowserInlineButtonProps = ResourceBrowserInputProps & {
10
+ inlineType: InlineType;
11
+ };
12
+
13
+ export const ResourceBrowserInlineButton = ({
14
+ inlineType,
15
+ modalTitle,
16
+ allowedTypes,
17
+ onChange,
18
+ value,
19
+ useResource,
20
+ isDisabled,
21
+ plugin,
22
+ pluginMode,
23
+ searchEnabled,
24
+ source,
25
+ sources,
26
+ isLoading,
27
+ error,
28
+ setSource,
29
+ isModalOpen,
30
+ onModalStateChange,
31
+ }: ResourceBrowserInlineButtonProps) => {
32
+ const { data: resource, error: resourceError, isLoading: isResourceLoading } = useResource(value?.resourceId || null, source);
33
+
34
+ const isImagePicker = inlineType === 'image';
35
+
36
+ return (
37
+ <div className="inline-launch-button">
38
+ <ModalTrigger
39
+ overlayTriggerState={{
40
+ isOpen: isModalOpen,
41
+ onOpenChange: onModalStateChange,
42
+ }}
43
+ showLabel={false}
44
+ label=""
45
+ icon={isImagePicker ? <ImageInline aria-label="change image" /> : <LinkInline aria-label="change link" />}
46
+ isDisabled={isDisabled}
47
+ scope="squiz-rb-scope"
48
+ containerClasses="inline-launch-button__button"
49
+ >
50
+ {(onClose, titleProps) => (
51
+ <MainContainer
52
+ selectedSource={source}
53
+ sources={sources}
54
+ preselectedResource={resource}
55
+ plugin={plugin}
56
+ pluginMode={pluginMode}
57
+ searchEnabled={searchEnabled}
58
+ title={modalTitle}
59
+ titleAriaProps={titleProps}
60
+ allowedTypes={allowedTypes}
61
+ onSourceSelect={setSource}
62
+ onClose={onClose}
63
+ onChange={onChange}
64
+ />
65
+ )}
66
+ </ModalTrigger>
67
+ <div className="inline-launch-button__label">{isImagePicker ? `Change image` : `Change link`}</div>
68
+ </div>
69
+ );
70
+ };
package/src/index.scss CHANGED
@@ -3,8 +3,11 @@
3
3
  @import 'tailwindcss/components';
4
4
  @import 'tailwindcss/utilities';
5
5
 
6
+ @import '@squiz/sds/lib/styles/_imports.scss';
7
+
6
8
  // Components
7
9
  @import './ResourcePicker/resource-picker';
10
+ @import './ResourceBrowserInlineButton/ResourceBrowserInlineButton';
8
11
  @import '@squiz/generic-browser-lib/src/Spinner/spinner';
9
12
  @import '@squiz/generic-browser-lib/src/Skeleton/skeleton';
10
13
 
@@ -60,6 +60,7 @@ Primary.args = {
60
60
  singleSource: false,
61
61
  headerPortal: false,
62
62
  searchEnabled: false,
63
+ inline: false,
63
64
  };
64
65
 
65
66
  export const Selected = Template.bind({});
@@ -88,3 +89,14 @@ SearchEnabled.args = {
88
89
  ...Primary.args,
89
90
  searchEnabled: true,
90
91
  };
92
+
93
+ export const InlineEditSelected = Template.bind({});
94
+ InlineEditSelected.args = {
95
+ ...Primary.args,
96
+ inline: true,
97
+ inlineType: 'image',
98
+ value: {
99
+ resourceId: '1f7a25b4-380f-4540-9555-8be2dcab4019',
100
+ sourceId: 'c90feac1-55f3-4e1f-9b56-c22829e3f510',
101
+ },
102
+ };
package/src/index.tsx CHANGED
@@ -2,6 +2,7 @@ import React, { useState, useContext, useEffect, useCallback } from 'react';
2
2
 
3
3
  import { ResourceBrowserContext, ResourceBrowserContextProvider } from './ResourceBrowserContext/ResourceBrowserContext';
4
4
  import {
5
+ InlineType,
5
6
  PluginLaunchMode,
6
7
  ResourceBrowserUnresolvedResource,
7
8
  ResourceBrowserResource,
@@ -33,12 +34,14 @@ export type ResourceBrowserProps = {
33
34
  allowedTypes?: string[];
34
35
  isDisabled?: boolean;
35
36
  value: ResourceBrowserUnresolvedResource | null;
37
+ inline?: boolean; // Will render open button only, no input / showing of existing selection
38
+ inlineType?: InlineType; // Type of inline button to show
36
39
  onChange(resource: ResourceBrowserResource | null): void;
37
40
  onClear?(): void;
38
41
  };
39
42
 
40
43
  export const ResourceBrowser = (props: ResourceBrowserProps) => {
41
- const { value } = props;
44
+ const { value, inline, inlineType } = props;
42
45
  const [error, setError] = useState<Error | null>(null);
43
46
  const { onRequestSources, searchEnabled, plugins } = useContext(ResourceBrowserContext);
44
47
 
@@ -112,6 +115,8 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
112
115
  <PluginRender
113
116
  key="default"
114
117
  render={plugin === null}
118
+ inline={!!inline}
119
+ inlineType={inlineType}
115
120
  {...props}
116
121
  source={source}
117
122
  sources={sources}
@@ -136,6 +141,8 @@ export const ResourceBrowser = (props: ResourceBrowserProps) => {
136
141
  <PluginRender
137
142
  key={thisPlugin.type}
138
143
  render={thisPlugin === plugin}
144
+ inline={!!inline}
145
+ inlineType={inlineType}
139
146
  {...props}
140
147
  source={source}
141
148
  sources={sources}
package/src/types.ts CHANGED
@@ -3,6 +3,7 @@ import { SquizImageType } from '@squiz/dx-json-schema-lib';
3
3
 
4
4
  export type OnRequestSources = () => Promise<ResourceBrowserSource[]>;
5
5
  export type ResourceBrowserPluginType = 'dam' | 'matrix';
6
+ export type InlineType = 'image' | 'link';
6
7
 
7
8
  export type AuthenticationConfiguration = {
8
9
  authUrl: string;