@squiz/resource-browser 1.32.1-alpha.32 → 1.32.1-alpha.33
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/lib/Icons/Generics/Back.d.ts +4 -0
- package/lib/Icons/Generics/Back.js +12 -0
- package/lib/Icons/Generics/Empty.d.ts +4 -0
- package/lib/Icons/Generics/Empty.js +12 -0
- package/lib/Icons/Generics/GenericIconMap.d.ts +3 -1
- package/lib/Icons/Generics/GenericIconMap.js +2 -0
- package/lib/Icons/Generics/index.d.ts +2 -0
- package/lib/Icons/Generics/index.js +5 -1
- package/lib/Icons/Icon.d.ts +2 -0
- package/lib/ResourceItem/ResourceItem.d.ts +2 -2
- package/lib/ResourceItem/ResourceItem.js +5 -4
- package/lib/ResourceList/ResourceList.d.ts +3 -2
- package/lib/ResourceList/ResourceList.js +4 -3
- package/lib/ResourcePickerContainer/ResourcePickerContainer.js +2 -2
- package/lib/ResourceState/ResourceState.d.ts +7 -0
- package/lib/{ResourceError/ResourceError.js → ResourceState/ResourceState.js} +7 -7
- package/lib/SourceList/SourceList.d.ts +1 -3
- package/lib/SourceList/SourceList.js +4 -4
- package/lib/index.css +3 -3
- package/package.json +3 -3
- package/src/Icons/Generics/Back.tsx +13 -0
- package/src/Icons/Generics/Empty.tsx +13 -0
- package/src/Icons/Generics/GenericIconMap.ts +3 -1
- package/src/Icons/Generics/index.tsx +2 -0
- package/src/ResourceItem/ResourceItem.tsx +6 -4
- package/src/ResourceList/ResourceList.spec.tsx +6 -0
- package/src/ResourceList/ResourceList.tsx +12 -4
- package/src/ResourcePickerContainer/ResourcePickerContainer.spec.tsx +1 -1
- package/src/ResourcePickerContainer/ResourcePickerContainer.tsx +1 -2
- package/src/{ResourceError/ResourceError.spec.tsx → ResourceState/ResourceState.spec.tsx} +6 -5
- package/src/ResourceState/ResourceState.stories.tsx +24 -0
- package/src/ResourceState/ResourceState.tsx +31 -0
- package/src/SourceList/SourceList.spec.tsx +1 -40
- package/src/SourceList/SourceList.tsx +2 -9
- package/lib/ResourceError/ResourceError.d.ts +0 -6
- package/src/ResourceError/ResourceError.tsx +0 -27
@@ -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
|
+
const react_1 = __importDefault(require("react"));
|
7
|
+
function Back({ isDecorative, ...props }) {
|
8
|
+
return (react_1.default.createElement("svg", { width: "17", height: "16", viewBox: "0 0 17 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props },
|
9
|
+
react_1.default.createElement("path", { d: "M15.2912 7.00501H4.12124L9.00124 2.12501C9.39124 1.73501 9.39124 1.09501 9.00124 0.705006C8.61124 0.315006 7.98124 0.315006 7.59124 0.705006L1.00124 7.29501C0.61124 7.68501 0.61124 8.31501 1.00124 8.70501L7.59124 15.295C7.98124 15.685 8.61124 15.685 9.00124 15.295C9.39124 14.905 9.39124 14.275 9.00124 13.885L4.12124 9.00501H15.2912C15.8412 9.00501 16.2912 8.55501 16.2912 8.00501C16.2912 7.45501 15.8412 7.00501 15.2912 7.00501Z", fill: "#3D3D3D" }),
|
10
|
+
!isDecorative && react_1.default.createElement("title", null, "back icon")));
|
11
|
+
}
|
12
|
+
exports.default = Back;
|
@@ -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
|
+
const react_1 = __importDefault(require("react"));
|
7
|
+
function Empty({ isDecorative, ...props }) {
|
8
|
+
return (react_1.default.createElement("svg", { width: "42", height: "42", viewBox: "0 0 42 42", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props },
|
9
|
+
react_1.default.createElement("path", { d: "M1.3925 3.82749C0.612495 4.60749 0.612495 5.88749 1.3925 6.66749L4.5125 9.78749C2.0125 13.5475 0.752495 18.1875 1.3325 23.1675C2.37249 32.2475 9.75249 39.6275 18.8325 40.6675C23.8125 41.2475 28.4525 39.9875 32.2125 37.4875L35.3325 40.6075C36.1125 41.3875 37.3725 41.3875 38.1525 40.6075C38.9325 39.8275 38.9325 38.5675 38.1525 37.7875L4.21249 3.82749C3.4325 3.04749 2.17249 3.04749 1.3925 3.82749ZM21.1925 36.8075C12.3725 36.8075 5.19249 29.6275 5.19249 20.8075C5.19249 17.8475 6.01249 15.0875 7.43249 12.6875L29.3125 34.5675C26.9125 35.9875 24.1525 36.8075 21.1925 36.8075ZM13.0725 7.04749L10.1725 4.12749C13.3325 2.0275 17.1125 0.807495 21.1925 0.807495C32.2325 0.807495 41.1925 9.76749 41.1925 20.8075C41.1925 24.8875 39.9725 28.6675 37.8725 31.8275L34.9525 28.9075C36.3725 26.5275 37.1925 23.7675 37.1925 20.8075C37.1925 11.9875 30.0125 4.80749 21.1925 4.80749C18.2325 4.80749 15.4725 5.62749 13.0725 7.04749Z", fill: "#949494" }),
|
10
|
+
!isDecorative && react_1.default.createElement("title", null, "empty icon")));
|
11
|
+
}
|
12
|
+
exports.default = Empty;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { ArrowRight, ArrowDown, Selected, Root, ResourceSelect, Close, Error, Retry } from '.';
|
1
|
+
import { ArrowRight, ArrowDown, Selected, Root, ResourceSelect, Close, Error, Retry, Empty, Back } from '.';
|
2
2
|
declare const GenericIconMap: {
|
3
3
|
'arrow-right': typeof ArrowRight;
|
4
4
|
'arrow-down': typeof ArrowDown;
|
@@ -8,5 +8,7 @@ declare const GenericIconMap: {
|
|
8
8
|
close: typeof Close;
|
9
9
|
error: typeof Error;
|
10
10
|
retry: typeof Retry;
|
11
|
+
empty: typeof Empty;
|
12
|
+
back: typeof Back;
|
11
13
|
};
|
12
14
|
export default GenericIconMap;
|
@@ -6,3 +6,5 @@ export { default as ResourceSelect } from './ResourceSelect';
|
|
6
6
|
export { default as Close } from './Close';
|
7
7
|
export { default as Error } from './Error';
|
8
8
|
export { default as Retry } from './Retry';
|
9
|
+
export { default as Empty } from './Empty';
|
10
|
+
export { default as Back } from './Back';
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
exports.Retry = exports.Error = exports.Close = exports.ResourceSelect = exports.Root = exports.Selected = exports.ArrowDown = exports.ArrowRight = void 0;
|
6
|
+
exports.Back = exports.Empty = exports.Retry = exports.Error = exports.Close = exports.ResourceSelect = exports.Root = exports.Selected = exports.ArrowDown = exports.ArrowRight = void 0;
|
7
7
|
// Exports all icons from the Generics folder
|
8
8
|
var ArrowRight_1 = require("./ArrowRight");
|
9
9
|
Object.defineProperty(exports, "ArrowRight", { enumerable: true, get: function () { return __importDefault(ArrowRight_1).default; } });
|
@@ -21,3 +21,7 @@ var Error_1 = require("./Error");
|
|
21
21
|
Object.defineProperty(exports, "Error", { enumerable: true, get: function () { return __importDefault(Error_1).default; } });
|
22
22
|
var Retry_1 = require("./Retry");
|
23
23
|
Object.defineProperty(exports, "Retry", { enumerable: true, get: function () { return __importDefault(Retry_1).default; } });
|
24
|
+
var Empty_1 = require("./Empty");
|
25
|
+
Object.defineProperty(exports, "Empty", { enumerable: true, get: function () { return __importDefault(Empty_1).default; } });
|
26
|
+
var Back_1 = require("./Back");
|
27
|
+
Object.defineProperty(exports, "Back", { enumerable: true, get: function () { return __importDefault(Back_1).default; } });
|
package/lib/Icons/Icon.d.ts
CHANGED
@@ -9,6 +9,8 @@ export declare const iconSources: {
|
|
9
9
|
close: typeof import("./Generics").Close;
|
10
10
|
error: typeof import("./Generics").Error;
|
11
11
|
retry: typeof import("./Generics").Retry;
|
12
|
+
empty: typeof import("./Generics").Empty;
|
13
|
+
back: typeof import("./Generics").Back;
|
12
14
|
};
|
13
15
|
matrix: {
|
14
16
|
audio_file: typeof import("./MatrixResources").Audio;
|
@@ -5,10 +5,10 @@ interface ResourceItem<T> {
|
|
5
5
|
selected?: boolean;
|
6
6
|
label: string;
|
7
7
|
type: string;
|
8
|
-
childCount
|
8
|
+
childCount?: number;
|
9
9
|
previewModalState: OverlayTriggerState;
|
10
10
|
onSelect: (node: T, overlayProps: DOMAttributes<FocusableElement>) => void;
|
11
|
-
onDrillDown
|
11
|
+
onDrillDown?: (node: T) => void;
|
12
12
|
className: string;
|
13
13
|
allowedTypes?: string[] | undefined;
|
14
14
|
}
|
@@ -12,14 +12,15 @@ const ResourceItem = ({ item, selected, label, type, childCount, previewModalSta
|
|
12
12
|
const isDisabled = allowedTypes !== undefined && !allowedTypes.includes(type);
|
13
13
|
const title = isDisabled ? "You can't select this item" : label;
|
14
14
|
return (react_1.default.createElement("li", { className: `flex items-stretch p-1 bg-white border border-grey-200 min-h-[64px] ${className}` },
|
15
|
-
react_1.default.createElement(ModalOpeningButton_1.default, { type: "button", ...triggerProps, isDisabled: isDisabled, onPress: () => onSelect(item, overlayProps), className: `
|
16
|
-
relative grow flex items-center px-4 py-2 rounded outline-0 ${selected ? 'bg-blue-100 text-blue-400' : ''} ${childCount > 0 ? 'mr-2' : ''} ${isDisabled ? 'font-normal text-gray-600 cursor-not-allowed' : 'hover:bg-gray-50 focus:bg-gray-50'}
|
15
|
+
react_1.default.createElement(ModalOpeningButton_1.default, { type: "button", ...triggerProps, isDisabled: isDisabled, onPress: () => onSelect(item, overlayProps), "aria-label": childCount === undefined ? `Drill down to ${label} children` : '', className: `
|
16
|
+
relative grow flex items-center px-4 py-2 rounded outline-0 ${selected ? 'bg-blue-100 text-blue-400' : ''} ${childCount !== undefined && childCount > 0 ? 'mr-2' : ''} ${isDisabled ? 'font-normal text-gray-600 cursor-not-allowed' : 'hover:bg-gray-50 focus:bg-gray-50'}
|
17
17
|
`, title: title },
|
18
18
|
react_1.default.createElement(Icon_1.default, { icon: type, resourceSource: "matrix", "aria-label": type, className: `mr-4 shrink-0 ${isDisabled && 'opacity-40'}` }),
|
19
19
|
react_1.default.createElement("span", { className: `relative flex items-center ${selected ? 'mr-8' : ''}` },
|
20
20
|
react_1.default.createElement("span", { className: "line-clamp-2 text-left break-word" }, label),
|
21
|
-
selected && react_1.default.createElement(Icon_1.default, { icon: 'selected', "aria-label": "selected", className: "absolute -right-8" }))
|
22
|
-
|
21
|
+
selected && react_1.default.createElement(Icon_1.default, { icon: 'selected', "aria-label": "selected", className: "absolute -right-8" })),
|
22
|
+
childCount === undefined && react_1.default.createElement(Icon_1.default, { icon: 'arrow-right', className: "absolute right-5" })),
|
23
|
+
childCount !== undefined && childCount > 0 && onDrillDown && (react_1.default.createElement("button", { type: "button", "aria-label": `Drill down to ${label} children`, onClick: () => onDrillDown(item), className: `relative shrink-0 flex items-center p-4 rounded outline-0 before:w-px before:h-[calc(100%-0.75rem)] before:bg-gray-200 before:absolute before:top-1.5 before:-left-1 hover:bg-gray-50 focus:bg-gray-50` },
|
23
24
|
react_1.default.createElement("span", { className: "ml-auto flex items-center" },
|
24
25
|
react_1.default.createElement("span", { className: "truncate w-14 text-right", title: String(childCount) }, childCount),
|
25
26
|
react_1.default.createElement(Icon_1.default, { icon: 'arrow-right', className: "ml-1" }))))));
|
@@ -9,8 +9,9 @@ export interface ResourceListProps {
|
|
9
9
|
onResourceSelect: (resource: Resource, overlayProps: DOMAttributes<FocusableElement>) => void;
|
10
10
|
onResourceDrillDown: (resource: Resource) => void;
|
11
11
|
allowedTypes?: string[] | undefined;
|
12
|
-
|
12
|
+
handleReturnToRoot: () => void;
|
13
13
|
handleReload: () => void;
|
14
|
+
error: Error | null;
|
14
15
|
}
|
15
|
-
declare const ResourceList: ({ resources, selectedResource, previewModalState, isLoading, onResourceSelect, onResourceDrillDown, allowedTypes,
|
16
|
+
declare const ResourceList: ({ resources, selectedResource, previewModalState, isLoading, onResourceSelect, onResourceDrillDown, allowedTypes, handleReturnToRoot, handleReload, error, }: ResourceListProps) => JSX.Element;
|
16
17
|
export default ResourceList;
|
@@ -29,8 +29,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
const react_1 = __importStar(require("react"));
|
30
30
|
const ResourceItem_1 = __importDefault(require("../ResourceItem/ResourceItem"));
|
31
31
|
const SkeletonListItem_1 = require("../Skeleton/ListItem/SkeletonListItem");
|
32
|
-
const
|
33
|
-
const ResourceList = function ({ resources, selectedResource, previewModalState, isLoading, onResourceSelect, onResourceDrillDown, allowedTypes,
|
32
|
+
const ResourceState_1 = __importDefault(require("../ResourceState/ResourceState"));
|
33
|
+
const ResourceList = function ({ resources, selectedResource, previewModalState, isLoading, onResourceSelect, onResourceDrillDown, allowedTypes, handleReturnToRoot, handleReload, error, }) {
|
34
34
|
const listRef = (0, react_1.useRef)(null);
|
35
35
|
// When resources change, because we are on a new page, reset focus to the list
|
36
36
|
(0, react_1.useEffect)(() => {
|
@@ -44,7 +44,8 @@ const ResourceList = function ({ resources, selectedResource, previewModalState,
|
|
44
44
|
isLoading && (react_1.default.createElement(react_1.default.Fragment, null, [...Array(8)].map((_item, index) => {
|
45
45
|
return react_1.default.createElement(SkeletonListItem_1.SkeletonListItem, { key: index });
|
46
46
|
}))),
|
47
|
-
!isLoading && error && react_1.default.createElement(
|
47
|
+
!isLoading && error && react_1.default.createElement(ResourceState_1.default, { state: "error", message: error.message, handleReload: handleReload }),
|
48
|
+
!isLoading && !error && resources.length === 0 && (react_1.default.createElement(ResourceState_1.default, { state: "empty", handleReload: handleReturnToRoot })),
|
48
49
|
!isLoading &&
|
49
50
|
!error &&
|
50
51
|
resources.map((resource) => {
|
@@ -79,8 +79,8 @@ function ResourcePickerContainer({ title, titleAriaProps, allowedTypes, onReques
|
|
79
79
|
react_1.default.createElement("div", { className: "overflow-y-scroll flex-1 grow-[3] border-r border-gray-300" },
|
80
80
|
react_1.default.createElement("h3", { className: "sr-only" }, "Resource List"),
|
81
81
|
hierarchy.length > 0 && (react_1.default.createElement(ResourceBreadcrumb_1.default, { hierarchy: hierarchy, onBreadcrumbSelect: popUntil, onReturnToRoot: handleReturnToRoot })),
|
82
|
-
!source && (react_1.default.createElement(SourceList_1.default, { sources: sources, previewModalState: previewModalState, isLoading: isSourceLoading, onSourceSelect: handleSourceDrilldown,
|
83
|
-
source && (react_1.default.createElement(ResourceList_1.default, { previewModalState: previewModalState, resources: resources, selectedResource: selectedResource, isLoading: isResourcesLoading, onResourceSelect: handleResourceSelected, onResourceDrillDown: handleResourceDrillDown, allowedTypes: allowedTypes, handleReload: handleResourceReload, error: resourceError }))),
|
82
|
+
!source && (react_1.default.createElement(SourceList_1.default, { sources: sources, previewModalState: previewModalState, isLoading: isSourceLoading, onSourceSelect: handleSourceDrilldown, handleReload: handleSourceReload, error: sourceError })),
|
83
|
+
source && (react_1.default.createElement(ResourceList_1.default, { previewModalState: previewModalState, resources: resources, selectedResource: selectedResource, isLoading: isResourcesLoading, onResourceSelect: handleResourceSelected, onResourceDrillDown: handleResourceDrillDown, allowedTypes: allowedTypes, handleReturnToRoot: handleReturnToRoot, handleReload: handleResourceReload, error: resourceError }))),
|
84
84
|
react_1.default.createElement(PreviewPanel_1.default, { resource: selectedResource, modalState: previewModalState, previewModalOverlayProps: previewModalOverlayProps, allowedTypes: allowedTypes, onSelect: handleDetailSelect, onClose: handleDetailClose }))));
|
85
85
|
}
|
86
86
|
exports.default = ResourcePickerContainer;
|
@@ -5,12 +5,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
6
|
const react_1 = __importDefault(require("react"));
|
7
7
|
const Icon_1 = __importDefault(require("../Icons/Icon"));
|
8
|
-
const
|
8
|
+
const ResourceState = function ({ state, message, handleReload }) {
|
9
9
|
return (react_1.default.createElement("div", { className: "flex flex-col items-center rounded-lg py-8 bg-white h-204 gap-3" },
|
10
|
-
react_1.default.createElement(Icon_1.default, { icon:
|
11
|
-
react_1.default.createElement("span", { className: "text-md text-gray-800 font-semibold leading-5" },
|
12
|
-
react_1.default.createElement("button", { type: "button", onClick: handleReload, className: "flex flex-row items-center justify-center gap-3 bg-black bg-opacity-10
|
13
|
-
react_1.default.createElement(Icon_1.default, { icon: 'retry', "aria-hidden": true }),
|
14
|
-
|
10
|
+
react_1.default.createElement(Icon_1.default, { icon: state, "aria-hidden": true }),
|
11
|
+
react_1.default.createElement("span", { className: "text-md text-gray-800 font-semibold leading-5" }, state === 'empty' ? 'There are no items to display' : message),
|
12
|
+
react_1.default.createElement("button", { type: "button", onClick: handleReload, className: "flex flex-row items-center justify-center gap-3 bg-black bg-opacity-10 h-9 mt-3 rounded text-md font-bold text-gray-700 py-2 px-3" },
|
13
|
+
react_1.default.createElement(Icon_1.default, { icon: state === 'empty' ? 'back' : 'retry', "aria-hidden": true }),
|
14
|
+
state === 'empty' ? 'Back to source list' : 'Try again')));
|
15
15
|
};
|
16
|
-
exports.default =
|
16
|
+
exports.default = ResourceState;
|
@@ -6,10 +6,8 @@ export interface SourceListProps {
|
|
6
6
|
previewModalState: OverlayTriggerState;
|
7
7
|
isLoading: boolean;
|
8
8
|
onSourceSelect: (node: ScopedSource, overlayProps: DOMAttributes<FocusableElement>) => void;
|
9
|
-
onSourceDrillDown: (node: ScopedSource) => void;
|
10
|
-
allowedTypes?: string[] | undefined;
|
11
9
|
handleReload: () => void;
|
12
10
|
error: Error | null;
|
13
11
|
}
|
14
|
-
declare const SourceList: ({ sources, previewModalState, isLoading, onSourceSelect,
|
12
|
+
declare const SourceList: ({ sources, previewModalState, isLoading, onSourceSelect, handleReload, error, }: SourceListProps) => JSX.Element;
|
15
13
|
export default SourceList;
|
@@ -31,8 +31,8 @@ const ResourceItem_1 = __importDefault(require("../ResourceItem/ResourceItem"));
|
|
31
31
|
const SkeletonList_1 = require("../Skeleton/List/SkeletonList");
|
32
32
|
const clsx_1 = __importDefault(require("clsx"));
|
33
33
|
const useCategorisedSources_1 = require("../Hooks/useCategorisedSources");
|
34
|
-
const
|
35
|
-
const SourceList = function ({ sources, previewModalState, isLoading, onSourceSelect,
|
34
|
+
const ResourceState_1 = __importDefault(require("../ResourceState/ResourceState"));
|
35
|
+
const SourceList = function ({ sources, previewModalState, isLoading, onSourceSelect, handleReload, error, }) {
|
36
36
|
const categorisedSources = (0, useCategorisedSources_1.useCategorisedSources)(sources);
|
37
37
|
const listRef = (0, react_1.useRef)(null);
|
38
38
|
(0, react_1.useEffect)(() => {
|
@@ -48,7 +48,7 @@ const SourceList = function ({ sources, previewModalState, isLoading, onSourceSe
|
|
48
48
|
react_1.default.createElement(SkeletonList_1.SkeletonList, { itemCount: 3 })),
|
49
49
|
react_1.default.createElement("li", null,
|
50
50
|
react_1.default.createElement(SkeletonList_1.SkeletonList, { itemCount: 3 })))),
|
51
|
-
!isLoading && error && react_1.default.createElement(
|
51
|
+
!isLoading && error && react_1.default.createElement(ResourceState_1.default, { state: "error", message: error.message, handleReload: handleReload }),
|
52
52
|
!isLoading &&
|
53
53
|
!error &&
|
54
54
|
categorisedSources.map(({ key, label, sources }, index) => {
|
@@ -56,7 +56,7 @@ const SourceList = function ({ sources, previewModalState, isLoading, onSourceSe
|
|
56
56
|
react_1.default.createElement("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" },
|
57
57
|
react_1.default.createElement("span", { className: "z-10 bg-gray-100 px-2.5" }, label)),
|
58
58
|
sources.length > 0 && (react_1.default.createElement("ul", { "aria-label": `${label} nodes`, className: "flex flex-col" }, sources.map(({ source, resource }) => {
|
59
|
-
return (react_1.default.createElement(ResourceItem_1.default, { key: `${source.id}-${resource?.id}`, item: { source, resource }, label: resource?.name || source.name, type: resource?.type.code || 'folder',
|
59
|
+
return (react_1.default.createElement(ResourceItem_1.default, { key: `${source.id}-${resource?.id}`, item: { source, resource }, label: resource?.name || source.name, type: resource?.type.code || 'folder', previewModalState: previewModalState, onSelect: onSourceSelect, className: "mt-3 rounded-lg" }));
|
60
60
|
})))));
|
61
61
|
})));
|
62
62
|
};
|
package/lib/index.css
CHANGED
@@ -393,6 +393,9 @@
|
|
393
393
|
.squiz-rb-scope .right-4 {
|
394
394
|
right: 1rem;
|
395
395
|
}
|
396
|
+
.squiz-rb-scope .right-5 {
|
397
|
+
right: 1.25rem;
|
398
|
+
}
|
396
399
|
.squiz-rb-scope .top-2 {
|
397
400
|
top: 0.5rem;
|
398
401
|
}
|
@@ -610,9 +613,6 @@
|
|
610
613
|
.squiz-rb-scope .w-72 {
|
611
614
|
width: 18rem;
|
612
615
|
}
|
613
|
-
.squiz-rb-scope .w-\[119px\] {
|
614
|
-
width: 119px;
|
615
|
-
}
|
616
616
|
.squiz-rb-scope .w-\[20px\] {
|
617
617
|
width: 20px;
|
618
618
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@squiz/resource-browser",
|
3
|
-
"version": "1.32.1-alpha.
|
3
|
+
"version": "1.32.1-alpha.33",
|
4
4
|
"main": "lib/index.js",
|
5
5
|
"types": "lib/index.d.ts",
|
6
6
|
"scripts": {
|
@@ -15,7 +15,7 @@
|
|
15
15
|
},
|
16
16
|
"dependencies": {
|
17
17
|
"@mui/icons-material": "5.11.16",
|
18
|
-
"@squiz/dx-json-schema-lib": "1.32.1-alpha.
|
18
|
+
"@squiz/dx-json-schema-lib": "1.32.1-alpha.33",
|
19
19
|
"pretty-bytes": "5.6.0",
|
20
20
|
"react-aria": "3.23.1",
|
21
21
|
"react-responsive": "9.0.2",
|
@@ -73,5 +73,5 @@
|
|
73
73
|
"volta": {
|
74
74
|
"node": "18.15.0"
|
75
75
|
},
|
76
|
-
"gitHead": "
|
76
|
+
"gitHead": "614c4ec6f82220758521a2887b45094c36474aef"
|
77
77
|
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
|
3
|
+
export default function Back({ isDecorative, ...props }: { isDecorative: boolean } & React.SVGProps<SVGSVGElement>) {
|
4
|
+
return (
|
5
|
+
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
6
|
+
<path
|
7
|
+
d="M15.2912 7.00501H4.12124L9.00124 2.12501C9.39124 1.73501 9.39124 1.09501 9.00124 0.705006C8.61124 0.315006 7.98124 0.315006 7.59124 0.705006L1.00124 7.29501C0.61124 7.68501 0.61124 8.31501 1.00124 8.70501L7.59124 15.295C7.98124 15.685 8.61124 15.685 9.00124 15.295C9.39124 14.905 9.39124 14.275 9.00124 13.885L4.12124 9.00501H15.2912C15.8412 9.00501 16.2912 8.55501 16.2912 8.00501C16.2912 7.45501 15.8412 7.00501 15.2912 7.00501Z"
|
8
|
+
fill="#3D3D3D"
|
9
|
+
/>
|
10
|
+
{!isDecorative && <title>back icon</title>}
|
11
|
+
</svg>
|
12
|
+
);
|
13
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
|
3
|
+
export default function Empty({ isDecorative, ...props }: { isDecorative: boolean } & React.SVGProps<SVGSVGElement>) {
|
4
|
+
return (
|
5
|
+
<svg width="42" height="42" viewBox="0 0 42 42" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
6
|
+
<path
|
7
|
+
d="M1.3925 3.82749C0.612495 4.60749 0.612495 5.88749 1.3925 6.66749L4.5125 9.78749C2.0125 13.5475 0.752495 18.1875 1.3325 23.1675C2.37249 32.2475 9.75249 39.6275 18.8325 40.6675C23.8125 41.2475 28.4525 39.9875 32.2125 37.4875L35.3325 40.6075C36.1125 41.3875 37.3725 41.3875 38.1525 40.6075C38.9325 39.8275 38.9325 38.5675 38.1525 37.7875L4.21249 3.82749C3.4325 3.04749 2.17249 3.04749 1.3925 3.82749ZM21.1925 36.8075C12.3725 36.8075 5.19249 29.6275 5.19249 20.8075C5.19249 17.8475 6.01249 15.0875 7.43249 12.6875L29.3125 34.5675C26.9125 35.9875 24.1525 36.8075 21.1925 36.8075ZM13.0725 7.04749L10.1725 4.12749C13.3325 2.0275 17.1125 0.807495 21.1925 0.807495C32.2325 0.807495 41.1925 9.76749 41.1925 20.8075C41.1925 24.8875 39.9725 28.6675 37.8725 31.8275L34.9525 28.9075C36.3725 26.5275 37.1925 23.7675 37.1925 20.8075C37.1925 11.9875 30.0125 4.80749 21.1925 4.80749C18.2325 4.80749 15.4725 5.62749 13.0725 7.04749Z"
|
8
|
+
fill="#949494"
|
9
|
+
/>
|
10
|
+
{!isDecorative && <title>empty icon</title>}
|
11
|
+
</svg>
|
12
|
+
);
|
13
|
+
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { ArrowRight, ArrowDown, Selected, Root, ResourceSelect, Close, Error, Retry } from '.';
|
1
|
+
import { ArrowRight, ArrowDown, Selected, Root, ResourceSelect, Close, Error, Retry, Empty, Back } from '.';
|
2
2
|
|
3
3
|
// Define our map of matrix types to icons
|
4
4
|
const GenericIconMap = {
|
@@ -10,6 +10,8 @@ const GenericIconMap = {
|
|
10
10
|
close: Close,
|
11
11
|
error: Error,
|
12
12
|
retry: Retry,
|
13
|
+
empty: Empty,
|
14
|
+
back: Back,
|
13
15
|
};
|
14
16
|
|
15
17
|
// Export our map
|
@@ -7,3 +7,5 @@ export { default as ResourceSelect } from './ResourceSelect';
|
|
7
7
|
export { default as Close } from './Close';
|
8
8
|
export { default as Error } from './Error';
|
9
9
|
export { default as Retry } from './Retry';
|
10
|
+
export { default as Empty } from './Empty';
|
11
|
+
export { default as Back } from './Back';
|
@@ -11,10 +11,10 @@ interface ResourceItem<T> {
|
|
11
11
|
selected?: boolean;
|
12
12
|
label: string;
|
13
13
|
type: string;
|
14
|
-
childCount
|
14
|
+
childCount?: number;
|
15
15
|
previewModalState: OverlayTriggerState;
|
16
16
|
onSelect: (node: T, overlayProps: DOMAttributes<FocusableElement>) => void;
|
17
|
-
onDrillDown
|
17
|
+
onDrillDown?: (node: T) => void;
|
18
18
|
className: string;
|
19
19
|
allowedTypes?: string[] | undefined;
|
20
20
|
}
|
@@ -42,9 +42,10 @@ const ResourceItem = <T,>({
|
|
42
42
|
{...triggerProps}
|
43
43
|
isDisabled={isDisabled}
|
44
44
|
onPress={() => onSelect(item, overlayProps)}
|
45
|
+
aria-label={childCount === undefined ? `Drill down to ${label} children` : ''}
|
45
46
|
className={`
|
46
47
|
relative grow flex items-center px-4 py-2 rounded outline-0 ${selected ? 'bg-blue-100 text-blue-400' : ''} ${
|
47
|
-
childCount > 0 ? 'mr-2' : ''
|
48
|
+
childCount !== undefined && childCount > 0 ? 'mr-2' : ''
|
48
49
|
} ${isDisabled ? 'font-normal text-gray-600 cursor-not-allowed' : 'hover:bg-gray-50 focus:bg-gray-50'}
|
49
50
|
`}
|
50
51
|
title={title}
|
@@ -59,8 +60,9 @@ const ResourceItem = <T,>({
|
|
59
60
|
<span className="line-clamp-2 text-left break-word">{label}</span>
|
60
61
|
{selected && <Icon icon={'selected' as IconOptions} aria-label="selected" className="absolute -right-8" />}
|
61
62
|
</span>
|
63
|
+
{childCount === undefined && <Icon icon={'arrow-right' as IconOptions} className="absolute right-5" />}
|
62
64
|
</ModalOpeningButton>
|
63
|
-
{childCount > 0 && (
|
65
|
+
{childCount !== undefined && childCount > 0 && onDrillDown && (
|
64
66
|
<button
|
65
67
|
type="button"
|
66
68
|
aria-label={`Drill down to ${label} children`}
|
@@ -39,6 +39,7 @@ describe('ResourceList', () => {
|
|
39
39
|
onResourceDrillDown={() => {}}
|
40
40
|
error={null}
|
41
41
|
handleReload={reload}
|
42
|
+
handleReturnToRoot={reload}
|
42
43
|
/>
|
43
44
|
);
|
44
45
|
}}
|
@@ -65,6 +66,7 @@ describe('ResourceList', () => {
|
|
65
66
|
onResourceDrillDown={() => {}}
|
66
67
|
error={null}
|
67
68
|
handleReload={reload}
|
69
|
+
handleReturnToRoot={reload}
|
68
70
|
/>
|
69
71
|
);
|
70
72
|
}}
|
@@ -91,6 +93,7 @@ describe('ResourceList', () => {
|
|
91
93
|
onResourceDrillDown={() => {}}
|
92
94
|
error={null}
|
93
95
|
handleReload={reload}
|
96
|
+
handleReturnToRoot={reload}
|
94
97
|
/>
|
95
98
|
);
|
96
99
|
}}
|
@@ -122,6 +125,7 @@ describe('ResourceList', () => {
|
|
122
125
|
onResourceDrillDown={() => {}}
|
123
126
|
error={null}
|
124
127
|
handleReload={reload}
|
128
|
+
handleReturnToRoot={reload}
|
125
129
|
/>
|
126
130
|
);
|
127
131
|
}}
|
@@ -153,6 +157,7 @@ describe('ResourceList', () => {
|
|
153
157
|
onResourceDrillDown={onResourceDrillDown}
|
154
158
|
error={null}
|
155
159
|
handleReload={reload}
|
160
|
+
handleReturnToRoot={reload}
|
156
161
|
/>
|
157
162
|
);
|
158
163
|
}}
|
@@ -182,6 +187,7 @@ describe('ResourceList', () => {
|
|
182
187
|
onResourceDrillDown={() => {}}
|
183
188
|
error={new Error('This is a resource error!')}
|
184
189
|
handleReload={reload}
|
190
|
+
handleReturnToRoot={reload}
|
185
191
|
/>
|
186
192
|
);
|
187
193
|
}}
|
@@ -5,7 +5,7 @@ import { DOMAttributes, FocusableElement } from '@react-types/shared';
|
|
5
5
|
import ResourceItem from '../ResourceItem/ResourceItem';
|
6
6
|
import { Resource } from '../types';
|
7
7
|
import { SkeletonListItem } from '../Skeleton/ListItem/SkeletonListItem';
|
8
|
-
import
|
8
|
+
import ResourceState from '../ResourceState/ResourceState';
|
9
9
|
|
10
10
|
export interface ResourceListProps {
|
11
11
|
resources: Array<Resource>;
|
@@ -15,8 +15,9 @@ export interface ResourceListProps {
|
|
15
15
|
onResourceSelect: (resource: Resource, overlayProps: DOMAttributes<FocusableElement>) => void;
|
16
16
|
onResourceDrillDown: (resource: Resource) => void;
|
17
17
|
allowedTypes?: string[] | undefined;
|
18
|
-
|
18
|
+
handleReturnToRoot: () => void;
|
19
19
|
handleReload: () => void;
|
20
|
+
error: Error | null;
|
20
21
|
}
|
21
22
|
|
22
23
|
const ResourceList = function ({
|
@@ -27,8 +28,9 @@ const ResourceList = function ({
|
|
27
28
|
onResourceSelect,
|
28
29
|
onResourceDrillDown,
|
29
30
|
allowedTypes,
|
30
|
-
|
31
|
+
handleReturnToRoot,
|
31
32
|
handleReload,
|
33
|
+
error,
|
32
34
|
}: ResourceListProps) {
|
33
35
|
const listRef = useRef<HTMLUListElement>(null);
|
34
36
|
|
@@ -56,7 +58,13 @@ const ResourceList = function ({
|
|
56
58
|
</>
|
57
59
|
)}
|
58
60
|
|
59
|
-
{
|
61
|
+
{/* Error State */}
|
62
|
+
{!isLoading && error && <ResourceState state="error" message={error.message} handleReload={handleReload} />}
|
63
|
+
|
64
|
+
{/* Empty State */}
|
65
|
+
{!isLoading && !error && resources.length === 0 && (
|
66
|
+
<ResourceState state="empty" handleReload={handleReturnToRoot} />
|
67
|
+
)}
|
60
68
|
|
61
69
|
{!isLoading &&
|
62
70
|
!error &&
|
@@ -324,7 +324,7 @@ describe('ResourcePickerContainer', () => {
|
|
324
324
|
});
|
325
325
|
|
326
326
|
const user = userEvent.setup();
|
327
|
-
user.click(screen.getByRole('button', { name: '
|
327
|
+
user.click(screen.getByRole('button', { name: 'Drill down to Test Website children' }));
|
328
328
|
|
329
329
|
expect(mockModalState).toEqual(
|
330
330
|
expect.objectContaining({
|
@@ -136,8 +136,6 @@ function ResourcePickerContainer({
|
|
136
136
|
previewModalState={previewModalState}
|
137
137
|
isLoading={isSourceLoading}
|
138
138
|
onSourceSelect={handleSourceDrilldown}
|
139
|
-
onSourceDrillDown={handleSourceDrilldown}
|
140
|
-
allowedTypes={allowedTypes}
|
141
139
|
handleReload={handleSourceReload}
|
142
140
|
error={sourceError}
|
143
141
|
/>
|
@@ -151,6 +149,7 @@ function ResourcePickerContainer({
|
|
151
149
|
onResourceSelect={handleResourceSelected}
|
152
150
|
onResourceDrillDown={handleResourceDrillDown}
|
153
151
|
allowedTypes={allowedTypes}
|
152
|
+
handleReturnToRoot={handleReturnToRoot}
|
154
153
|
handleReload={handleResourceReload}
|
155
154
|
error={resourceError}
|
156
155
|
/>
|
@@ -1,22 +1,23 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { render, fireEvent } from '@testing-library/react';
|
3
|
-
import
|
3
|
+
import ResourceState from './ResourceState';
|
4
4
|
|
5
5
|
const defaultProps: any = {
|
6
|
-
|
6
|
+
state: 'error',
|
7
|
+
message: 'This is a test error!',
|
7
8
|
handleReload: jest.fn(),
|
8
9
|
};
|
9
10
|
|
10
11
|
describe('ResourceError', () => {
|
11
12
|
it('should render the component with the correct error message', () => {
|
12
|
-
const { getByText } = render(<
|
13
|
-
const errorMessage = getByText(defaultProps.
|
13
|
+
const { getByText } = render(<ResourceState {...defaultProps} />);
|
14
|
+
const errorMessage = getByText(defaultProps.message);
|
14
15
|
|
15
16
|
expect(errorMessage).toBeInTheDocument();
|
16
17
|
});
|
17
18
|
|
18
19
|
it('should call the reload function when the button is pressed', () => {
|
19
|
-
const { getByRole } = render(<
|
20
|
+
const { getByRole } = render(<ResourceState {...defaultProps} />);
|
20
21
|
const buttonElement = getByRole('button');
|
21
22
|
fireEvent.click(buttonElement);
|
22
23
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { StoryFn, Meta } from '@storybook/react';
|
3
|
+
|
4
|
+
import ResourceState from './ResourceState';
|
5
|
+
|
6
|
+
export default {
|
7
|
+
title: 'Resource State',
|
8
|
+
component: ResourceState,
|
9
|
+
} as Meta<typeof ResourceState>;
|
10
|
+
|
11
|
+
const Template: StoryFn<typeof ResourceState> = ({ state, message }) => (
|
12
|
+
<ResourceState state={state} message={message} handleReload={() => alert('Resource browser reload')} />
|
13
|
+
);
|
14
|
+
|
15
|
+
export const Error = Template.bind({});
|
16
|
+
Error.args = {
|
17
|
+
state: 'error',
|
18
|
+
message: 'This is a resource browser error!',
|
19
|
+
};
|
20
|
+
|
21
|
+
export const Empty = Template.bind({});
|
22
|
+
Empty.args = {
|
23
|
+
state: 'empty',
|
24
|
+
};
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import Icon, { IconOptions } from '../Icons/Icon';
|
3
|
+
|
4
|
+
interface ResourceState {
|
5
|
+
state: 'error' | 'empty';
|
6
|
+
message?: string;
|
7
|
+
handleReload: () => void;
|
8
|
+
}
|
9
|
+
|
10
|
+
const ResourceState = function ({ state, message, handleReload }: ResourceState) {
|
11
|
+
return (
|
12
|
+
<div className="flex flex-col items-center rounded-lg py-8 bg-white h-204 gap-3">
|
13
|
+
<Icon icon={state as IconOptions} aria-hidden />
|
14
|
+
{/* Message */}
|
15
|
+
<span className="text-md text-gray-800 font-semibold leading-5">
|
16
|
+
{state === 'empty' ? 'There are no items to display' : message}
|
17
|
+
</span>
|
18
|
+
{/* Retry button */}
|
19
|
+
<button
|
20
|
+
type="button"
|
21
|
+
onClick={handleReload}
|
22
|
+
className="flex flex-row items-center justify-center gap-3 bg-black bg-opacity-10 h-9 mt-3 rounded text-md font-bold text-gray-700 py-2 px-3"
|
23
|
+
>
|
24
|
+
<Icon icon={state === 'empty' ? 'back' : 'retry'} aria-hidden />
|
25
|
+
{state === 'empty' ? 'Back to source list' : 'Try again'}
|
26
|
+
</button>
|
27
|
+
</div>
|
28
|
+
);
|
29
|
+
};
|
30
|
+
|
31
|
+
export default ResourceState;
|
@@ -80,7 +80,6 @@ describe('SourceList', () => {
|
|
80
80
|
previewModalState={previewModalState}
|
81
81
|
isLoading={true}
|
82
82
|
onSourceSelect={() => {}}
|
83
|
-
onSourceDrillDown={() => {}}
|
84
83
|
error={null}
|
85
84
|
handleReload={reload}
|
86
85
|
/>
|
@@ -106,7 +105,6 @@ describe('SourceList', () => {
|
|
106
105
|
previewModalState={previewModalState}
|
107
106
|
isLoading={false}
|
108
107
|
onSourceSelect={() => {}}
|
109
|
-
onSourceDrillDown={() => {}}
|
110
108
|
error={null}
|
111
109
|
handleReload={reload}
|
112
110
|
/>
|
@@ -133,7 +131,6 @@ describe('SourceList', () => {
|
|
133
131
|
previewModalState={previewModalState}
|
134
132
|
isLoading={false}
|
135
133
|
onSourceSelect={() => {}}
|
136
|
-
onSourceDrillDown={() => {}}
|
137
134
|
error={null}
|
138
135
|
handleReload={reload}
|
139
136
|
/>
|
@@ -159,7 +156,6 @@ describe('SourceList', () => {
|
|
159
156
|
previewModalState={previewModalState}
|
160
157
|
isLoading={false}
|
161
158
|
onSourceSelect={() => {}}
|
162
|
-
onSourceDrillDown={() => {}}
|
163
159
|
error={null}
|
164
160
|
handleReload={reload}
|
165
161
|
/>
|
@@ -192,7 +188,6 @@ describe('SourceList', () => {
|
|
192
188
|
previewModalState={previewModalState}
|
193
189
|
isLoading={false}
|
194
190
|
onSourceSelect={onSourceSelect}
|
195
|
-
onSourceDrillDown={() => {}}
|
196
191
|
error={null}
|
197
192
|
handleReload={reload}
|
198
193
|
/>
|
@@ -202,7 +197,7 @@ describe('SourceList', () => {
|
|
202
197
|
);
|
203
198
|
|
204
199
|
const user = userEvent.setup();
|
205
|
-
const itemButton = screen.getByRole('button', { name: '
|
200
|
+
const itemButton = screen.getByRole('button', { name: 'Drill down to Node 1 children' });
|
206
201
|
user.click(itemButton);
|
207
202
|
|
208
203
|
await waitFor(() => {
|
@@ -217,39 +212,6 @@ describe('SourceList', () => {
|
|
217
212
|
});
|
218
213
|
});
|
219
214
|
|
220
|
-
it('Clicking node child count triggers correct onSourceDrillDown', async () => {
|
221
|
-
const onSourceDrillDown = jest.fn();
|
222
|
-
const reload = jest.fn();
|
223
|
-
|
224
|
-
render(
|
225
|
-
<SourceListTestWrapper
|
226
|
-
constructFunction={(previewModalState) => {
|
227
|
-
return (
|
228
|
-
<SourceList
|
229
|
-
sources={sources}
|
230
|
-
previewModalState={previewModalState}
|
231
|
-
isLoading={false}
|
232
|
-
onSourceSelect={() => {}}
|
233
|
-
onSourceDrillDown={onSourceDrillDown}
|
234
|
-
error={null}
|
235
|
-
handleReload={reload}
|
236
|
-
/>
|
237
|
-
);
|
238
|
-
}}
|
239
|
-
/>,
|
240
|
-
);
|
241
|
-
|
242
|
-
const user = userEvent.setup();
|
243
|
-
user.click(screen.getByRole('button', { name: 'Drill down to Node 1 children' }));
|
244
|
-
|
245
|
-
await waitFor(() => {
|
246
|
-
expect(onSourceDrillDown).toHaveBeenCalledWith({
|
247
|
-
source: sources[0],
|
248
|
-
resource: sources[0].nodes[0],
|
249
|
-
});
|
250
|
-
});
|
251
|
-
});
|
252
|
-
|
253
215
|
it('Renders error state when an error occurs loading source list', async () => {
|
254
216
|
const reload = jest.fn();
|
255
217
|
|
@@ -262,7 +224,6 @@ describe('SourceList', () => {
|
|
262
224
|
previewModalState={previewModalState}
|
263
225
|
isLoading={false}
|
264
226
|
onSourceSelect={() => {}}
|
265
|
-
onSourceDrillDown={() => {}}
|
266
227
|
error={new Error('Source list error!')}
|
267
228
|
handleReload={reload}
|
268
229
|
/>
|
@@ -7,15 +7,13 @@ import { Source, ScopedSource } from '../types';
|
|
7
7
|
import { SkeletonList } from '../Skeleton/List/SkeletonList';
|
8
8
|
import clsx from 'clsx';
|
9
9
|
import { useCategorisedSources } from '../Hooks/useCategorisedSources';
|
10
|
-
import
|
10
|
+
import ResourceState from '../ResourceState/ResourceState';
|
11
11
|
|
12
12
|
export interface SourceListProps {
|
13
13
|
sources: Source[];
|
14
14
|
previewModalState: OverlayTriggerState;
|
15
15
|
isLoading: boolean;
|
16
16
|
onSourceSelect: (node: ScopedSource, overlayProps: DOMAttributes<FocusableElement>) => void;
|
17
|
-
onSourceDrillDown: (node: ScopedSource) => void;
|
18
|
-
allowedTypes?: string[] | undefined;
|
19
17
|
handleReload: () => void;
|
20
18
|
error: Error | null;
|
21
19
|
}
|
@@ -25,8 +23,6 @@ const SourceList = function ({
|
|
25
23
|
previewModalState,
|
26
24
|
isLoading,
|
27
25
|
onSourceSelect,
|
28
|
-
onSourceDrillDown,
|
29
|
-
allowedTypes,
|
30
26
|
handleReload,
|
31
27
|
error,
|
32
28
|
}: SourceListProps) {
|
@@ -59,7 +55,7 @@ const SourceList = function ({
|
|
59
55
|
</>
|
60
56
|
)}
|
61
57
|
|
62
|
-
{!isLoading && error && <
|
58
|
+
{!isLoading && error && <ResourceState state="error" message={error.message} handleReload={handleReload} />}
|
63
59
|
|
64
60
|
{!isLoading &&
|
65
61
|
!error &&
|
@@ -78,12 +74,9 @@ const SourceList = function ({
|
|
78
74
|
item={{ source, resource }}
|
79
75
|
label={resource?.name || source.name}
|
80
76
|
type={resource?.type.code || 'folder'}
|
81
|
-
childCount={resource?.childCount || 0}
|
82
77
|
previewModalState={previewModalState}
|
83
78
|
onSelect={onSourceSelect}
|
84
|
-
onDrillDown={onSourceDrillDown}
|
85
79
|
className="mt-3 rounded-lg"
|
86
|
-
allowedTypes={allowedTypes}
|
87
80
|
/>
|
88
81
|
);
|
89
82
|
})}
|
@@ -1,27 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import Icon, { IconOptions } from '../Icons/Icon';
|
3
|
-
|
4
|
-
interface ResourceError {
|
5
|
-
errorMessage: string;
|
6
|
-
handleReload: () => void;
|
7
|
-
}
|
8
|
-
|
9
|
-
const ResourceError = function ({ errorMessage, handleReload }: ResourceError) {
|
10
|
-
return (
|
11
|
-
<div className="flex flex-col items-center rounded-lg py-8 bg-white h-204 gap-3">
|
12
|
-
<Icon icon={'error' as IconOptions} aria-hidden />
|
13
|
-
{/* Error message */}
|
14
|
-
<span className="text-md text-gray-800 font-semibold leading-5">{errorMessage}</span>
|
15
|
-
{/* Retry button */}
|
16
|
-
<button
|
17
|
-
type="button"
|
18
|
-
onClick={handleReload}
|
19
|
-
className="flex flex-row items-center justify-center gap-3 bg-black bg-opacity-10 w-[119px] h-9 mt-3 rounded text-md font-bold text-gray-700"
|
20
|
-
>
|
21
|
-
<Icon icon={'retry' as IconOptions} aria-hidden /> Try again
|
22
|
-
</button>
|
23
|
-
</div>
|
24
|
-
);
|
25
|
-
};
|
26
|
-
|
27
|
-
export default ResourceError;
|