@squiz/generic-browser-lib 1.39.1-alpha.1 → 1.39.1-alpha.3
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/Icon.d.ts +3 -1
- package/lib/Icons/Icon.js +6 -1
- package/lib/Modal/ModalTrigger.d.ts +3 -1
- package/lib/Modal/ModalTrigger.js +4 -3
- package/lib/PreviewPanel/PreviewPanel.d.ts +2 -1
- package/lib/PreviewPanel/PreviewPanel.js +2 -2
- package/lib/ResourceItem/ResourceItem.d.ts +3 -1
- package/lib/ResourceItem/ResourceItem.js +4 -3
- package/lib/ResourceState/ResourceState.d.ts +3 -2
- package/lib/ResourceState/ResourceState.js +5 -4
- package/lib/Skeleton/List/SkeletonList.js +3 -2
- package/lib/Utils/uuid.d.ts +2 -0
- package/lib/Utils/uuid.js +7 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +2 -0
- package/package.json +2 -2
- package/src/Icons/Icon.spec.tsx +8 -0
- package/src/Icons/Icon.tsx +8 -0
- package/src/Modal/Modal.spec.tsx +20 -0
- package/src/Modal/ModalTrigger.tsx +9 -2
- package/src/PreviewPanel/PreviewPanel.tsx +5 -1
- package/src/ResourceItem/ResourceItem.tsx +6 -2
- package/src/ResourceState/ResourceState.tsx +15 -10
- package/src/Skeleton/List/SkeletonList.tsx +3 -2
- package/src/Utils/uuid.ts +7 -0
- package/src/index.ts +4 -0
package/lib/Icons/Icon.d.ts
CHANGED
@@ -25,6 +25,7 @@ export declare const iconSources: {
|
|
25
25
|
video_file: typeof import("./MatrixResources").Video;
|
26
26
|
word_doc: typeof import("./MatrixResources").Word;
|
27
27
|
};
|
28
|
+
component: never[];
|
28
29
|
};
|
29
30
|
export type ResourceSources = keyof typeof iconSources;
|
30
31
|
export type IconOptions = string;
|
@@ -43,8 +44,9 @@ export type IconOptions = string;
|
|
43
44
|
* @example
|
44
45
|
* <Icon resourceSource="matrix" icon="page" className="custom-class" />
|
45
46
|
*/
|
46
|
-
export declare function Icon({ resourceSource, icon, isDecorative, ...props }: {
|
47
|
+
export declare function Icon({ resourceSource, icon, componentIcon, isDecorative, ...props }: {
|
47
48
|
icon?: IconOptions;
|
48
49
|
resourceSource?: ResourceSources;
|
49
50
|
isDecorative?: boolean;
|
51
|
+
componentIcon?: React.ReactNode | undefined;
|
50
52
|
} & React.HTMLAttributes<HTMLElement> & React.SVGAttributes<SVGElement>): JSX.Element;
|
package/lib/Icons/Icon.js
CHANGED
@@ -10,6 +10,7 @@ const MatrixResourceMap_1 = __importDefault(require("./MatrixResources/MatrixRes
|
|
10
10
|
exports.iconSources = {
|
11
11
|
generic: GenericIconMap_1.default,
|
12
12
|
matrix: MatrixResourceMap_1.default,
|
13
|
+
component: [],
|
13
14
|
};
|
14
15
|
/**
|
15
16
|
* Renders an icon based on the resource source and the icon name
|
@@ -26,7 +27,11 @@ exports.iconSources = {
|
|
26
27
|
* @example
|
27
28
|
* <Icon resourceSource="matrix" icon="page" className="custom-class" />
|
28
29
|
*/
|
29
|
-
function Icon({ resourceSource = 'generic', icon, isDecorative = true, ...props }) {
|
30
|
+
function Icon({ resourceSource = 'generic', icon, componentIcon, isDecorative = true, ...props }) {
|
31
|
+
if (resourceSource === 'component' && componentIcon !== undefined) {
|
32
|
+
const className = props?.className || '';
|
33
|
+
return react_1.default.createElement("div", { className: className }, componentIcon);
|
34
|
+
}
|
30
35
|
const icons = exports.iconSources[resourceSource] || null;
|
31
36
|
// If the resource source is the current source and the icon is in the current source map, render the icon
|
32
37
|
if (icons && icon && icon in icons) {
|
@@ -1,7 +1,9 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { DOMAttributes, FocusableElement } from '@react-types/shared';
|
3
|
-
export declare function ModalTrigger({ label, showLabel, icon, isDisabled, children, ...props }: {
|
3
|
+
export declare function ModalTrigger({ label, labelClasses, showLabel, containerClasses, icon, isDisabled, children, ...props }: {
|
4
4
|
label: string;
|
5
|
+
labelClasses?: string;
|
6
|
+
containerClasses?: string;
|
5
7
|
showLabel?: boolean;
|
6
8
|
icon?: React.ReactNode;
|
7
9
|
isDisabled?: boolean;
|
@@ -10,7 +10,7 @@ const react_stately_1 = require("react-stately");
|
|
10
10
|
const clsx_1 = __importDefault(require("clsx"));
|
11
11
|
const Modal_1 = require("./Modal");
|
12
12
|
const ModalOpeningButton_1 = require("./ModalOpeningButton");
|
13
|
-
function ModalTrigger({ label, showLabel, icon, isDisabled, children, ...props }) {
|
13
|
+
function ModalTrigger({ label, labelClasses, showLabel, containerClasses, icon, isDisabled, children, ...props }) {
|
14
14
|
const state = (0, react_stately_1.useOverlayTriggerState)(props);
|
15
15
|
const { triggerProps, overlayProps } = (0, react_aria_1.useOverlayTrigger)({ type: 'dialog' }, state);
|
16
16
|
let ariaAttr = {};
|
@@ -18,9 +18,10 @@ function ModalTrigger({ label, showLabel, icon, isDisabled, children, ...props }
|
|
18
18
|
ariaAttr = { ...ariaAttr, 'aria-label': label };
|
19
19
|
}
|
20
20
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
21
|
-
react_1.default.createElement(ModalOpeningButton_1.ModalOpeningButton, { type: "button", ...triggerProps, ...ariaAttr, isDisabled: isDisabled, className: (0, clsx_1.default)(
|
21
|
+
react_1.default.createElement(ModalOpeningButton_1.ModalOpeningButton, { type: "button", ...triggerProps, ...ariaAttr, isDisabled: isDisabled, className: (0, clsx_1.default)(`${containerClasses ||
|
22
|
+
'flex p-1 px-1.5 rounded mr-auto text-blue-300 hover:bg-blue-100 focus:bg-blue-100 focus:outline-none'}`, isDisabled && 'hover:bg-transparent cursor-not-allowed text-gray-600') },
|
22
23
|
icon,
|
23
|
-
showLabel && react_1.default.createElement("span", { className:
|
24
|
+
showLabel && react_1.default.createElement("span", { className: `${labelClasses || 'ml-1 text-sm font-semibold leading-4'}` }, label)),
|
24
25
|
state.isOpen && (react_1.default.createElement(Modal_1.Modal, { isDismissable: true, state: state, overlayProps: overlayProps }, (titleProps) => children(state.close, titleProps)))));
|
25
26
|
}
|
26
27
|
exports.ModalTrigger = ModalTrigger;
|
@@ -9,5 +9,6 @@ export interface PreviewPanelProps {
|
|
9
9
|
onSelect: (resource: any) => void;
|
10
10
|
onClose: () => void;
|
11
11
|
ResourceComponent?: React.ElementType;
|
12
|
+
selectionCallback?: (param: any) => void;
|
12
13
|
}
|
13
|
-
export declare const PreviewPanel: ({ resource, previewModalOverlayProps, modalState, onSelect, onClose, ResourceComponent, }: PreviewPanelProps) => JSX.Element;
|
14
|
+
export declare const PreviewPanel: ({ resource, previewModalOverlayProps, modalState, onSelect, onClose, ResourceComponent, selectionCallback, }: PreviewPanelProps) => JSX.Element;
|
@@ -31,7 +31,7 @@ const react_1 = __importStar(require("react"));
|
|
31
31
|
const react_responsive_1 = require("react-responsive");
|
32
32
|
const Icon_1 = require("../Icons/Icon");
|
33
33
|
const PreviewModal_1 = __importDefault(require("./PreviewModal"));
|
34
|
-
const PreviewPanel = function ({ resource, previewModalOverlayProps, modalState, onSelect, onClose, ResourceComponent, }) {
|
34
|
+
const PreviewPanel = function ({ resource, previewModalOverlayProps, modalState, onSelect, onClose, ResourceComponent, selectionCallback, }) {
|
35
35
|
// Watch the media size to see if we are on mobile size
|
36
36
|
const isMobile = (0, react_responsive_1.useMediaQuery)({ query: '(max-width: 640px)' });
|
37
37
|
// If we are on mobile and the selected resource changes show the preview panel modal.
|
@@ -41,7 +41,7 @@ const PreviewPanel = function ({ resource, previewModalOverlayProps, modalState,
|
|
41
41
|
}
|
42
42
|
}, [resource, isMobile]);
|
43
43
|
const previewPanel = resource && (react_1.default.createElement(react_1.default.Fragment, null,
|
44
|
-
react_1.default.createElement("div", { className: "flex flex-col grow" }, ResourceComponent && react_1.default.createElement(ResourceComponent, { resource: resource })),
|
44
|
+
react_1.default.createElement("div", { className: "flex flex-col grow" }, ResourceComponent && react_1.default.createElement(ResourceComponent, { resource: resource, selectionCallback: selectionCallback })),
|
45
45
|
react_1.default.createElement("div", { className: "flex justify-end border-t border-gray-300" },
|
46
46
|
react_1.default.createElement("button", { type: "button", onClick: () => onSelect(resource), className: "rounded text-sm text-white bg-blue-300 py-2 px-2.5 m-5" }, "Select"))));
|
47
47
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { ReactElement } from 'react';
|
1
2
|
import { DOMAttributes, FocusableElement } from '@react-types/shared';
|
2
3
|
import { OverlayTriggerState } from 'react-stately';
|
3
4
|
import { ResourceSources } from '../Icons/Icon';
|
@@ -12,8 +13,9 @@ interface ResourceItem<T> {
|
|
12
13
|
onDrillDown?: (node: T) => void;
|
13
14
|
className: string;
|
14
15
|
allowedTypes?: string[] | undefined;
|
16
|
+
componentIcon?: ReactElement | undefined;
|
15
17
|
iconSource?: ResourceSources;
|
16
18
|
showChevron?: boolean;
|
17
19
|
}
|
18
|
-
declare const ResourceItem: <T>({ item, selected, label, type, childCount, previewModalState, onSelect, onDrillDown, className, allowedTypes, iconSource, showChevron, }: ResourceItem<T>) => JSX.Element;
|
20
|
+
declare const ResourceItem: <T>({ item, selected, label, type, childCount, previewModalState, onSelect, onDrillDown, className, allowedTypes, componentIcon, iconSource, showChevron, }: ResourceItem<T>) => JSX.Element;
|
19
21
|
export { ResourceItem };
|
@@ -8,15 +8,16 @@ const react_1 = __importDefault(require("react"));
|
|
8
8
|
const react_aria_1 = require("react-aria");
|
9
9
|
const ModalOpeningButton_1 = require("../Modal/ModalOpeningButton");
|
10
10
|
const Icon_1 = require("../Icons/Icon");
|
11
|
-
const
|
11
|
+
const uuid_1 = require("../Utils/uuid");
|
12
|
+
const ResourceItem = ({ item, selected, label, type, childCount, previewModalState, onSelect, onDrillDown, className, allowedTypes, componentIcon, iconSource = 'matrix', showChevron = false, }) => {
|
12
13
|
const { triggerProps, overlayProps } = (0, react_aria_1.useOverlayTrigger)({ type: 'dialog' }, previewModalState);
|
13
14
|
const isDisabled = allowedTypes !== undefined && !allowedTypes.includes(type);
|
14
15
|
const title = isDisabled ? "You can't select this item" : label;
|
15
|
-
return (react_1.default.createElement("li", { className: `flex items-stretch p-1 bg-white border-1 border-grey-200 min-h-[64px] ${className}
|
16
|
+
return (react_1.default.createElement("li", { className: `flex items-stretch p-1 bg-white border-1 border-grey-200 min-h-[64px] ${className}`, key: (0, uuid_1.uuid)() },
|
16
17
|
react_1.default.createElement(ModalOpeningButton_1.ModalOpeningButton, { type: "button", ...triggerProps, isDisabled: isDisabled, onPress: () => onSelect(item, overlayProps), "aria-label": childCount === undefined ? `Drill down to ${label} children` : '', className: `
|
17
18
|
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'}
|
18
19
|
`, title: title },
|
19
|
-
react_1.default.createElement(Icon_1.Icon, { icon: type, resourceSource: iconSource, "aria-label": type, className: `mr-4 shrink-0 ${isDisabled && 'opacity-40'}
|
20
|
+
react_1.default.createElement(Icon_1.Icon, { icon: type, resourceSource: iconSource, "aria-label": type, className: `mr-4 shrink-0 ${isDisabled && 'opacity-40'}`, componentIcon: componentIcon }),
|
20
21
|
react_1.default.createElement("span", { className: `relative flex items-center ${selected ? 'mr-8' : ''}` },
|
21
22
|
react_1.default.createElement("span", { className: "line-clamp-2 text-left break-word" }, label),
|
22
23
|
selected && react_1.default.createElement(Icon_1.Icon, { icon: 'selected', "aria-label": "selected", className: "absolute -right-8" })),
|
@@ -1,7 +1,8 @@
|
|
1
1
|
interface ResourceState {
|
2
2
|
state: 'error' | 'empty';
|
3
3
|
message?: string;
|
4
|
-
handleReload
|
4
|
+
handleReload?: () => void;
|
5
|
+
showButton?: boolean;
|
5
6
|
}
|
6
|
-
declare const ResourceState: ({ state, message, handleReload }: ResourceState) => JSX.Element;
|
7
|
+
declare const ResourceState: ({ state, message, handleReload, showButton }: ResourceState) => JSX.Element;
|
7
8
|
export { ResourceState };
|
@@ -6,12 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ResourceState = void 0;
|
7
7
|
const react_1 = __importDefault(require("react"));
|
8
8
|
const Icon_1 = require("../Icons/Icon");
|
9
|
-
const ResourceState = function ({ state, message, handleReload }) {
|
9
|
+
const ResourceState = function ({ state, message, handleReload, showButton = true }) {
|
10
|
+
const RetryButton = (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" },
|
11
|
+
react_1.default.createElement(Icon_1.Icon, { icon: state === 'empty' ? 'back' : 'retry', "aria-hidden": true }),
|
12
|
+
state === 'empty' ? 'Back to source list' : 'Try again'));
|
10
13
|
return (react_1.default.createElement("div", { className: "flex flex-col items-center rounded-lg py-8 bg-white h-204 gap-3" },
|
11
14
|
react_1.default.createElement(Icon_1.Icon, { icon: state, "aria-hidden": true }),
|
12
15
|
react_1.default.createElement("span", { className: "text-md text-gray-800 font-semibold leading-5" }, state === 'empty' ? 'There are no items to display' : message),
|
13
|
-
|
14
|
-
react_1.default.createElement(Icon_1.Icon, { icon: state === 'empty' ? 'back' : 'retry', "aria-hidden": true }),
|
15
|
-
state === 'empty' ? 'Back to source list' : 'Try again')));
|
16
|
+
showButton && RetryButton));
|
16
17
|
};
|
17
18
|
exports.ResourceState = ResourceState;
|
@@ -6,8 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.SkeletonList = void 0;
|
7
7
|
const react_1 = __importDefault(require("react"));
|
8
8
|
const SkeletonListItem_1 = require("../ListItem/SkeletonListItem");
|
9
|
+
const uuid_1 = require("../../Utils/uuid");
|
9
10
|
const clsx_1 = __importDefault(require("clsx"));
|
10
|
-
const SkeletonList = ({ itemCount = 8, className, ariaLabel }) => (react_1.default.createElement("ul", { className: (0, clsx_1.default)(`flex flex-col px-7 my-4 focus-visible:outline-0`, className), "aria-label": `${ariaLabel || 'Skeleton loader list'}` }, [...Array(itemCount)].map((_item
|
11
|
-
return react_1.default.createElement(SkeletonListItem_1.SkeletonListItem, { key:
|
11
|
+
const SkeletonList = ({ itemCount = 8, className, ariaLabel }) => (react_1.default.createElement("ul", { className: (0, clsx_1.default)(`flex flex-col px-7 my-4 focus-visible:outline-0`, className), "aria-label": `${ariaLabel || 'Skeleton loader list'}` }, [...Array(itemCount)].map((_item) => {
|
12
|
+
return react_1.default.createElement(SkeletonListItem_1.SkeletonListItem, { key: (0, uuid_1.uuid)() });
|
12
13
|
})));
|
13
14
|
exports.SkeletonList = SkeletonList;
|
@@ -0,0 +1,7 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.uuid = void 0;
|
4
|
+
function uuid() {
|
5
|
+
return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c) => (Number(c) ^ (window.crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (Number(c) / 4)))).toString(16));
|
6
|
+
}
|
7
|
+
exports.uuid = uuid;
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
"use strict";
|
2
|
+
/* istanbul ignore file */
|
2
3
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
4
|
if (k2 === undefined) k2 = k;
|
4
5
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
@@ -26,3 +27,4 @@ __exportStar(require("./PreviewPanel/PreviewPanel"), exports);
|
|
26
27
|
__exportStar(require("./PreviewPanel/PreviewPanelHOC"), exports);
|
27
28
|
__exportStar(require("./Hooks/useAsync"), exports);
|
28
29
|
__exportStar(require("./ResetButton/ResetButton"), exports);
|
30
|
+
__exportStar(require("./Utils/uuid"), exports);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@squiz/generic-browser-lib",
|
3
|
-
"version": "1.39.1-alpha.
|
3
|
+
"version": "1.39.1-alpha.3",
|
4
4
|
"description": "Package with reusable components used by resource/component browsers",
|
5
5
|
"main": "lib/index.js",
|
6
6
|
"types": "lib/index.d.ts",
|
@@ -72,5 +72,5 @@
|
|
72
72
|
"volta": {
|
73
73
|
"node": "18.15.0"
|
74
74
|
},
|
75
|
-
"gitHead": "
|
75
|
+
"gitHead": "5be5e45cfe2e31be51e8a09e2a1a934900ff29fe"
|
76
76
|
}
|
package/src/Icons/Icon.spec.tsx
CHANGED
@@ -59,4 +59,12 @@ describe('Icon', () => {
|
|
59
59
|
}
|
60
60
|
}
|
61
61
|
});
|
62
|
+
|
63
|
+
it('should render passed componentIcon and resourceSource is set to component', async () => {
|
64
|
+
const Component = () => <div data-testid="test-icon" />;
|
65
|
+
const { getByTestId } = render(
|
66
|
+
<Icon resourceSource="component" componentIcon={(<Component />) as React.ReactNode} />,
|
67
|
+
);
|
68
|
+
expect(getByTestId('test-icon')).toBeInTheDocument();
|
69
|
+
});
|
62
70
|
});
|
package/src/Icons/Icon.tsx
CHANGED
@@ -5,6 +5,7 @@ import MatrixResourceMap from './MatrixResources/MatrixResourceMap';
|
|
5
5
|
export const iconSources = {
|
6
6
|
generic: GenericIconMap,
|
7
7
|
matrix: MatrixResourceMap,
|
8
|
+
component: [],
|
8
9
|
};
|
9
10
|
|
10
11
|
// The resource sources options
|
@@ -29,14 +30,21 @@ export type IconOptions = string;
|
|
29
30
|
export function Icon({
|
30
31
|
resourceSource = 'generic',
|
31
32
|
icon,
|
33
|
+
componentIcon,
|
32
34
|
isDecorative = true,
|
33
35
|
...props
|
34
36
|
}: {
|
35
37
|
icon?: IconOptions;
|
36
38
|
resourceSource?: ResourceSources;
|
37
39
|
isDecorative?: boolean;
|
40
|
+
componentIcon?: React.ReactNode | undefined;
|
38
41
|
} & React.HTMLAttributes<HTMLElement> &
|
39
42
|
React.SVGAttributes<SVGElement>) {
|
43
|
+
if (resourceSource === 'component' && componentIcon !== undefined) {
|
44
|
+
const className = props?.className || '';
|
45
|
+
return <div className={className}>{componentIcon}</div>;
|
46
|
+
}
|
47
|
+
|
40
48
|
const icons = (iconSources[resourceSource] as any) || null;
|
41
49
|
|
42
50
|
// If the resource source is the current source and the icon is in the current source map, render the icon
|
package/src/Modal/Modal.spec.tsx
CHANGED
@@ -22,6 +22,26 @@ describe('Modal', () => {
|
|
22
22
|
</div>,
|
23
23
|
);
|
24
24
|
expect(screen.queryByTestId('modal')).toBeFalsy();
|
25
|
+
expect(screen.queryAllByLabelText('Open testing modal')).toHaveLength(0);
|
26
|
+
});
|
27
|
+
|
28
|
+
it('Modal should render label', async () => {
|
29
|
+
render(
|
30
|
+
<div>
|
31
|
+
<ModalTrigger label={'Open testing modal'} showLabel={false}>
|
32
|
+
{(onClose, titleProps) => (
|
33
|
+
<div data-testingid="modal">
|
34
|
+
<div {...titleProps}>Testing</div>
|
35
|
+
<button type="button" onClick={onClose}>
|
36
|
+
Close
|
37
|
+
</button>
|
38
|
+
</div>
|
39
|
+
)}
|
40
|
+
</ModalTrigger>
|
41
|
+
<div style={{ height: '150vh' }} />
|
42
|
+
</div>,
|
43
|
+
);
|
44
|
+
expect(screen.getByLabelText('Open testing modal')).toBeTruthy();
|
25
45
|
});
|
26
46
|
|
27
47
|
it('Modal opens when triggered', async () => {
|
@@ -9,13 +9,17 @@ import { ModalOpeningButton } from './ModalOpeningButton';
|
|
9
9
|
|
10
10
|
export function ModalTrigger({
|
11
11
|
label,
|
12
|
+
labelClasses,
|
12
13
|
showLabel,
|
14
|
+
containerClasses,
|
13
15
|
icon,
|
14
16
|
isDisabled,
|
15
17
|
children,
|
16
18
|
...props
|
17
19
|
}: {
|
18
20
|
label: string;
|
21
|
+
labelClasses?: string;
|
22
|
+
containerClasses?: string;
|
19
23
|
showLabel?: boolean;
|
20
24
|
icon?: React.ReactNode;
|
21
25
|
isDisabled?: boolean;
|
@@ -37,12 +41,15 @@ export function ModalTrigger({
|
|
37
41
|
{...ariaAttr}
|
38
42
|
isDisabled={isDisabled}
|
39
43
|
className={clsx(
|
40
|
-
|
44
|
+
`${
|
45
|
+
containerClasses ||
|
46
|
+
'flex p-1 px-1.5 rounded mr-auto text-blue-300 hover:bg-blue-100 focus:bg-blue-100 focus:outline-none'
|
47
|
+
}`,
|
41
48
|
isDisabled && 'hover:bg-transparent cursor-not-allowed text-gray-600',
|
42
49
|
)}
|
43
50
|
>
|
44
51
|
{icon}
|
45
|
-
{showLabel && <span className=
|
52
|
+
{showLabel && <span className={`${labelClasses || 'ml-1 text-sm font-semibold leading-4'}`}>{label}</span>}
|
46
53
|
</ModalOpeningButton>
|
47
54
|
{state.isOpen && (
|
48
55
|
<Modal isDismissable state={state} overlayProps={overlayProps}>
|
@@ -14,6 +14,7 @@ export interface PreviewPanelProps {
|
|
14
14
|
onSelect: (resource: any) => void;
|
15
15
|
onClose: () => void;
|
16
16
|
ResourceComponent?: React.ElementType;
|
17
|
+
selectionCallback?: (param: any) => void;
|
17
18
|
}
|
18
19
|
|
19
20
|
export const PreviewPanel = function ({
|
@@ -23,6 +24,7 @@ export const PreviewPanel = function ({
|
|
23
24
|
onSelect,
|
24
25
|
onClose,
|
25
26
|
ResourceComponent,
|
27
|
+
selectionCallback,
|
26
28
|
}: PreviewPanelProps) {
|
27
29
|
// Watch the media size to see if we are on mobile size
|
28
30
|
const isMobile = useMediaQuery({ query: '(max-width: 640px)' });
|
@@ -36,7 +38,9 @@ export const PreviewPanel = function ({
|
|
36
38
|
|
37
39
|
const previewPanel = resource && (
|
38
40
|
<>
|
39
|
-
<div className="flex flex-col grow">
|
41
|
+
<div className="flex flex-col grow">
|
42
|
+
{ResourceComponent && <ResourceComponent resource={resource} selectionCallback={selectionCallback} />}
|
43
|
+
</div>
|
40
44
|
<div className="flex justify-end border-t border-gray-300">
|
41
45
|
<button
|
42
46
|
type="button"
|
@@ -1,10 +1,11 @@
|
|
1
|
-
import React from 'react';
|
1
|
+
import React, { ReactElement } from 'react';
|
2
2
|
import { DOMAttributes, FocusableElement } from '@react-types/shared';
|
3
3
|
import { useOverlayTrigger } from 'react-aria';
|
4
4
|
import { OverlayTriggerState } from 'react-stately';
|
5
5
|
|
6
6
|
import { ModalOpeningButton } from '../Modal/ModalOpeningButton';
|
7
7
|
import { Icon, IconOptions, ResourceSources } from '../Icons/Icon';
|
8
|
+
import { uuid } from '../Utils/uuid';
|
8
9
|
|
9
10
|
interface ResourceItem<T> {
|
10
11
|
item: T;
|
@@ -17,6 +18,7 @@ interface ResourceItem<T> {
|
|
17
18
|
onDrillDown?: (node: T) => void;
|
18
19
|
className: string;
|
19
20
|
allowedTypes?: string[] | undefined;
|
21
|
+
componentIcon?: ReactElement | undefined;
|
20
22
|
iconSource?: ResourceSources;
|
21
23
|
showChevron?: boolean;
|
22
24
|
}
|
@@ -32,6 +34,7 @@ const ResourceItem = <T,>({
|
|
32
34
|
onDrillDown,
|
33
35
|
className,
|
34
36
|
allowedTypes,
|
37
|
+
componentIcon,
|
35
38
|
iconSource = 'matrix',
|
36
39
|
showChevron = false,
|
37
40
|
}: ResourceItem<T>) => {
|
@@ -40,7 +43,7 @@ const ResourceItem = <T,>({
|
|
40
43
|
const title = isDisabled ? "You can't select this item" : label;
|
41
44
|
|
42
45
|
return (
|
43
|
-
<li className={`flex items-stretch p-1 bg-white border-1 border-grey-200 min-h-[64px] ${className}`}>
|
46
|
+
<li className={`flex items-stretch p-1 bg-white border-1 border-grey-200 min-h-[64px] ${className}`} key={uuid()}>
|
44
47
|
<ModalOpeningButton
|
45
48
|
type="button"
|
46
49
|
{...triggerProps}
|
@@ -59,6 +62,7 @@ const ResourceItem = <T,>({
|
|
59
62
|
resourceSource={iconSource}
|
60
63
|
aria-label={type}
|
61
64
|
className={`mr-4 shrink-0 ${isDisabled && 'opacity-40'}`}
|
65
|
+
componentIcon={componentIcon}
|
62
66
|
/>
|
63
67
|
<span className={`relative flex items-center ${selected ? 'mr-8' : ''}`}>
|
64
68
|
<span className="line-clamp-2 text-left break-word">{label}</span>
|
@@ -4,10 +4,22 @@ import { Icon, IconOptions } from '../Icons/Icon';
|
|
4
4
|
interface ResourceState {
|
5
5
|
state: 'error' | 'empty';
|
6
6
|
message?: string;
|
7
|
-
handleReload
|
7
|
+
handleReload?: () => void;
|
8
|
+
showButton?: boolean;
|
8
9
|
}
|
9
10
|
|
10
|
-
const ResourceState = function ({ state, message, handleReload }: ResourceState) {
|
11
|
+
const ResourceState = function ({ state, message, handleReload, showButton = true }: ResourceState) {
|
12
|
+
const RetryButton = (
|
13
|
+
<button
|
14
|
+
type="button"
|
15
|
+
onClick={handleReload}
|
16
|
+
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"
|
17
|
+
>
|
18
|
+
<Icon icon={state === 'empty' ? 'back' : 'retry'} aria-hidden />
|
19
|
+
{state === 'empty' ? 'Back to source list' : 'Try again'}
|
20
|
+
</button>
|
21
|
+
);
|
22
|
+
|
11
23
|
return (
|
12
24
|
<div className="flex flex-col items-center rounded-lg py-8 bg-white h-204 gap-3">
|
13
25
|
<Icon icon={state as IconOptions} aria-hidden />
|
@@ -16,14 +28,7 @@ const ResourceState = function ({ state, message, handleReload }: ResourceState)
|
|
16
28
|
{state === 'empty' ? 'There are no items to display' : message}
|
17
29
|
</span>
|
18
30
|
{/* Retry button */}
|
19
|
-
|
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>
|
31
|
+
{showButton && RetryButton}
|
27
32
|
</div>
|
28
33
|
);
|
29
34
|
};
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { SkeletonListItem } from '../ListItem/SkeletonListItem';
|
3
|
+
import { uuid } from '../../Utils/uuid';
|
3
4
|
import clsx from 'clsx';
|
4
5
|
|
5
6
|
export type SkeletonListProps = {
|
@@ -13,8 +14,8 @@ export const SkeletonList = ({ itemCount = 8, className, ariaLabel }: SkeletonLi
|
|
13
14
|
className={clsx(`flex flex-col px-7 my-4 focus-visible:outline-0`, className)}
|
14
15
|
aria-label={`${ariaLabel || 'Skeleton loader list'}`}
|
15
16
|
>
|
16
|
-
{[...Array(itemCount)].map((_item
|
17
|
-
return <SkeletonListItem key={
|
17
|
+
{[...Array(itemCount)].map((_item) => {
|
18
|
+
return <SkeletonListItem key={uuid()} />;
|
18
19
|
})}
|
19
20
|
</ul>
|
20
21
|
);
|
package/src/index.ts
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
/* istanbul ignore file */
|
2
|
+
|
1
3
|
export * from './Icons/Icon';
|
2
4
|
|
3
5
|
export * from './Spinner/Spinner';
|
@@ -18,3 +20,5 @@ export * from './PreviewPanel/PreviewPanelHOC';
|
|
18
20
|
export * from './Hooks/useAsync';
|
19
21
|
|
20
22
|
export * from './ResetButton/ResetButton';
|
23
|
+
|
24
|
+
export * from './Utils/uuid';
|