@squiz/resource-browser 1.32.1-alpha.27 → 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.
Files changed (48) hide show
  1. package/lib/Modal/ModalTrigger.d.ts +2 -1
  2. package/lib/Modal/ModalTrigger.js +5 -4
  3. package/lib/PreviewPanel/PreviewPanel.d.ts +0 -1
  4. package/lib/PreviewPanel/details/MatrixResource.d.ts +0 -1
  5. package/lib/PreviewPanel/details/MatrixResource.js +2 -17
  6. package/lib/ResourceBreadcrumb/ResourceBreadcrumb.d.ts +0 -1
  7. package/lib/ResourceError/ResourceError.d.ts +0 -1
  8. package/lib/ResourceItem/ResourceItem.d.ts +0 -1
  9. package/lib/ResourceList/ResourceList.d.ts +0 -1
  10. package/lib/ResourcePickerContainer/ResourcePickerContainer.d.ts +0 -1
  11. package/lib/Skeleton/List/SkeletonList.d.ts +0 -1
  12. package/lib/Skeleton/ListItem/SkeletonListItem.d.ts +0 -1
  13. package/lib/SourceDropdown/SourceDropdown.d.ts +0 -1
  14. package/lib/SourceDropdown/SourceDropdown.js +1 -1
  15. package/lib/SourceList/SourceList.d.ts +0 -1
  16. package/lib/StatusIndicator/StatusIndicator.d.ts +6 -0
  17. package/lib/StatusIndicator/StatusIndicator.js +26 -0
  18. package/lib/index.css +110 -4
  19. package/lib/index.d.ts +2 -5
  20. package/lib/index.js +9 -3
  21. package/package.json +5 -3
  22. package/src/Icons/Icon.stories.tsx +5 -0
  23. package/src/Modal/Modal.spec.tsx +25 -0
  24. package/src/Modal/ModalTrigger.tsx +14 -2
  25. package/src/PreviewPanel/details/MatrixResource.tsx +2 -22
  26. package/src/ResourceError/ResourceError.spec.tsx +0 -4
  27. package/src/ResourceItem/ResourceItem.spec.tsx +0 -4
  28. package/src/ResourcePicker/ResetButton.tsx +14 -0
  29. package/src/ResourcePicker/ResourcePicker.spec.tsx +81 -0
  30. package/src/ResourcePicker/ResourcePicker.stories.tsx +62 -0
  31. package/src/ResourcePicker/ResourcePicker.tsx +55 -0
  32. package/src/ResourcePicker/States/Error.tsx +12 -0
  33. package/src/ResourcePicker/States/Loading.tsx +9 -0
  34. package/src/ResourcePicker/States/Selected.tsx +51 -0
  35. package/src/ResourcePicker/mock-image-resource.json +51 -0
  36. package/src/ResourcePicker/mock-resource.json +15 -0
  37. package/src/ResourcePicker/resource-picker.scss +13 -0
  38. package/src/ResourcePickerContainer/ResourcePickerContainer.stories.tsx +1 -1
  39. package/src/SourceDropdown/SourceDropdown.tsx +1 -1
  40. package/src/Spinner/Spinner.stories.tsx +1 -1
  41. package/src/Spinner/_spinner.scss +1 -1
  42. package/src/StatusIndicator/StatusIndicator.stories.tsx +83 -0
  43. package/src/StatusIndicator/StatusIndicator.tsx +35 -0
  44. package/src/index.scss +1 -0
  45. package/src/index.spec.tsx +44 -0
  46. package/src/index.stories.tsx +15 -17
  47. package/src/index.tsx +41 -20
  48. package/src/types.d.ts +5 -4
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
2
  import { DOMAttributes, FocusableElement } from '@react-types/shared';
3
- declare function ModalTrigger({ label, showLabel, icon, children, ...props }: {
3
+ declare function ModalTrigger({ label, showLabel, icon, isDisabled, children, ...props }: {
4
4
  label: string;
5
5
  showLabel?: boolean;
6
6
  icon?: React.ReactNode;
7
+ isDisabled?: boolean;
7
8
  children: (onClose: () => void, titleProps: DOMAttributes<FocusableElement>) => React.ReactElement;
8
9
  }): JSX.Element;
9
10
  export default ModalTrigger;
@@ -8,7 +8,8 @@ const react_aria_1 = require("react-aria");
8
8
  const react_stately_1 = require("react-stately");
9
9
  const Modal_1 = __importDefault(require("./Modal"));
10
10
  const ModalOpeningButton_1 = __importDefault(require("./ModalOpeningButton"));
11
- function ModalTrigger({ label, showLabel, icon, children, ...props }) {
11
+ const clsx_1 = __importDefault(require("clsx"));
12
+ function ModalTrigger({ label, showLabel, icon, isDisabled, children, ...props }) {
12
13
  const state = (0, react_stately_1.useOverlayTriggerState)(props);
13
14
  const { triggerProps, overlayProps } = (0, react_aria_1.useOverlayTrigger)({ type: 'dialog' }, state);
14
15
  let ariaAttr = {};
@@ -16,9 +17,9 @@ function ModalTrigger({ label, showLabel, icon, children, ...props }) {
16
17
  ariaAttr = { ...ariaAttr, 'aria-label': label };
17
18
  }
18
19
  return (react_1.default.createElement(react_1.default.Fragment, null,
19
- react_1.default.createElement(ModalOpeningButton_1.default, { type: "button", ...triggerProps, ...ariaAttr },
20
- showLabel && label,
21
- icon),
20
+ react_1.default.createElement(ModalOpeningButton_1.default, { type: "button", ...triggerProps, ...ariaAttr, isDisabled: isDisabled, className: (0, clsx_1.default)('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') },
21
+ icon,
22
+ showLabel && react_1.default.createElement("span", { className: "ml-1 text-sm leading-4" }, label)),
22
23
  state.isOpen && (react_1.default.createElement(Modal_1.default, { isDismissable: true, state: state, overlayProps: overlayProps }, (titleProps) => children(state.close, titleProps)))));
23
24
  }
24
25
  exports.default = ModalTrigger;
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  import { OverlayTriggerState } from 'react-stately';
3
2
  import { DOMAttributes, FocusableElement } from '@react-types/shared';
4
3
  import { Resource } from '../types';
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  import { Resource } from '../../types';
3
2
  type MatrixResourceProps = {
4
3
  resource: Resource;
@@ -5,23 +5,8 @@ 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 statusColour = {
9
- // Duplicated from the Matrix repository.
10
- // src/Api/AssetManagementApi/Constants/AssetStatuses.php - contains a list of possible statuses.
11
- // frontend/src/styles/common/status-colors.scss - contains the colours used for those statuses in Matrix.
12
- unknown: '#ff0000',
13
- archived: '#c98a67',
14
- under_construction: '#94d1f9',
15
- pending_approval: '#d0bbf0',
16
- approved_to_go_live: '#f7eaa2',
17
- live: '#bfe60a',
18
- up_for_review: '#72cd32',
19
- safe_editing: '#ff97b0',
20
- safe_editing_pending_approval: '#d688db',
21
- safe_edit_approved_to_go_live: '#ffb34a',
22
- };
8
+ const StatusIndicator_1 = __importDefault(require("../../StatusIndicator/StatusIndicator"));
23
9
  const MatrixResource = ({ resource: { id, type, name, status } }) => {
24
- const color = statusColour[status.code] || statusColour.unknown;
25
10
  return (react_1.default.createElement("div", null,
26
11
  react_1.default.createElement("div", { className: "flex flex-col items-center text-gray-800 mt-7 mx-5 pb-4 border-b border-gray-300" },
27
12
  react_1.default.createElement(Icon_1.default, { icon: type.code, resourceSource: "matrix", className: "w-14 h-14" }),
@@ -39,7 +24,7 @@ const MatrixResource = ({ resource: { id, type, name, status } }) => {
39
24
  react_1.default.createElement("div", { className: "flex mb-2" },
40
25
  react_1.default.createElement("dt", { className: "w-[60px] mr-4 text-gray-600" }, "Status"),
41
26
  react_1.default.createElement("dd", { className: "flex items-center font-semibold" },
42
- react_1.default.createElement("span", { style: { backgroundColor: color }, className: "block rounded-full w-3 h-3 mr-1 border border-solid border-black border-opacity-20" }),
27
+ react_1.default.createElement(StatusIndicator_1.default, { status: status }),
43
28
  status.name))))));
44
29
  };
45
30
  exports.default = MatrixResource;
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  import { Hierarchy } from '../types';
3
2
  export interface ResourceBreadcrumbProps<T> {
4
3
  hierarchy: Hierarchy<T>;
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  interface ResourceError {
3
2
  errorMessage: string;
4
3
  handleReload: () => void;
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  import { DOMAttributes, FocusableElement } from '@react-types/shared';
3
2
  import { OverlayTriggerState } from 'react-stately';
4
3
  interface ResourceItem<T> {
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  import { OverlayTriggerState } from 'react-stately';
3
2
  import { DOMAttributes, FocusableElement } from '@react-types/shared';
4
3
  import { Resource } from '../types';
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  import { DOMAttributes, FocusableElement } from '@react-types/shared';
3
2
  import { Source, Resource, HydratedResourceReference } from '../types';
4
3
  interface ResourcePickerContainerProps {
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  export type SkeletonListProps = {
3
2
  itemCount: number;
4
3
  className?: string;
@@ -1,2 +1 @@
1
- /// <reference types="react" />
2
1
  export declare const SkeletonListItem: () => JSX.Element;
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  import type { Source, ScopedSource } from '../types';
3
2
  export default function SourceDropdown({ sources, selectedSource, isLoading, onRootSelect, onSourceSelect, }: {
4
3
  sources: Source[];
@@ -79,7 +79,7 @@ function SourceDropdown({ sources, selectedSource, isLoading, onRootSelect, onSo
79
79
  react_1.default.createElement(Icon_1.default, { icon: 'root', "aria-hidden": true, className: "mr-2.5" }),
80
80
  "All available sources")),
81
81
  isLoading && (react_1.default.createElement("li", { className: "mt-6" },
82
- react_1.default.createElement(Spinner_1.default, { size: "lg", label: "Loading sources" }))),
82
+ react_1.default.createElement(Spinner_1.default, { size: "lg", label: "Loading sources", className: "m-3" }))),
83
83
  !isLoading &&
84
84
  categorisedSources.map(({ key, label, sources }, index) => {
85
85
  return (react_1.default.createElement("li", { key: key, className: `flex flex-col text-sm font-semibold text-grey-800 ${index > 0 ? 'mt-3' : ''}` },
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  import { OverlayTriggerState } from 'react-stately';
3
2
  import { DOMAttributes, FocusableElement } from '@react-types/shared';
4
3
  import { Source, ScopedSource } from '../types';
@@ -0,0 +1,6 @@
1
+ import { Status } from '../types';
2
+ export type StatusIndicatorProps = {
3
+ status: Status;
4
+ };
5
+ declare const StatusIndicator: ({ status }: StatusIndicatorProps) => JSX.Element;
6
+ export default StatusIndicator;
@@ -0,0 +1,26 @@
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
+ const statusColour = {
8
+ // Duplicated from the Matrix repository.
9
+ // src/Api/AssetManagementApi/Constants/AssetStatuses.php - contains a list of possible statuses.
10
+ // frontend/src/styles/common/status-colors.scss - contains the colours used for those statuses in Matrix.
11
+ unknown: '#ff0000',
12
+ archived: '#c98a67',
13
+ under_construction: '#94d1f9',
14
+ pending_approval: '#d0bbf0',
15
+ approved_to_go_live: '#f7eaa2',
16
+ live: '#bfe60a',
17
+ up_for_review: '#72cd32',
18
+ safe_editing: '#ff97b0',
19
+ safe_editing_pending_approval: '#d688db',
20
+ safe_edit_approved_to_go_live: '#ffb34a',
21
+ };
22
+ const StatusIndicator = ({ status }) => {
23
+ const color = statusColour[status.code] || statusColour.unknown;
24
+ return (react_1.default.createElement("span", { style: { backgroundColor: color }, className: "block rounded-full w-3 h-3 border border-solid border-black border-opacity-20" }));
25
+ };
26
+ exports.default = StatusIndicator;
package/lib/index.css CHANGED
@@ -414,6 +414,18 @@
414
414
  .squiz-rb-scope .z-\[9999\] {
415
415
  z-index: 9999;
416
416
  }
417
+ .squiz-rb-scope .col-start-2 {
418
+ grid-column-start: 2;
419
+ }
420
+ .squiz-rb-scope .col-end-2 {
421
+ grid-column-end: 2;
422
+ }
423
+ .squiz-rb-scope .m-2 {
424
+ margin: 0.5rem;
425
+ }
426
+ .squiz-rb-scope .m-3 {
427
+ margin: 0.75rem;
428
+ }
417
429
  .squiz-rb-scope .m-5 {
418
430
  margin: 1.25rem;
419
431
  }
@@ -454,12 +466,12 @@
454
466
  .squiz-rb-scope .ml-1 {
455
467
  margin-left: 0.25rem;
456
468
  }
469
+ .squiz-rb-scope .ml-4 {
470
+ margin-left: 1rem;
471
+ }
457
472
  .squiz-rb-scope .ml-auto {
458
473
  margin-left: auto;
459
474
  }
460
- .squiz-rb-scope .mr-1 {
461
- margin-right: 0.25rem;
462
- }
463
475
  .squiz-rb-scope .mr-2 {
464
476
  margin-right: 0.5rem;
465
477
  }
@@ -478,6 +490,9 @@
478
490
  .squiz-rb-scope .mr-7 {
479
491
  margin-right: 1.75rem;
480
492
  }
493
+ .squiz-rb-scope .mr-auto {
494
+ margin-right: auto;
495
+ }
481
496
  .squiz-rb-scope .mt-2 {
482
497
  margin-top: 0.5rem;
483
498
  }
@@ -511,6 +526,9 @@
511
526
  .squiz-rb-scope .hidden {
512
527
  display: none;
513
528
  }
529
+ .squiz-rb-scope .\!h-4 {
530
+ height: 1rem !important;
531
+ }
514
532
  .squiz-rb-scope .h-14 {
515
533
  height: 3.5rem;
516
534
  }
@@ -547,6 +565,9 @@
547
565
  .squiz-rb-scope .min-h-full {
548
566
  min-height: 100%;
549
567
  }
568
+ .squiz-rb-scope .\!w-4 {
569
+ width: 1rem !important;
570
+ }
550
571
  .squiz-rb-scope .w-1\/2 {
551
572
  width: 50%;
552
573
  }
@@ -580,6 +601,9 @@
580
601
  .squiz-rb-scope .w-\[119px\] {
581
602
  width: 119px;
582
603
  }
604
+ .squiz-rb-scope .w-\[400px\] {
605
+ width: 400px;
606
+ }
583
607
  .squiz-rb-scope .w-\[60px\] {
584
608
  width: 60px;
585
609
  }
@@ -640,9 +664,18 @@
640
664
  .squiz-rb-scope .justify-center {
641
665
  justify-content: center;
642
666
  }
667
+ .squiz-rb-scope .gap-1 {
668
+ gap: 0.25rem;
669
+ }
643
670
  .squiz-rb-scope .gap-3 {
644
671
  gap: 0.75rem;
645
672
  }
673
+ .squiz-rb-scope .self-center {
674
+ align-self: center;
675
+ }
676
+ .squiz-rb-scope .justify-self-start {
677
+ justify-self: start;
678
+ }
646
679
  .squiz-rb-scope .overflow-y-scroll {
647
680
  overflow-y: scroll;
648
681
  }
@@ -721,6 +754,10 @@
721
754
  --tw-bg-opacity: 1;
722
755
  background-color: rgb(237 237 237 / var(--tw-bg-opacity));
723
756
  }
757
+ .squiz-rb-scope .bg-gray-300 {
758
+ --tw-bg-opacity: 1;
759
+ background-color: rgb(224 224 224 / var(--tw-bg-opacity));
760
+ }
724
761
  .squiz-rb-scope .bg-white {
725
762
  --tw-bg-opacity: 1;
726
763
  background-color: rgb(255 255 255 / var(--tw-bg-opacity));
@@ -743,6 +780,14 @@
743
780
  .squiz-rb-scope .p-6 {
744
781
  padding: 1.5rem;
745
782
  }
783
+ .squiz-rb-scope .px-1 {
784
+ padding-left: 0.25rem;
785
+ padding-right: 0.25rem;
786
+ }
787
+ .squiz-rb-scope .px-1\.5 {
788
+ padding-left: 0.375rem;
789
+ padding-right: 0.375rem;
790
+ }
746
791
  .squiz-rb-scope .px-2 {
747
792
  padding-left: 0.5rem;
748
793
  padding-right: 0.5rem;
@@ -805,6 +850,9 @@
805
850
  .squiz-rb-scope .font-semibold {
806
851
  font-weight: 600;
807
852
  }
853
+ .squiz-rb-scope .leading-4 {
854
+ line-height: 1rem;
855
+ }
808
856
  .squiz-rb-scope .leading-5 {
809
857
  line-height: 1.25rem;
810
858
  }
@@ -819,6 +867,10 @@
819
867
  --tw-text-opacity: 1;
820
868
  color: rgb(4 73 133 / var(--tw-text-opacity));
821
869
  }
870
+ .squiz-rb-scope .text-gray-500 {
871
+ --tw-text-opacity: 1;
872
+ color: rgb(148 148 148 / var(--tw-text-opacity));
873
+ }
822
874
  .squiz-rb-scope .text-gray-600 {
823
875
  --tw-text-opacity: 1;
824
876
  color: rgb(112 112 112 / var(--tw-text-opacity));
@@ -831,6 +883,10 @@
831
883
  --tw-text-opacity: 1;
832
884
  color: rgb(61 61 61 / var(--tw-text-opacity));
833
885
  }
886
+ .squiz-rb-scope .text-red-300 {
887
+ --tw-text-opacity: 1;
888
+ color: rgb(215 35 33 / var(--tw-text-opacity));
889
+ }
834
890
  .squiz-rb-scope .text-white {
835
891
  --tw-text-opacity: 1;
836
892
  color: rgb(255 255 255 / var(--tw-text-opacity));
@@ -869,7 +925,6 @@
869
925
  }
870
926
  }
871
927
  .squiz-rb-scope .spinner__wrapper {
872
- margin: 0.75rem;
873
928
  display: flex;
874
929
  align-items: center;
875
930
  justify-content: center;
@@ -918,6 +973,35 @@
918
973
  .squiz-rb-scope .resource-breadcrumb--expanded .resource-breadcrumb__item:last-child {
919
974
  display: flex;
920
975
  }
976
+ .squiz-rb-scope .resource-picker {
977
+ display: grid;
978
+ grid-template-columns: 24px 1fr;
979
+ gap: 0.5rem;
980
+ border-width: 2px;
981
+ --tw-border-opacity: 1;
982
+ border-color: rgb(224 224 224 / var(--tw-border-opacity));
983
+ border-radius: 4px;
984
+ padding: 0.5rem;
985
+ --tw-text-opacity: 1;
986
+ color: rgb(148 148 148 / var(--tw-text-opacity));
987
+ }
988
+ .squiz-rb-scope .resource-picker-info {
989
+ width: 100%;
990
+ border-radius: 6px;
991
+ --tw-bg-opacity: 1;
992
+ background-color: rgb(245 245 245 / var(--tw-bg-opacity));
993
+ padding: 0.5rem;
994
+ font-size: 0.875rem;
995
+ font-weight: 600;
996
+ --tw-text-opacity: 1;
997
+ color: rgb(43 43 43 / var(--tw-text-opacity));
998
+ }
999
+ .squiz-rb-scope .resource-picker-info__layout {
1000
+ display: grid;
1001
+ grid-template-columns: 24px 1fr 24px;
1002
+ gap: 0.5rem;
1003
+ justify-items: center;
1004
+ }
921
1005
  .squiz-rb-scope .before\:fixed::before {
922
1006
  content: var(--tw-content);
923
1007
  position: fixed;
@@ -1027,6 +1111,13 @@
1027
1111
  --tw-bg-opacity: 1;
1028
1112
  background-color: rgb(245 245 245 / var(--tw-bg-opacity));
1029
1113
  }
1114
+ .squiz-rb-scope .hover\:bg-transparent:hover {
1115
+ background-color: transparent;
1116
+ }
1117
+ .squiz-rb-scope .hover\:text-gray-800:hover {
1118
+ --tw-text-opacity: 1;
1119
+ color: rgb(61 61 61 / var(--tw-text-opacity));
1120
+ }
1030
1121
  .squiz-rb-scope .focus\:bg-blue-100:focus {
1031
1122
  --tw-bg-opacity: 1;
1032
1123
  background-color: rgb(230 241 250 / var(--tw-bg-opacity));
@@ -1035,6 +1126,21 @@
1035
1126
  --tw-bg-opacity: 1;
1036
1127
  background-color: rgb(245 245 245 / var(--tw-bg-opacity));
1037
1128
  }
1129
+ .squiz-rb-scope .focus\:text-gray-800:focus {
1130
+ --tw-text-opacity: 1;
1131
+ color: rgb(61 61 61 / var(--tw-text-opacity));
1132
+ }
1133
+ .squiz-rb-scope .focus\:outline-none:focus {
1134
+ outline: 2px solid transparent;
1135
+ outline-offset: 2px;
1136
+ }
1137
+ .squiz-rb-scope .disabled\:cursor-not-allowed:disabled {
1138
+ cursor: not-allowed;
1139
+ }
1140
+ .squiz-rb-scope .disabled\:text-gray-500:disabled {
1141
+ --tw-text-opacity: 1;
1142
+ color: rgb(148 148 148 / var(--tw-text-opacity));
1143
+ }
1038
1144
  @media not all and (min-width: 768px) {
1039
1145
  .squiz-rb-scope .max-md\:hidden {
1040
1146
  display: none;
package/lib/index.d.ts CHANGED
@@ -1,14 +1,11 @@
1
- import React from 'react';
2
1
  import { HydratedResourceReference, Resource, ResourceReference, Source } from './types';
3
2
  export type { HydratedResourceReference, Resource, ResourceReference, Source };
4
- export default function ComponentEditorContentBrowser({ showButtonLabel, buttonLabel, buttonIcon, modalTitle, allowedTypes, onRequestSources, onRequestChildren, onChange, }: {
5
- showButtonLabel?: boolean;
6
- buttonLabel: string;
7
- buttonIcon: React.ReactNode;
3
+ export default function ComponentEditorContentBrowser({ modalTitle, allowedTypes, onRequestSources, onRequestChildren, onChange, isDisabled, }: {
8
4
  modalTitle: string;
9
5
  allowedTypes: string[] | undefined;
10
6
  onRequestSources: () => Promise<Source[]>;
11
7
  onRequestChildren(source: Source, resource: Resource | null): Promise<Resource[]>;
12
8
  onRequestResource(reference: ResourceReference): Promise<Resource | null>;
13
9
  onChange(resource: HydratedResourceReference | null): void;
10
+ isDisabled?: boolean;
14
11
  }): JSX.Element;
package/lib/index.js CHANGED
@@ -6,9 +6,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
7
  const ModalTrigger_1 = __importDefault(require("./Modal/ModalTrigger"));
8
8
  const ResourcePickerContainer_1 = __importDefault(require("./ResourcePickerContainer/ResourcePickerContainer"));
9
- function ComponentEditorContentBrowser({ showButtonLabel, buttonLabel, buttonIcon, modalTitle, allowedTypes, onRequestSources, onRequestChildren, onChange, }) {
10
- const showLabel = showButtonLabel || false;
9
+ const AdsClickRounded_1 = __importDefault(require("@mui/icons-material/AdsClickRounded"));
10
+ const AddCircleOutlineRounded_1 = __importDefault(require("@mui/icons-material/AddCircleOutlineRounded"));
11
+ const PhotoLibraryRounded_1 = __importDefault(require("@mui/icons-material/PhotoLibraryRounded"));
12
+ const clsx_1 = __importDefault(require("clsx"));
13
+ function ComponentEditorContentBrowser({ modalTitle, allowedTypes, onRequestSources, onRequestChildren, onChange, isDisabled, }) {
14
+ const isImagePicker = allowedTypes && allowedTypes.length === 1 && allowedTypes.includes('image');
11
15
  return (react_1.default.createElement("div", { className: "squiz-rb-scope" },
12
- react_1.default.createElement(ModalTrigger_1.default, { showLabel: showLabel, label: buttonLabel, icon: buttonIcon }, (onClose, titleProps) => (react_1.default.createElement(ResourcePickerContainer_1.default, { title: modalTitle, titleAriaProps: titleProps, allowedTypes: allowedTypes, onClose: onClose, onRequestSources: onRequestSources, onRequestChildren: onRequestChildren, onChange: onChange })))));
16
+ react_1.default.createElement("div", { className: (0, clsx_1.default)('resource-picker', isDisabled && 'bg-gray-300') },
17
+ isImagePicker ? (react_1.default.createElement(PhotoLibraryRounded_1.default, { "aria-hidden": true, className: "w-6 h-6" })) : (react_1.default.createElement(AdsClickRounded_1.default, { "aria-hidden": true, className: "w-6 h-6" })),
18
+ react_1.default.createElement(ModalTrigger_1.default, { showLabel: true, label: isImagePicker ? `Choose image` : `Choose asset`, icon: react_1.default.createElement(AddCircleOutlineRounded_1.default, { "aria-hidden": true, className: "!w-4 !h-4" }), isDisabled: isDisabled }, (onClose, titleProps) => (react_1.default.createElement(ResourcePickerContainer_1.default, { title: modalTitle, titleAriaProps: titleProps, allowedTypes: allowedTypes, onClose: onClose, onRequestSources: onRequestSources, onRequestChildren: onRequestChildren, onChange: onChange }))))));
13
19
  }
14
20
  exports.default = ComponentEditorContentBrowser;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/resource-browser",
3
- "version": "1.32.1-alpha.27",
3
+ "version": "1.32.1-alpha.29",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "scripts": {
@@ -14,7 +14,9 @@
14
14
  "clean": "rimraf ./lib"
15
15
  },
16
16
  "dependencies": {
17
- "@squiz/dx-json-schema-lib": "1.32.1-alpha.27",
17
+ "@mui/icons-material": "5.11.16",
18
+ "@squiz/dx-json-schema-lib": "1.32.1-alpha.29",
19
+ "pretty-bytes": "5.6.0",
18
20
  "react-aria": "3.23.1",
19
21
  "react-responsive": "9.0.2",
20
22
  "react-stately": "3.21.0"
@@ -71,5 +73,5 @@
71
73
  "volta": {
72
74
  "node": "18.15.0"
73
75
  },
74
- "gitHead": "940727a9ceebc91e6d1ab09e3ee51ae1db78e253"
76
+ "gitHead": "dd5931dd752cb79620662dc260e8c1df96fc63b1"
75
77
  }
@@ -10,6 +10,11 @@ export default {
10
10
 
11
11
  const Template: StoryFn<typeof Icon> = (props) => <Icon {...props} />;
12
12
 
13
+ export const Error = Template.bind({});
14
+ Error.args = {
15
+ icon: 'error',
16
+ };
17
+
13
18
  export const ArrowRight = Template.bind({});
14
19
  ArrowRight.args = {
15
20
  icon: 'arrow-right',
@@ -142,6 +142,31 @@ describe('Modal', () => {
142
142
  });
143
143
  });
144
144
 
145
+ it('Modal does not open if modal trigger button is disabled', async () => {
146
+ render(
147
+ <div>
148
+ <ModalTrigger label={'Open testing modal'} showLabel isDisabled={true}>
149
+ {(onClose, titleProps) => (
150
+ <div data-testid="modal">
151
+ <div {...titleProps}>Testing</div>
152
+ <button type="button" onClick={onClose}>
153
+ Close
154
+ </button>
155
+ </div>
156
+ )}
157
+ </ModalTrigger>
158
+ <div style={{ height: '150vh' }} />
159
+ </div>,
160
+ );
161
+
162
+ const user = userEvent.setup();
163
+ user.click(screen.getByText('Open testing modal'));
164
+
165
+ await waitFor(() => {
166
+ expect(screen.queryByTestId('modal')).not.toBeTruthy();
167
+ });
168
+ });
169
+
145
170
  it('Focus is trapped within modal', async () => {
146
171
  render(
147
172
  <div>
@@ -6,17 +6,20 @@ import { DOMAttributes, FocusableElement } from '@react-types/shared';
6
6
 
7
7
  import Modal from './Modal';
8
8
  import ModalOpeningButton from './ModalOpeningButton';
9
+ import clsx from 'clsx';
9
10
 
10
11
  function ModalTrigger({
11
12
  label,
12
13
  showLabel,
13
14
  icon,
15
+ isDisabled,
14
16
  children,
15
17
  ...props
16
18
  }: {
17
19
  label: string;
18
20
  showLabel?: boolean;
19
21
  icon?: React.ReactNode;
22
+ isDisabled?: boolean;
20
23
  children: (onClose: () => void, titleProps: DOMAttributes<FocusableElement>) => React.ReactElement; // Child needs to be a functions which generates the 'content' so we can pass the onClose function and title aria props
21
24
  }) {
22
25
  const state = useOverlayTriggerState(props);
@@ -29,9 +32,18 @@ function ModalTrigger({
29
32
 
30
33
  return (
31
34
  <>
32
- <ModalOpeningButton type="button" {...triggerProps} {...ariaAttr}>
33
- {showLabel && label}
35
+ <ModalOpeningButton
36
+ type="button"
37
+ {...triggerProps}
38
+ {...ariaAttr}
39
+ isDisabled={isDisabled}
40
+ className={clsx(
41
+ 'flex p-1 px-1.5 rounded mr-auto text-blue-300 hover:bg-blue-100 focus:bg-blue-100 focus:outline-none',
42
+ isDisabled && 'hover:bg-transparent cursor-not-allowed text-gray-600',
43
+ )}
44
+ >
34
45
  {icon}
46
+ {showLabel && <span className="ml-1 text-sm leading-4">{label}</span>}
35
47
  </ModalOpeningButton>
36
48
  {state.isOpen && (
37
49
  <Modal isDismissable state={state} overlayProps={overlayProps}>
@@ -2,30 +2,13 @@ import React from 'react';
2
2
 
3
3
  import { Resource } from '../../types';
4
4
  import Icon, { IconOptions } from '../../Icons/Icon';
5
-
6
- const statusColour = {
7
- // Duplicated from the Matrix repository.
8
- // src/Api/AssetManagementApi/Constants/AssetStatuses.php - contains a list of possible statuses.
9
- // frontend/src/styles/common/status-colors.scss - contains the colours used for those statuses in Matrix.
10
- unknown: '#ff0000',
11
- archived: '#c98a67',
12
- under_construction: '#94d1f9',
13
- pending_approval: '#d0bbf0',
14
- approved_to_go_live: '#f7eaa2',
15
- live: '#bfe60a',
16
- up_for_review: '#72cd32',
17
- safe_editing: '#ff97b0',
18
- safe_editing_pending_approval: '#d688db',
19
- safe_edit_approved_to_go_live: '#ffb34a',
20
- };
5
+ import StatusIndicator from '../../StatusIndicator/StatusIndicator';
21
6
 
22
7
  type MatrixResourceProps = {
23
8
  resource: Resource;
24
9
  };
25
10
 
26
11
  const MatrixResource = ({ resource: { id, type, name, status } }: MatrixResourceProps) => {
27
- const color = statusColour[status.code as keyof typeof statusColour] || statusColour.unknown;
28
-
29
12
  return (
30
13
  <div>
31
14
  <div className="flex flex-col items-center text-gray-800 mt-7 mx-5 pb-4 border-b border-gray-300">
@@ -47,10 +30,7 @@ const MatrixResource = ({ resource: { id, type, name, status } }: MatrixResource
47
30
  <div className="flex mb-2">
48
31
  <dt className="w-[60px] mr-4 text-gray-600">Status</dt>
49
32
  <dd className="flex items-center font-semibold">
50
- <span
51
- style={{ backgroundColor: color }}
52
- className="block rounded-full w-3 h-3 mr-1 border border-solid border-black border-opacity-20"
53
- ></span>
33
+ <StatusIndicator status={status} />
54
34
  {status.name}
55
35
  </dd>
56
36
  </div>
@@ -8,10 +8,6 @@ const defaultProps: any = {
8
8
  };
9
9
 
10
10
  describe('ResourceError', () => {
11
- afterEach(() => {
12
- jest.clearAllMocks();
13
- });
14
-
15
11
  it('should render the component with the correct error message', () => {
16
12
  const { getByText } = render(<ResourceError {...defaultProps} />);
17
13
  const errorMessage = getByText(defaultProps.errorMessage);
@@ -19,10 +19,6 @@ const defaultProps: any = {
19
19
  };
20
20
 
21
21
  describe('ResourceItem', () => {
22
- afterEach(() => {
23
- jest.clearAllMocks();
24
- });
25
-
26
22
  it('should render the component with the correct label', () => {
27
23
  const { getByText } = render(<ResourceItem {...defaultProps} />);
28
24
  const labelElement = getByText('My Resource');
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
3
+
4
+ export const ResetButton = ({ isDisabled }: { isDisabled: boolean }) => (
5
+ <button
6
+ type="button"
7
+ aria-label={`Remove selection`}
8
+ title={`Remove selection`}
9
+ className="text-gray-500 hover:text-gray-800 focus:text-gray-800 w-6 h-6 disabled:text-gray-500 disabled:cursor-not-allowed"
10
+ disabled={isDisabled}
11
+ >
12
+ <CloseRoundedIcon />
13
+ </button>
14
+ );