@squiz/resource-browser 1.32.1-alpha.28 → 1.32.1-alpha.29
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/Modal/ModalTrigger.d.ts +2 -1
- package/lib/Modal/ModalTrigger.js +5 -4
- package/lib/PreviewPanel/PreviewPanel.d.ts +0 -1
- package/lib/PreviewPanel/details/MatrixResource.d.ts +0 -1
- package/lib/PreviewPanel/details/MatrixResource.js +2 -17
- package/lib/ResourceBreadcrumb/ResourceBreadcrumb.d.ts +0 -1
- package/lib/ResourceError/ResourceError.d.ts +0 -1
- package/lib/ResourceItem/ResourceItem.d.ts +0 -1
- package/lib/ResourceList/ResourceList.d.ts +0 -1
- package/lib/ResourcePickerContainer/ResourcePickerContainer.d.ts +0 -1
- package/lib/Skeleton/List/SkeletonList.d.ts +0 -1
- package/lib/Skeleton/ListItem/SkeletonListItem.d.ts +0 -1
- package/lib/SourceDropdown/SourceDropdown.d.ts +0 -1
- package/lib/SourceDropdown/SourceDropdown.js +1 -1
- package/lib/SourceList/SourceList.d.ts +0 -1
- package/lib/StatusIndicator/StatusIndicator.d.ts +6 -0
- package/lib/StatusIndicator/StatusIndicator.js +26 -0
- package/lib/index.css +110 -4
- package/lib/index.d.ts +2 -5
- package/lib/index.js +9 -3
- package/package.json +5 -3
- package/src/Icons/Icon.stories.tsx +5 -0
- package/src/Modal/Modal.spec.tsx +25 -0
- package/src/Modal/ModalTrigger.tsx +14 -2
- package/src/PreviewPanel/details/MatrixResource.tsx +2 -22
- package/src/ResourceError/ResourceError.spec.tsx +0 -4
- package/src/ResourceItem/ResourceItem.spec.tsx +0 -4
- package/src/ResourcePicker/ResetButton.tsx +14 -0
- package/src/ResourcePicker/ResourcePicker.spec.tsx +81 -0
- package/src/ResourcePicker/ResourcePicker.stories.tsx +62 -0
- package/src/ResourcePicker/ResourcePicker.tsx +55 -0
- package/src/ResourcePicker/States/Error.tsx +12 -0
- package/src/ResourcePicker/States/Loading.tsx +9 -0
- package/src/ResourcePicker/States/Selected.tsx +51 -0
- package/src/ResourcePicker/mock-image-resource.json +51 -0
- package/src/ResourcePicker/mock-resource.json +15 -0
- package/src/ResourcePicker/resource-picker.scss +13 -0
- package/src/ResourcePickerContainer/ResourcePickerContainer.stories.tsx +1 -1
- package/src/SourceDropdown/SourceDropdown.tsx +1 -1
- package/src/Spinner/Spinner.stories.tsx +1 -1
- package/src/Spinner/_spinner.scss +1 -1
- package/src/StatusIndicator/StatusIndicator.stories.tsx +83 -0
- package/src/StatusIndicator/StatusIndicator.tsx +35 -0
- package/src/index.scss +1 -0
- package/src/index.spec.tsx +44 -0
- package/src/index.stories.tsx +15 -17
- package/src/index.tsx +41 -20
- package/src/types.d.ts +5 -4
@@ -0,0 +1,81 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { render, screen } from '@testing-library/react';
|
3
|
+
import ResourcePicker from './ResourcePicker';
|
4
|
+
import mockResource from './mock-resource.json';
|
5
|
+
|
6
|
+
const defaultProps: any = {
|
7
|
+
resource: null,
|
8
|
+
allowedTypes: undefined,
|
9
|
+
isLoading: false,
|
10
|
+
isError: false,
|
11
|
+
};
|
12
|
+
|
13
|
+
describe('Resource picker', () => {
|
14
|
+
it('should render the initial state with the default label', () => {
|
15
|
+
render(<ResourcePicker {...defaultProps} />);
|
16
|
+
const pickerLabel = screen.getByText('Choose asset');
|
17
|
+
|
18
|
+
expect(pickerLabel).toBeInTheDocument();
|
19
|
+
});
|
20
|
+
|
21
|
+
it('should render the initial state with the image label if it s an image picker', () => {
|
22
|
+
render(<ResourcePicker {...defaultProps} allowedTypes={['image']} />);
|
23
|
+
const pickerLabel = screen.getByText('Choose image');
|
24
|
+
|
25
|
+
expect(pickerLabel).toBeInTheDocument();
|
26
|
+
});
|
27
|
+
|
28
|
+
it('should render the loading state if set to true', () => {
|
29
|
+
render(<ResourcePicker {...defaultProps} isLoading={true} />);
|
30
|
+
const pickerLabel = screen.queryByText('Choose image');
|
31
|
+
const loadingLabel = screen.queryByLabelText('Loading selection');
|
32
|
+
|
33
|
+
expect(pickerLabel).not.toBeInTheDocument();
|
34
|
+
expect(loadingLabel).toBeInTheDocument();
|
35
|
+
});
|
36
|
+
|
37
|
+
it('should render the error state if set to true', () => {
|
38
|
+
render(<ResourcePicker {...defaultProps} isError={true} />);
|
39
|
+
const pickerLabel = screen.queryByText('Choose image');
|
40
|
+
const errorLabel = screen.queryByText('Failed to retrieve asset info due to a Component Service API key problem.');
|
41
|
+
|
42
|
+
expect(pickerLabel).not.toBeInTheDocument();
|
43
|
+
expect(errorLabel).toBeInTheDocument();
|
44
|
+
});
|
45
|
+
|
46
|
+
it('should render the selected state if there is a resource returned', () => {
|
47
|
+
render(<ResourcePicker {...defaultProps} resource={mockResource} />);
|
48
|
+
const pickerLabel = screen.queryByText('Choose image');
|
49
|
+
const resourceName = screen.queryByText('Products');
|
50
|
+
|
51
|
+
expect(pickerLabel).not.toBeInTheDocument();
|
52
|
+
expect(resourceName).toBeInTheDocument();
|
53
|
+
});
|
54
|
+
|
55
|
+
it('should display the reset button in selected state', () => {
|
56
|
+
render(<ResourcePicker {...defaultProps} resource={mockResource} />);
|
57
|
+
const resourceName = screen.queryByText('Products');
|
58
|
+
const removeButton = screen.queryByLabelText('Remove selection');
|
59
|
+
|
60
|
+
expect(resourceName).toBeInTheDocument();
|
61
|
+
expect(removeButton).toBeInTheDocument();
|
62
|
+
});
|
63
|
+
|
64
|
+
it('should display the reset button in error state', () => {
|
65
|
+
render(<ResourcePicker {...defaultProps} isError={true} />);
|
66
|
+
const errorLabel = screen.queryByText('Failed to retrieve asset info due to a Component Service API key problem.');
|
67
|
+
const removeButton = screen.queryByLabelText('Remove selection');
|
68
|
+
|
69
|
+
expect(errorLabel).toBeInTheDocument();
|
70
|
+
expect(removeButton).toBeInTheDocument();
|
71
|
+
});
|
72
|
+
|
73
|
+
it('should not display the reset button in the empty state', () => {
|
74
|
+
render(<ResourcePicker {...defaultProps} />);
|
75
|
+
const pickerLabel = screen.getByText('Choose asset');
|
76
|
+
const removeButton = screen.queryByLabelText('Remove selection');
|
77
|
+
|
78
|
+
expect(pickerLabel).toBeInTheDocument();
|
79
|
+
expect(removeButton).not.toBeInTheDocument();
|
80
|
+
});
|
81
|
+
});
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { StoryFn, Meta } from '@storybook/react';
|
3
|
+
|
4
|
+
import ResourcePicker, { ResourcePickerProps } from './ResourcePicker';
|
5
|
+
import mockResource from './mock-resource.json';
|
6
|
+
import mockImageResource from './mock-image-resource.json';
|
7
|
+
|
8
|
+
export default {
|
9
|
+
title: 'Resource picker field',
|
10
|
+
component: ResourcePicker,
|
11
|
+
} as Meta<typeof ResourcePicker>;
|
12
|
+
|
13
|
+
const Template: StoryFn<ResourcePickerProps> = (args: ResourcePickerProps) => (
|
14
|
+
<div className="w-[400px] m-3">
|
15
|
+
<ResourcePicker {...args} />
|
16
|
+
</div>
|
17
|
+
);
|
18
|
+
|
19
|
+
export const Empty = Template.bind({});
|
20
|
+
Empty.args = {
|
21
|
+
resource: null,
|
22
|
+
allowedTypes: undefined,
|
23
|
+
isLoading: false,
|
24
|
+
isError: false,
|
25
|
+
isDisabled: false,
|
26
|
+
};
|
27
|
+
|
28
|
+
export const ImagePicker = Template.bind({});
|
29
|
+
ImagePicker.args = {
|
30
|
+
...Empty.args,
|
31
|
+
allowedTypes: ['image'],
|
32
|
+
};
|
33
|
+
|
34
|
+
export const Loading = Template.bind({});
|
35
|
+
Loading.args = {
|
36
|
+
...Empty.args,
|
37
|
+
isLoading: true,
|
38
|
+
};
|
39
|
+
|
40
|
+
export const Error = Template.bind({});
|
41
|
+
Error.args = {
|
42
|
+
...Empty.args,
|
43
|
+
isError: true,
|
44
|
+
};
|
45
|
+
|
46
|
+
export const Selected = Template.bind({});
|
47
|
+
Selected.args = {
|
48
|
+
...Empty.args,
|
49
|
+
resource: mockResource,
|
50
|
+
};
|
51
|
+
|
52
|
+
export const SelectedImage = Template.bind({});
|
53
|
+
SelectedImage.args = {
|
54
|
+
...Empty.args,
|
55
|
+
resource: mockImageResource,
|
56
|
+
};
|
57
|
+
|
58
|
+
export const Disabled = Template.bind({});
|
59
|
+
Disabled.args = {
|
60
|
+
...Empty.args,
|
61
|
+
isDisabled: true,
|
62
|
+
};
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
|
3
|
+
import { Resource } from '../types';
|
4
|
+
import AdsClickRoundedIcon from '@mui/icons-material/AdsClickRounded';
|
5
|
+
import AddCircleOutlineRoundedIcon from '@mui/icons-material/AddCircleOutlineRounded';
|
6
|
+
import PhotoLibraryRoundedIcon from '@mui/icons-material/PhotoLibraryRounded';
|
7
|
+
|
8
|
+
import ModalTrigger from '../Modal/ModalTrigger';
|
9
|
+
import { ErrorState } from './States/Error';
|
10
|
+
import { LoadingState } from './States/Loading';
|
11
|
+
import { SelectedState } from './States/Selected';
|
12
|
+
import clsx from 'clsx';
|
13
|
+
|
14
|
+
export type ResourcePickerProps = {
|
15
|
+
resource: Resource;
|
16
|
+
allowedTypes: string[] | undefined;
|
17
|
+
isError: boolean;
|
18
|
+
isLoading: boolean;
|
19
|
+
isDisabled: boolean;
|
20
|
+
};
|
21
|
+
|
22
|
+
const ResourcePicker = ({ resource, allowedTypes, isError, isLoading, isDisabled }: ResourcePickerProps) => {
|
23
|
+
const isImagePicker = allowedTypes && allowedTypes.length === 1 && (allowedTypes.includes('image') as boolean);
|
24
|
+
const isEmpty = resource === null && !isLoading && (!isError as boolean);
|
25
|
+
|
26
|
+
return (
|
27
|
+
<div className={clsx('resource-picker', isDisabled && 'bg-gray-300')}>
|
28
|
+
{isImagePicker ? (
|
29
|
+
<PhotoLibraryRoundedIcon aria-hidden className="w-6 h-6" />
|
30
|
+
) : (
|
31
|
+
<AdsClickRoundedIcon aria-hidden className="w-6 h-6" />
|
32
|
+
)}
|
33
|
+
{isEmpty ? (
|
34
|
+
<ModalTrigger
|
35
|
+
showLabel={true}
|
36
|
+
label={isImagePicker ? `Choose image` : `Choose asset`}
|
37
|
+
icon={<AddCircleOutlineRoundedIcon aria-hidden className="!w-4 !h-4" />}
|
38
|
+
isDisabled={isDisabled}
|
39
|
+
>
|
40
|
+
{() => <div>Resource browser here</div>}
|
41
|
+
</ModalTrigger>
|
42
|
+
) : (
|
43
|
+
<div className="resource-picker-info">
|
44
|
+
<div className="resource-picker-info__layout">
|
45
|
+
{isLoading && <LoadingState />}
|
46
|
+
{isError && <ErrorState isDisabled={isDisabled} />}
|
47
|
+
{resource !== null && <SelectedState resource={resource} isDisabled={isDisabled} />}
|
48
|
+
</div>
|
49
|
+
</div>
|
50
|
+
)}
|
51
|
+
</div>
|
52
|
+
);
|
53
|
+
};
|
54
|
+
|
55
|
+
export default ResourcePicker;
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
|
3
|
+
import Icon, { IconOptions } from '../../Icons/Icon';
|
4
|
+
import { ResetButton } from '../ResetButton';
|
5
|
+
|
6
|
+
export const ErrorState = ({ isDisabled }: { isDisabled: boolean }) => (
|
7
|
+
<>
|
8
|
+
<Icon icon={'error' as IconOptions} aria-hidden className="w-6 h-6 text-red-300" />
|
9
|
+
<div className="text-red-300">Failed to retrieve asset info due to a Component Service API key problem.</div>
|
10
|
+
<ResetButton isDisabled={isDisabled} />
|
11
|
+
</>
|
12
|
+
);
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import prettyBytes from 'pretty-bytes';
|
3
|
+
|
4
|
+
import { Resource } from '../../types';
|
5
|
+
import Icon, { IconOptions } from '../../Icons/Icon';
|
6
|
+
import StatusIndicator from '../../StatusIndicator/StatusIndicator';
|
7
|
+
import { ResetButton } from '../ResetButton';
|
8
|
+
|
9
|
+
export type SelectedStateProps = {
|
10
|
+
resource: Resource;
|
11
|
+
isDisabled: boolean;
|
12
|
+
};
|
13
|
+
|
14
|
+
export const SelectedState = ({ resource: { id, type, name, status, squizImage }, isDisabled }: SelectedStateProps) => {
|
15
|
+
const fileSize = squizImage?.imageVariations?.original?.byteSize;
|
16
|
+
const fileWidth = squizImage?.imageVariations?.original?.width;
|
17
|
+
const fileHeight = squizImage?.imageVariations?.original?.height;
|
18
|
+
return (
|
19
|
+
<>
|
20
|
+
<>
|
21
|
+
{/* Left column */}
|
22
|
+
<Icon icon={type.code as IconOptions} resourceSource="matrix" className="w-4 h-4 flex self-center" />
|
23
|
+
{/* Center column */}
|
24
|
+
<div className="justify-self-start self-center">{name}</div>
|
25
|
+
{/* End column */}
|
26
|
+
<ResetButton isDisabled={isDisabled} />
|
27
|
+
</>
|
28
|
+
<dl className="col-start-2 col-end-2 flex flex-column gap-1 justify-self-start items-center font-normal text-sm">
|
29
|
+
<div>
|
30
|
+
<dt className="hidden">Status: {status.name}</dt>
|
31
|
+
<dd className="flex items-center">
|
32
|
+
<StatusIndicator status={status} />
|
33
|
+
</dd>
|
34
|
+
</div>
|
35
|
+
|
36
|
+
<div>
|
37
|
+
<dt className="hidden">Asset ID</dt>
|
38
|
+
<dd className="text-gray-700">#{id}</dd>
|
39
|
+
</div>
|
40
|
+
{fileSize && (
|
41
|
+
<div>
|
42
|
+
<dt className="hidden">Asset size</dt>
|
43
|
+
<dd className="ml-4 text-gray-600">
|
44
|
+
{prettyBytes(fileSize)}, {fileWidth} x {fileHeight}px
|
45
|
+
</dd>
|
46
|
+
</div>
|
47
|
+
)}
|
48
|
+
</dl>
|
49
|
+
</>
|
50
|
+
);
|
51
|
+
};
|
@@ -0,0 +1,51 @@
|
|
1
|
+
{
|
2
|
+
"id": "45104",
|
3
|
+
"type": {
|
4
|
+
"code": "image",
|
5
|
+
"name": "Image"
|
6
|
+
},
|
7
|
+
"name": "DfBFfNQyB77dQMJYU4eH.jpg",
|
8
|
+
"status": {
|
9
|
+
"code": "under_construction",
|
10
|
+
"name": "Under Construction"
|
11
|
+
},
|
12
|
+
"url": "",
|
13
|
+
"urls": [],
|
14
|
+
"childCount": 0,
|
15
|
+
"squizImage": {
|
16
|
+
"name": "Fancy music",
|
17
|
+
"alt": "",
|
18
|
+
"caption": "",
|
19
|
+
"imageVariations": {
|
20
|
+
"original": {
|
21
|
+
"url": "",
|
22
|
+
"width": 400,
|
23
|
+
"height": 400,
|
24
|
+
"byteSize": 37174,
|
25
|
+
"mimeType": "image/",
|
26
|
+
"aspectRatio": "4:3",
|
27
|
+
"sha1Hash": ""
|
28
|
+
},
|
29
|
+
"aspectRatios": [
|
30
|
+
{
|
31
|
+
"url": "",
|
32
|
+
"width": 100,
|
33
|
+
"height": 100,
|
34
|
+
"byteSize": 4628,
|
35
|
+
"mimeType": "image/",
|
36
|
+
"aspectRatio": "4:3",
|
37
|
+
"sha1Hash": ""
|
38
|
+
},
|
39
|
+
{
|
40
|
+
"url": "",
|
41
|
+
"width": 104,
|
42
|
+
"height": 96,
|
43
|
+
"byteSize": 5718,
|
44
|
+
"mimeType": "image/",
|
45
|
+
"aspectRatio": "4:3",
|
46
|
+
"sha1Hash": ""
|
47
|
+
}
|
48
|
+
]
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
{
|
2
|
+
"id": "1345",
|
3
|
+
"name": "Products",
|
4
|
+
"type": {
|
5
|
+
"code": "page_standard",
|
6
|
+
"name": "Standard Page"
|
7
|
+
},
|
8
|
+
"status": {
|
9
|
+
"code": "under_construction",
|
10
|
+
"name": "Under construction"
|
11
|
+
},
|
12
|
+
"url": "http://my-squiz.net/assets/1",
|
13
|
+
"urls": [],
|
14
|
+
"childCount": 0
|
15
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
.resource-picker {
|
2
|
+
@apply grid grid-cols-[24px_1fr] gap-2;
|
3
|
+
@apply border-2 border-gray-300;
|
4
|
+
@apply text-gray-500 rounded p-2;
|
5
|
+
&-info {
|
6
|
+
@apply w-full p-2 bg-gray-100 rounded-md;
|
7
|
+
@apply text-gray-900 text-base text-md font-semibold;
|
8
|
+
&__layout {
|
9
|
+
@apply grid grid-cols-[24px_1fr_24px] gap-2;
|
10
|
+
@apply justify-items-center;
|
11
|
+
}
|
12
|
+
}
|
13
|
+
}
|
@@ -4,7 +4,7 @@ import ResourcePickerContainer from './ResourcePickerContainer';
|
|
4
4
|
import { createResourceBrowserCallbacks } from '../__mocks__/StorybookHelpers';
|
5
5
|
|
6
6
|
export default {
|
7
|
-
title: 'Resource Picker',
|
7
|
+
title: 'Resource Picker container',
|
8
8
|
component: ResourcePickerContainer,
|
9
9
|
} as Meta<typeof ResourcePickerContainer>;
|
10
10
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
@apply border-4 border-solid border-current border-r-transparent;
|
4
4
|
@apply animate-spin align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite];
|
5
5
|
&__wrapper {
|
6
|
-
@apply flex items-center justify-center
|
6
|
+
@apply flex items-center justify-center text-gray-300;
|
7
7
|
}
|
8
8
|
&--lg {
|
9
9
|
@apply h-12 w-12;
|
@@ -0,0 +1,83 @@
|
|
1
|
+
import StatusIndicator, { StatusIndicatorProps } from './StatusIndicator';
|
2
|
+
import { Meta, StoryFn } from '@storybook/react';
|
3
|
+
|
4
|
+
export default {
|
5
|
+
title: 'Status Indicator',
|
6
|
+
component: StatusIndicator,
|
7
|
+
} as Meta<typeof StatusIndicator>;
|
8
|
+
|
9
|
+
const Template: StoryFn<StatusIndicatorProps> = (args: StatusIndicatorProps) => (
|
10
|
+
<div className="m-3">
|
11
|
+
<StatusIndicator {...args} />
|
12
|
+
</div>
|
13
|
+
);
|
14
|
+
|
15
|
+
export const UnderConstruction = Template.bind({});
|
16
|
+
UnderConstruction.args = {
|
17
|
+
status: {
|
18
|
+
code: 'under_construction',
|
19
|
+
name: 'Under construction',
|
20
|
+
},
|
21
|
+
};
|
22
|
+
|
23
|
+
export const PendingApproval = Template.bind({});
|
24
|
+
PendingApproval.args = {
|
25
|
+
status: {
|
26
|
+
code: 'pending_approval',
|
27
|
+
name: 'Pending approval',
|
28
|
+
},
|
29
|
+
};
|
30
|
+
|
31
|
+
export const ApprovedToGoLive = Template.bind({});
|
32
|
+
ApprovedToGoLive.args = {
|
33
|
+
status: {
|
34
|
+
code: 'approved_to_go_live',
|
35
|
+
name: 'Approved to go live',
|
36
|
+
},
|
37
|
+
};
|
38
|
+
|
39
|
+
export const Live = Template.bind({});
|
40
|
+
Live.args = {
|
41
|
+
status: {
|
42
|
+
code: 'live',
|
43
|
+
name: 'Live',
|
44
|
+
},
|
45
|
+
};
|
46
|
+
|
47
|
+
export const UpForReview = Template.bind({});
|
48
|
+
UpForReview.args = {
|
49
|
+
status: {
|
50
|
+
code: 'up_for_review',
|
51
|
+
name: 'Up for review',
|
52
|
+
},
|
53
|
+
};
|
54
|
+
|
55
|
+
export const SafeEditing = Template.bind({});
|
56
|
+
SafeEditing.args = {
|
57
|
+
status: {
|
58
|
+
code: 'safe_editing',
|
59
|
+
name: 'Safe editing',
|
60
|
+
},
|
61
|
+
};
|
62
|
+
|
63
|
+
export const SafeEditingPendingApproval = Template.bind({});
|
64
|
+
SafeEditingPendingApproval.args = {
|
65
|
+
status: {
|
66
|
+
code: 'safe_editing_pending_approval',
|
67
|
+
name: 'Safe editing pending approval',
|
68
|
+
},
|
69
|
+
};
|
70
|
+
export const SafeEditApprovedToGoLive = Template.bind({});
|
71
|
+
SafeEditApprovedToGoLive.args = {
|
72
|
+
status: {
|
73
|
+
code: 'safe_edit_approved_to_go_live',
|
74
|
+
name: 'Safe edit approved to go live',
|
75
|
+
},
|
76
|
+
};
|
77
|
+
export const Archived = Template.bind({});
|
78
|
+
Archived.args = {
|
79
|
+
status: {
|
80
|
+
code: 'archived',
|
81
|
+
name: 'Archived',
|
82
|
+
},
|
83
|
+
};
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Status } from '../types';
|
3
|
+
|
4
|
+
const statusColour = {
|
5
|
+
// Duplicated from the Matrix repository.
|
6
|
+
// src/Api/AssetManagementApi/Constants/AssetStatuses.php - contains a list of possible statuses.
|
7
|
+
// frontend/src/styles/common/status-colors.scss - contains the colours used for those statuses in Matrix.
|
8
|
+
unknown: '#ff0000',
|
9
|
+
archived: '#c98a67',
|
10
|
+
under_construction: '#94d1f9',
|
11
|
+
pending_approval: '#d0bbf0',
|
12
|
+
approved_to_go_live: '#f7eaa2',
|
13
|
+
live: '#bfe60a',
|
14
|
+
up_for_review: '#72cd32',
|
15
|
+
safe_editing: '#ff97b0',
|
16
|
+
safe_editing_pending_approval: '#d688db',
|
17
|
+
safe_edit_approved_to_go_live: '#ffb34a',
|
18
|
+
};
|
19
|
+
|
20
|
+
export type StatusIndicatorProps = {
|
21
|
+
status: Status;
|
22
|
+
};
|
23
|
+
|
24
|
+
const StatusIndicator = ({ status }: StatusIndicatorProps) => {
|
25
|
+
const color = statusColour[status.code as keyof typeof statusColour] || statusColour.unknown;
|
26
|
+
|
27
|
+
return (
|
28
|
+
<span
|
29
|
+
style={{ backgroundColor: color }}
|
30
|
+
className="block rounded-full w-3 h-3 border border-solid border-black border-opacity-20"
|
31
|
+
></span>
|
32
|
+
);
|
33
|
+
};
|
34
|
+
|
35
|
+
export default StatusIndicator;
|
package/src/index.scss
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { render, screen } from '@testing-library/react';
|
3
|
+
import RelatedAssetPicker from './index';
|
4
|
+
|
5
|
+
const mockRequestSources = jest.fn();
|
6
|
+
const mockRequestChildren = jest.fn();
|
7
|
+
const mockRequestResource = jest.fn();
|
8
|
+
const mockChange = jest.fn();
|
9
|
+
|
10
|
+
const defaultProps: any = {
|
11
|
+
modalTitle: 'Asset Picker',
|
12
|
+
allowedTypes: ['site', 'image', 'physical_file'],
|
13
|
+
onRequestSources: mockRequestSources,
|
14
|
+
onRequestChildren: mockRequestChildren,
|
15
|
+
onRequestResource: mockRequestResource,
|
16
|
+
onChange: mockChange,
|
17
|
+
};
|
18
|
+
|
19
|
+
describe('Related Asset Picker', () => {
|
20
|
+
it('should render the related asset picker with the default label', () => {
|
21
|
+
render(<RelatedAssetPicker {...defaultProps} />);
|
22
|
+
const pickerLabel = screen.getByText('Choose asset');
|
23
|
+
|
24
|
+
expect(pickerLabel).toBeInTheDocument();
|
25
|
+
});
|
26
|
+
|
27
|
+
it('should display the generic asset picking icon', () => {
|
28
|
+
render(<RelatedAssetPicker {...defaultProps} />);
|
29
|
+
const pickerLabel = screen.getByText('Choose asset');
|
30
|
+
const pickerIcon = screen.getByTestId('AdsClickRoundedIcon');
|
31
|
+
|
32
|
+
expect(pickerLabel).toBeInTheDocument();
|
33
|
+
expect(pickerIcon).toBeInTheDocument();
|
34
|
+
});
|
35
|
+
|
36
|
+
it('should display the image icon when only images are allowed', () => {
|
37
|
+
render(<RelatedAssetPicker {...defaultProps} allowedTypes={['image']} />);
|
38
|
+
const pickerLabel = screen.getByText('Choose image');
|
39
|
+
const pickerIcon = screen.getByTestId('PhotoLibraryRoundedIcon');
|
40
|
+
|
41
|
+
expect(pickerLabel).toBeInTheDocument();
|
42
|
+
expect(pickerIcon).toBeInTheDocument();
|
43
|
+
});
|
44
|
+
});
|
package/src/index.stories.tsx
CHANGED
@@ -16,32 +16,30 @@ const Template: StoryFn<typeof RelatedAssetPicker> = (props) => {
|
|
16
16
|
});
|
17
17
|
|
18
18
|
return (
|
19
|
-
<
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
<div className="w-[400px] m-3">
|
20
|
+
<RelatedAssetPicker
|
21
|
+
{...props}
|
22
|
+
onRequestSources={onRequestSources}
|
23
|
+
onRequestChildren={onRequestChildren}
|
24
|
+
onRequestResource={onRequestResource}
|
25
|
+
onChange={onChange}
|
26
|
+
/>
|
27
|
+
</div>
|
26
28
|
);
|
27
29
|
};
|
28
30
|
|
29
31
|
export const Primary = Template.bind({});
|
30
32
|
|
31
33
|
Primary.args = {
|
32
|
-
showButtonLabel: false,
|
33
|
-
buttonLabel: 'Open related asset picker',
|
34
|
-
buttonIcon: (
|
35
|
-
<svg width="30" height="30" viewBox="0 0 61 61" fill="none" xmlns="http://www.w3.org/2000/svg">
|
36
|
-
<path
|
37
|
-
d="M29.1 48C24.3 47.75 20.25 45.9 16.95 42.45C13.65 39 12 34.85 12 30C12 25 13.75 20.75 17.25 17.25C20.75 13.75 25 12 30 12C34.85 12 39 13.65 42.45 16.95C45.9 20.25 47.75 24.3 48 29.1L41.7 27.225C41.05 24.525 39.65 22.3125 37.5 20.5875C35.35 18.8625 32.85 18 30 18C26.7 18 23.875 19.175 21.525 21.525C19.175 23.875 18 26.7 18 30C18 32.85 18.8625 35.35 20.5875 37.5C22.3125 39.65 24.525 41.05 27.225 41.7L29.1 48ZM30 60C25.85 60 21.95 59.2125 18.3 57.6375C14.65 56.0625 11.475 53.925 8.775 51.225C6.075 48.525 3.9375 45.35 2.3625 41.7C0.7875 38.05 0 34.15 0 30C0 25.85 0.7875 21.95 2.3625 18.3C3.9375 14.65 6.075 11.475 8.775 8.775C11.475 6.075 14.65 3.9375 18.3 2.3625C21.95 0.7875 25.85 0 30 0C34.15 0 38.05 0.7875 41.7 2.3625C45.35 3.9375 48.525 6.075 51.225 8.775C53.925 11.475 56.0625 14.65 57.6375 18.3C59.2125 21.95 60 25.85 60 30V31.35C60 31.8 59.95 32.25 59.85 32.7L54 30.9V30C54 23.3 51.675 17.625 47.025 12.975C42.375 8.325 36.7 6 30 6C23.3 6 17.625 8.325 12.975 12.975C8.325 17.625 6 23.3 6 30C6 36.7 8.325 42.375 12.975 47.025C17.625 51.675 23.3 54 30 54H30.9L32.7 59.85C32.25 59.95 31.8 60 31.35 60H30ZM54.525 60.45L42.75 48.675L40.5 55.5C40.25 56.2 39.775 56.5375 39.075 56.5125C38.375 56.4875 37.9 56.125 37.65 55.425L30.825 32.7C30.625 32.15 30.75 31.65 31.2 31.2C31.65 30.75 32.15 30.625 32.7 30.825L55.425 37.65C56.125 37.9 56.4875 38.375 56.5125 39.075C56.5375 39.775 56.2 40.25 55.5 40.5L48.675 42.75L60.45 54.525C60.75 54.825 60.9 55.175 60.9 55.575C60.9 55.975 60.75 56.325 60.45 56.625L56.625 60.45C56.325 60.75 55.975 60.9 55.575 60.9C55.175 60.9 54.825 60.75 54.525 60.45Z"
|
38
|
-
fill="#BABABA"
|
39
|
-
/>
|
40
|
-
</svg>
|
41
|
-
),
|
42
34
|
modalTitle: 'Asset Picker',
|
43
35
|
sourceIsLoading: false,
|
44
36
|
resourceIsLoading: false,
|
45
37
|
error: '',
|
46
38
|
allowedTypes: ['site', 'image', 'physical_file'],
|
47
39
|
};
|
40
|
+
|
41
|
+
export const ImagesOnly = Template.bind({});
|
42
|
+
|
43
|
+
ImagesOnly.args = {
|
44
|
+
allowedTypes: ['image'],
|
45
|
+
};
|