@squiz/formatted-text-editor 1.33.1-alpha.2 → 1.33.1-alpha.4

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 (79) hide show
  1. package/demo/App.tsx +4 -24
  2. package/demo/AppContext.tsx +28 -0
  3. package/demo/index.scss +0 -2
  4. package/demo/main.tsx +2 -0
  5. package/demo/resources.json +28 -0
  6. package/demo/sources.json +23 -0
  7. package/lib/Editor/EditorContext.d.ts +0 -7
  8. package/lib/Editor/EditorContext.js +0 -2
  9. package/lib/EditorToolbar/Tools/Image/Form/ImageForm.js +16 -32
  10. package/lib/EditorToolbar/Tools/Image/ImageModal.js +3 -2
  11. package/lib/EditorToolbar/Tools/Link/Form/LinkForm.js +18 -58
  12. package/lib/EditorToolbar/Tools/Link/LinkModal.js +3 -2
  13. package/lib/EditorToolbar/Tools/Link/RemoveLinkButton.js +1 -1
  14. package/lib/Extensions/Extensions.js +0 -2
  15. package/lib/Extensions/ImageExtension/AssetImageExtension.d.ts +0 -1
  16. package/lib/Extensions/ImageExtension/AssetImageExtension.js +1 -2
  17. package/lib/Extensions/LinkExtension/AssetLinkExtension.d.ts +0 -1
  18. package/lib/Extensions/LinkExtension/AssetLinkExtension.js +2 -3
  19. package/lib/Extensions/LinkExtension/LinkExtension.js +1 -1
  20. package/lib/index.css +84 -4
  21. package/lib/types.d.ts +3 -3
  22. package/lib/ui/Fields/Checkbox/Checkbox.d.ts +8 -0
  23. package/lib/ui/Fields/Checkbox/Checkbox.js +47 -0
  24. package/lib/ui/Fields/Input/Input.d.ts +2 -4
  25. package/lib/ui/Fields/Input/Input.js +3 -9
  26. package/lib/ui/Fields/InputContainer/InputContainer.d.ts +9 -0
  27. package/lib/ui/Fields/InputContainer/InputContainer.js +16 -0
  28. package/lib/ui/Fields/MatrixAsset/MatrixAsset.d.ts +17 -0
  29. package/lib/ui/Fields/MatrixAsset/MatrixAsset.js +29 -0
  30. package/lib/ui/Modal/Modal.d.ts +1 -0
  31. package/lib/ui/Modal/Modal.js +3 -2
  32. package/lib/ui/Tabs/Tabs.d.ts +10 -0
  33. package/lib/ui/Tabs/Tabs.js +46 -0
  34. package/lib/utils/validation.d.ts +2 -1
  35. package/lib/utils/validation.js +8 -2
  36. package/package.json +4 -3
  37. package/src/Editor/Editor.spec.tsx +1 -1
  38. package/src/Editor/EditorContext.spec.tsx +11 -13
  39. package/src/Editor/EditorContext.ts +0 -11
  40. package/src/EditorToolbar/Tools/Image/Form/ImageForm.spec.tsx +29 -12
  41. package/src/EditorToolbar/Tools/Image/Form/ImageForm.tsx +37 -53
  42. package/src/EditorToolbar/Tools/Image/ImageButton.spec.tsx +76 -49
  43. package/src/EditorToolbar/Tools/Image/ImageModal.spec.tsx +1 -0
  44. package/src/EditorToolbar/Tools/Image/ImageModal.tsx +3 -2
  45. package/src/EditorToolbar/Tools/Link/Form/LinkForm.spec.tsx +22 -13
  46. package/src/EditorToolbar/Tools/Link/Form/LinkForm.tsx +35 -57
  47. package/src/EditorToolbar/Tools/Link/LinkButton.spec.tsx +52 -36
  48. package/src/EditorToolbar/Tools/Link/LinkModal.tsx +3 -2
  49. package/src/EditorToolbar/Tools/Link/RemoveLinkButton.spec.tsx +47 -4
  50. package/src/EditorToolbar/Tools/Link/RemoveLinkButton.tsx +3 -2
  51. package/src/Extensions/Extensions.ts +0 -2
  52. package/src/Extensions/ImageExtension/AssetImageExtension.ts +1 -3
  53. package/src/Extensions/LinkExtension/AssetLinkExtension.ts +2 -4
  54. package/src/Extensions/LinkExtension/LinkExtension.ts +1 -1
  55. package/src/index.scss +1 -0
  56. package/src/types.ts +7 -5
  57. package/src/ui/Fields/Checkbox/Checkbox.spec.tsx +50 -0
  58. package/src/ui/Fields/Checkbox/Checkbox.tsx +49 -0
  59. package/src/ui/Fields/Checkbox/_checkbox.scss +26 -0
  60. package/src/ui/Fields/Input/Input.tsx +4 -18
  61. package/src/ui/Fields/InputContainer/InputContainer.spec.tsx +18 -0
  62. package/src/ui/Fields/InputContainer/InputContainer.tsx +29 -0
  63. package/src/ui/Fields/MatrixAsset/MatrixAsset.spec.tsx +103 -0
  64. package/src/ui/Fields/MatrixAsset/MatrixAsset.tsx +55 -0
  65. package/src/ui/Modal/FormModal.spec.tsx +2 -1
  66. package/src/ui/Modal/Modal.spec.tsx +15 -7
  67. package/src/ui/Modal/Modal.tsx +4 -2
  68. package/src/ui/Tabs/Tabs.spec.tsx +44 -0
  69. package/src/ui/Tabs/Tabs.tsx +41 -0
  70. package/src/ui/_forms.scss +4 -2
  71. package/src/utils/validation.spec.ts +22 -0
  72. package/src/utils/validation.ts +9 -1
  73. package/tests/index.ts +2 -0
  74. package/tests/mockResourceBrowserContext.tsx +63 -0
  75. package/tests/renderWithContext.tsx +18 -0
  76. package/tests/renderWithEditor.tsx +18 -21
  77. package/vite.config.ts +8 -0
  78. package/lib/ui/Fields/Select/Select.d.ts +0 -12
  79. package/lib/ui/Fields/Select/Select.js +0 -53
package/lib/index.css CHANGED
@@ -404,18 +404,33 @@
404
404
  .squiz-fte-scope .mb-2 {
405
405
  margin-bottom: 0.5rem !important;
406
406
  }
407
+ .squiz-fte-scope .mb-4 {
408
+ margin-bottom: 1rem !important;
409
+ }
407
410
  .squiz-fte-scope .ml-3 {
408
411
  margin-left: 0.75rem !important;
409
412
  }
410
413
  .squiz-fte-scope .ml-auto {
411
414
  margin-left: auto !important;
412
415
  }
416
+ .squiz-fte-scope .mr-1 {
417
+ margin-right: 0.25rem !important;
418
+ }
419
+ .squiz-fte-scope .mr-1\.5 {
420
+ margin-right: 0.375rem !important;
421
+ }
413
422
  .squiz-fte-scope .mr-2 {
414
423
  margin-right: 0.5rem !important;
415
424
  }
416
425
  .squiz-fte-scope .mt-1 {
417
426
  margin-top: 0.25rem !important;
418
427
  }
428
+ .squiz-fte-scope .mt-\[-1px\] {
429
+ margin-top: -1px !important;
430
+ }
431
+ .squiz-fte-scope .mt-\[7px\] {
432
+ margin-top: 7px !important;
433
+ }
419
434
  .squiz-fte-scope .block {
420
435
  display: block !important;
421
436
  }
@@ -425,9 +440,21 @@
425
440
  .squiz-fte-scope .flex {
426
441
  display: flex !important;
427
442
  }
443
+ .squiz-fte-scope .grid {
444
+ display: grid !important;
445
+ }
428
446
  .squiz-fte-scope .hidden {
429
447
  display: none !important;
430
448
  }
449
+ .squiz-fte-scope .h-10 {
450
+ height: 2.5rem !important;
451
+ }
452
+ .squiz-fte-scope .h-\[3px\] {
453
+ height: 3px !important;
454
+ }
455
+ .squiz-fte-scope .w-11\/12 {
456
+ width: 91.666667% !important;
457
+ }
431
458
  .squiz-fte-scope .w-169 {
432
459
  width: 169px !important;
433
460
  }
@@ -445,17 +472,29 @@
445
472
  -moz-user-select: none !important;
446
473
  user-select: none !important;
447
474
  }
475
+ .squiz-fte-scope .grid-flow-col {
476
+ grid-auto-flow: column !important;
477
+ }
448
478
  .squiz-fte-scope .flex-row {
449
479
  flex-direction: row !important;
450
480
  }
481
+ .squiz-fte-scope .flex-col {
482
+ flex-direction: column !important;
483
+ }
451
484
  .squiz-fte-scope .items-center {
452
485
  align-items: center !important;
453
486
  }
487
+ .squiz-fte-scope .justify-between {
488
+ justify-content: space-between !important;
489
+ }
454
490
  .squiz-fte-scope .divide-y > :not([hidden]) ~ :not([hidden]) {
455
491
  --tw-divide-y-reverse: 0 !important;
456
492
  border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))) !important;
457
493
  border-bottom-width: calc(1px * var(--tw-divide-y-reverse)) !important;
458
494
  }
495
+ .squiz-fte-scope .self-center {
496
+ align-self: center !important;
497
+ }
459
498
  .squiz-fte-scope .overflow-auto {
460
499
  overflow: auto !important;
461
500
  }
@@ -467,12 +506,19 @@
467
506
  .squiz-fte-scope .rounded {
468
507
  border-radius: 4px !important;
469
508
  }
509
+ .squiz-fte-scope .rounded-t-sm {
510
+ border-top-left-radius: 0.125rem !important;
511
+ border-top-right-radius: 0.125rem !important;
512
+ }
470
513
  .squiz-fte-scope .border-0 {
471
514
  border-width: 0px !important;
472
515
  }
473
516
  .squiz-fte-scope .border-2 {
474
517
  border-width: 2px !important;
475
518
  }
519
+ .squiz-fte-scope .border-b {
520
+ border-bottom-width: 1px !important;
521
+ }
476
522
  .squiz-fte-scope .border-gray-300 {
477
523
  --tw-border-opacity: 1 !important;
478
524
  border-color: rgb(224 224 224 / var(--tw-border-opacity)) !important;
@@ -493,6 +539,10 @@
493
539
  --tw-bg-opacity: 1 !important;
494
540
  background-color: rgb(237 237 237 / var(--tw-bg-opacity)) !important;
495
541
  }
542
+ .squiz-fte-scope .bg-gray-800 {
543
+ --tw-bg-opacity: 1 !important;
544
+ background-color: rgb(61 61 61 / var(--tw-bg-opacity)) !important;
545
+ }
496
546
  .squiz-fte-scope .bg-transparent {
497
547
  background-color: transparent !important;
498
548
  }
@@ -511,8 +561,8 @@
511
561
  padding-top: 0.5rem !important;
512
562
  padding-bottom: 0.5rem !important;
513
563
  }
514
- .squiz-fte-scope .pb-2 {
515
- padding-bottom: 0.5rem !important;
564
+ .squiz-fte-scope .pb-4 {
565
+ padding-bottom: 1rem !important;
516
566
  }
517
567
  .squiz-fte-scope .pl-3 {
518
568
  padding-left: 0.75rem !important;
@@ -533,6 +583,9 @@
533
583
  .squiz-fte-scope .text-md {
534
584
  font-size: 0.875rem !important;
535
585
  }
586
+ .squiz-fte-scope .font-bold {
587
+ font-weight: 700 !important;
588
+ }
536
589
  .squiz-fte-scope .font-semibold {
537
590
  font-weight: 600 !important;
538
591
  }
@@ -577,6 +630,9 @@
577
630
  --tw-blur: blur(8px) !important;
578
631
  filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow) !important;
579
632
  }
633
+ .squiz-fte-scope .filter {
634
+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow) !important;
635
+ }
580
636
  .squiz-fte-scope a {
581
637
  --tw-text-opacity: 1;
582
638
  color: rgb(7 116 210 / var(--tw-text-opacity));
@@ -630,6 +686,7 @@
630
686
  color: rgb(112 112 112 / var(--tw-text-opacity));
631
687
  }
632
688
  .squiz-fte-scope .squiz-fte-form-control {
689
+ height: 36px;
633
690
  padding: 6px 12px;
634
691
  position: relative;
635
692
  width: 100%;
@@ -663,13 +720,14 @@
663
720
  .squiz-fte-scope .squiz-fte-form-control:active {
664
721
  box-shadow: none;
665
722
  }
666
- .squiz-fte-scope .squiz-fte-invalid-form-field .squiz-fte-form-control {
723
+ .squiz-fte-scope .squiz-fte-invalid-form-field .squiz-fte-form-control,
724
+ .squiz-fte-scope .squiz-fte-invalid-form-field .resource-picker {
667
725
  --tw-border-opacity: 1;
668
726
  border-color: rgb(215 35 33 / var(--tw-border-opacity));
669
727
  background-repeat: no-repeat;
670
728
  padding-right: 2rem;
671
729
  background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0ibm9uZSIvPjxnIGNsYXNzPSJjdXJyZW50TGF5ZXIiPjxwYXRoIGQ9Ik00LjQ3IDIxaDE1LjA2YzEuNTQgMCAyLjUtMS42NyAxLjczLTNMMTMuNzMgNC45OWMtLjc3LTEuMzMtMi42OS0xLjMzLTMuNDYgMEwyLjc0IDE4Yy0uNzcgMS4zMy4xOSAzIDEuNzMgM3pNMTIgMTRjLS41NSAwLTEtLjQ1LTEtMXYtMmMwLS41NS40NS0xIDEtMXMxIC40NSAxIDF2MmMwIC41NS0uNDUgMS0xIDF6bTEgNGgtMnYtMmgydjJ6IiBjbGFzcz0ic2VsZWN0ZWQiIGZpbGw9IiNkNzIzMjEiLz48L2c+PC9zdmc+);
672
- background-position: top 0.25rem right 0.25rem;
730
+ background-position: center right 0.25rem;
673
731
  background-size: 1.5rem;
674
732
  }
675
733
  .squiz-fte-scope .squiz-fte-form-error {
@@ -901,6 +959,28 @@
901
959
  .squiz-fte-scope .dropdown-button:focus {
902
960
  background-color: rgba(0, 0, 0, 0.04);
903
961
  }
962
+ .squiz-fte-scope .squiz-fte-checkbox {
963
+ --tw-text-opacity: 1;
964
+ color: rgb(61 61 61 / var(--tw-text-opacity));
965
+ font-size: 14px;
966
+ display: flex;
967
+ align-items: center;
968
+ margin-top: 0.75rem;
969
+ gap: 0.75rem;
970
+ }
971
+ .squiz-fte-scope .squiz-fte-checkbox .checkbox {
972
+ display: flex;
973
+ justify-content: center;
974
+ align-items: center;
975
+ width: 1.25rem;
976
+ height: 1.25rem;
977
+ background-color: #fff;
978
+ border: 2px solid #e0e0e0;
979
+ border-radius: 4px;
980
+ }
981
+ .squiz-fte-scope .squiz-fte-checkbox .checkbox svg {
982
+ width: 100%;
983
+ }
904
984
  .squiz-fte-scope .squiz-fte-modal {
905
985
  display: flex;
906
986
  width: 100%;
package/lib/types.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export type DeepPartial<T> = T extends Record<string, unknown> ? {
2
- [P in keyof T]?: DeepPartial<T[P]>;
3
- } : T;
1
+ export type DeepPartial<T> = {
2
+ [P in keyof T]?: T[P] extends Array<infer U> ? Array<DeepPartial<U>> : T[P] extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : DeepPartial<T[P]>;
3
+ };
@@ -0,0 +1,8 @@
1
+ export type CheckboxProps<TChecked, TUnchecked> = {
2
+ label: string;
3
+ onChange: (value: TChecked | TUnchecked) => void;
4
+ defaultChecked?: boolean;
5
+ unchecked: TUnchecked;
6
+ checked: TChecked;
7
+ };
8
+ export declare const Checkbox: <TChecked, TUnchecked>({ label, onChange, defaultChecked, unchecked, checked, }: CheckboxProps<TChecked, TUnchecked>) => JSX.Element;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.Checkbox = void 0;
30
+ const react_1 = __importStar(require("react"));
31
+ const CheckRounded_1 = __importDefault(require("@mui/icons-material/CheckRounded"));
32
+ const Checkbox = ({ label, onChange, defaultChecked = false, unchecked, checked, }) => {
33
+ const [toggled, setToggled] = (0, react_1.useState)(defaultChecked);
34
+ (0, react_1.useEffect)(() => {
35
+ if (toggled) {
36
+ onChange(checked);
37
+ }
38
+ else {
39
+ onChange(unchecked);
40
+ }
41
+ }, [toggled]);
42
+ const toggleCheckbox = () => setToggled(!toggled);
43
+ return (react_1.default.createElement("div", { className: "squiz-fte-checkbox" },
44
+ react_1.default.createElement("button", { type: "button", role: "checkbox", "aria-label": label, "aria-checked": toggled, className: "checkbox", onClick: toggleCheckbox }, toggled && react_1.default.createElement(CheckRounded_1.default, null)),
45
+ react_1.default.createElement("button", { type: "button", className: "label", onClick: toggleCheckbox, tabIndex: -1 }, label)));
46
+ };
47
+ exports.Checkbox = Checkbox;
@@ -1,5 +1,3 @@
1
1
  import React from 'react';
2
- export declare const Input: React.ForwardRefExoticComponent<React.InputHTMLAttributes<HTMLInputElement> & {
3
- label?: string | undefined;
4
- error?: string | undefined;
5
- } & React.RefAttributes<HTMLInputElement>>;
2
+ import { InputContainerProps } from '../InputContainer/InputContainer';
3
+ export declare const Input: React.ForwardRefExoticComponent<React.InputHTMLAttributes<HTMLInputElement> & Omit<InputContainerProps, "children"> & React.RefAttributes<HTMLInputElement>>;
@@ -22,18 +22,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
25
  Object.defineProperty(exports, "__esModule", { value: true });
29
26
  exports.Input = void 0;
30
27
  const react_1 = __importStar(require("react"));
31
- const clsx_1 = __importDefault(require("clsx"));
28
+ const InputContainer_1 = require("../InputContainer/InputContainer");
32
29
  const InputInternal = ({ name, label, type = 'text', error, required, ...rest }, ref) => {
33
- return (react_1.default.createElement("div", { className: (0, clsx_1.default)(error && 'squiz-fte-invalid-form-field') },
34
- label && (react_1.default.createElement("label", { htmlFor: name, className: "squiz-fte-form-label" }, label)),
35
- required && (react_1.default.createElement("span", { className: "text-gray-600", "aria-label": "Required field" }, "*")),
36
- react_1.default.createElement("input", { ref: ref, id: name, name: name, type: type, "aria-invalid": !!error, className: "squiz-fte-form-control", ...rest }),
37
- error && react_1.default.createElement("div", { className: "squiz-fte-form-error" }, error)));
30
+ return (react_1.default.createElement(InputContainer_1.InputContainer, { name: name, label: label, error: error, required: required },
31
+ react_1.default.createElement("input", { ref: ref, id: name, name: name, type: type, "aria-invalid": !!error, className: "squiz-fte-form-control", ...rest })));
38
32
  };
39
33
  exports.Input = (0, react_1.forwardRef)(InputInternal);
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from 'react';
2
+ export type InputContainerProps = {
3
+ name?: string;
4
+ label?: string;
5
+ error?: string;
6
+ required?: boolean;
7
+ children: ReactNode;
8
+ };
9
+ export declare const InputContainer: ({ name, label, error, required, children }: InputContainerProps) => JSX.Element;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.InputContainer = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const clsx_1 = __importDefault(require("clsx"));
9
+ const InputContainer = ({ name, label, error, required, children }) => {
10
+ return (react_1.default.createElement("div", { className: (0, clsx_1.default)(error && 'squiz-fte-invalid-form-field') },
11
+ label && (react_1.default.createElement("label", { htmlFor: name, className: "squiz-fte-form-label" }, label)),
12
+ label && required && (react_1.default.createElement("span", { className: "text-gray-600", "aria-label": "Required field" }, "*")),
13
+ children,
14
+ error && react_1.default.createElement("div", { className: "squiz-fte-form-error" }, error)));
15
+ };
16
+ exports.InputContainer = InputContainer;
@@ -0,0 +1,17 @@
1
+ import { InputContainerProps } from '../InputContainer/InputContainer';
2
+ type MatrixAssetValue = {
3
+ matrixIdentifier?: string;
4
+ matrixAssetId?: string;
5
+ };
6
+ export type MatrixAssetProps<T extends MatrixAssetValue> = Omit<InputContainerProps, 'children'> & {
7
+ modalTitle: string;
8
+ allowedTypes?: string[];
9
+ value?: T | null;
10
+ onChange: (value: {
11
+ target: {
12
+ value: T;
13
+ };
14
+ }) => void;
15
+ };
16
+ export declare const MatrixAsset: <T extends MatrixAssetValue>({ modalTitle, allowedTypes, value, onChange, ...props }: MatrixAssetProps<T>) => JSX.Element;
17
+ export {};
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MatrixAsset = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const resource_browser_1 = require("@squiz/resource-browser");
9
+ const InputContainer_1 = require("../InputContainer/InputContainer");
10
+ const MatrixAsset = ({ modalTitle, allowedTypes, value, onChange, ...props }) => {
11
+ return (react_1.default.createElement(InputContainer_1.InputContainer, { ...props },
12
+ react_1.default.createElement(resource_browser_1.ResourceBrowserInput, { modalTitle: modalTitle, allowedTypes: allowedTypes, value: value && value.matrixIdentifier && value.matrixAssetId
13
+ ? {
14
+ source: value.matrixIdentifier,
15
+ resource: value.matrixAssetId,
16
+ }
17
+ : null, onChange: (reference) => {
18
+ onChange({
19
+ target: {
20
+ value: {
21
+ ...value,
22
+ matrixIdentifier: reference?.source?.id,
23
+ matrixAssetId: reference?.resource?.id,
24
+ },
25
+ },
26
+ });
27
+ } })));
28
+ };
29
+ exports.MatrixAsset = MatrixAsset;
@@ -1,6 +1,7 @@
1
1
  import React, { ReactElement } from 'react';
2
2
  export type ModalProps = {
3
3
  title: string;
4
+ icon: ReactElement;
4
5
  children: ReactElement;
5
6
  onCancel: () => void;
6
7
  onSubmit?: () => void;
@@ -31,7 +31,7 @@ const react_dom_1 = require("react-dom");
31
31
  const CloseRounded_1 = __importDefault(require("@mui/icons-material/CloseRounded"));
32
32
  const base_1 = require("@mui/base");
33
33
  const clsx_1 = __importDefault(require("clsx"));
34
- const Modal = ({ children, title, onCancel, onSubmit, className }, ref) => {
34
+ const Modal = ({ children, title, icon, onCancel, onSubmit, className }, ref) => {
35
35
  const content = (0, react_1.useRef)(null);
36
36
  const container = (0, react_1.useMemo)(() => {
37
37
  const element = document.createElement('div');
@@ -65,7 +65,8 @@ const Modal = ({ children, title, onCancel, onSubmit, className }, ref) => {
65
65
  react_1.default.createElement("div", { ref: ref, className: (0, clsx_1.default)('squiz-fte-modal-wrapper', className), tabIndex: -1 },
66
66
  react_1.default.createElement("div", { className: "w-modal-sm my-6 mx-auto" },
67
67
  react_1.default.createElement("div", { className: "squiz-fte-modal" },
68
- react_1.default.createElement("div", { className: "squiz-fte-modal-header p-6 pb-2" },
68
+ react_1.default.createElement("div", { className: "squiz-fte-modal-header p-6 pb-4" },
69
+ react_1.default.createElement("div", { className: "squiz-fte-modal-header-icon mr-1.5 mt-[-1px]" }, icon),
69
70
  react_1.default.createElement("h2", { className: "font-semibold text-gray-900 text-heading-2" }, title),
70
71
  react_1.default.createElement("button", { type: "button", className: "ml-auto -mr-3 -mt-3 bg-transparent border-0 text-gray-600 font-semibold outline-none focus:outline-none hover:text-color-gray-800", onClick: onCancel, "aria-label": "Close" },
71
72
  react_1.default.createElement(CloseRounded_1.default, null))),
@@ -0,0 +1,10 @@
1
+ export type TabOptions = Record<string, TabOption>;
2
+ export type TabOption = {
3
+ label: string;
4
+ };
5
+ export type TabsProps = {
6
+ value: string;
7
+ options: TabOptions;
8
+ onChange?: (value: string) => void;
9
+ };
10
+ export declare const Tabs: ({ value, options, onChange }: TabsProps) => JSX.Element;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.Tabs = void 0;
30
+ const react_1 = require("@headlessui/react");
31
+ const react_2 = __importStar(require("react"));
32
+ const clsx_1 = __importDefault(require("clsx"));
33
+ const Tabs = ({ value, options, onChange }) => (react_2.default.createElement(react_1.Tab.Group
34
+ // Check what index the selected tab is, otherwise default to first tab
35
+ , {
36
+ // Check what index the selected tab is, otherwise default to first tab
37
+ defaultIndex: Object.keys(options).indexOf(value) || 0,
38
+ // Check what the selected tab key is and trigger onChange
39
+ onChange: (index) => {
40
+ const selectedTab = Object.keys(options)[index];
41
+ onChange?.(selectedTab);
42
+ } },
43
+ react_2.default.createElement(react_1.Tab.List, { className: "grid grid-flow-col h-10 border-b border-gray-300" }, Object.entries(options).map(([key, option]) => (react_2.default.createElement(react_1.Tab, { key: key, as: react_2.Fragment }, ({ selected }) => (react_2.default.createElement("div", { className: "flex flex-col justify-between", "data-testid": key, "aria-selected": selected },
44
+ react_2.default.createElement("button", { type: "button", className: (0, clsx_1.default)('mt-[7px] text-gray-800', selected && 'font-bold') }, option.label),
45
+ selected && react_2.default.createElement("span", { className: "h-[3px] bg-gray-800 w-11/12 self-center rounded-t-sm" })))))))));
46
+ exports.Tabs = Tabs;
@@ -1,2 +1,3 @@
1
- export declare const noEmptySpacesValidation: (value: string | undefined) => Promise<"Empty space is not allowed" | undefined>;
1
+ export declare const noEmptySpacesValidation: (value: string | undefined) => "Empty space is not allowed" | undefined;
2
+ export declare const hasProperties: <T>(message: string, properties: (keyof T)[]) => (value: T) => string | undefined;
2
3
  export declare const regexDataURI: RegExp;
@@ -1,10 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.regexDataURI = exports.noEmptySpacesValidation = void 0;
4
- const noEmptySpacesValidation = async (value) => {
3
+ exports.regexDataURI = exports.hasProperties = exports.noEmptySpacesValidation = void 0;
4
+ const noEmptySpacesValidation = (value) => {
5
5
  if (value && !(value.trim().length > 0)) {
6
6
  return 'Empty space is not allowed';
7
7
  }
8
8
  };
9
9
  exports.noEmptySpacesValidation = noEmptySpacesValidation;
10
+ const hasProperties = (message, properties) => (value) => {
11
+ if (!value || properties.filter((property) => value[property]).length !== properties.length) {
12
+ return message;
13
+ }
14
+ };
15
+ exports.hasProperties = hasProperties;
10
16
  exports.regexDataURI = /^data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*)$/i;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/formatted-text-editor",
3
- "version": "1.33.1-alpha.2",
3
+ "version": "1.33.1-alpha.4",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "scripts": {
@@ -20,7 +20,8 @@
20
20
  "@headlessui/react": "1.7.11",
21
21
  "@mui/icons-material": "5.11.16",
22
22
  "@remirror/react": "2.0.25",
23
- "@squiz/dx-json-schema-lib": "1.21.1-alpha.2",
23
+ "@squiz/dx-json-schema-lib": "1.33.1-alpha.4",
24
+ "@squiz/resource-browser": "1.33.1-alpha.4",
24
25
  "clsx": "1.2.1",
25
26
  "react-hook-form": "7.43.2",
26
27
  "react-image-size": "2.0.0",
@@ -74,5 +75,5 @@
74
75
  "volta": {
75
76
  "node": "18.15.0"
76
77
  },
77
- "gitHead": "593039274a41b1b39826723fc8bd3bb05224def6"
78
+ "gitHead": "00015ccc2b5c7761d3a62e8bbcdf88be15038877"
78
79
  }
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { act, fireEvent, render, screen } from '@testing-library/react';
3
3
  import Editor from './Editor';
4
4
  import '@testing-library/jest-dom';
5
- import { renderWithEditor } from '../../tests/renderWithEditor';
5
+ import { renderWithEditor } from '../../tests';
6
6
  import ImageButton from '../EditorToolbar/Tools/Image/ImageButton';
7
7
 
8
8
  describe('Formatted text editor', () => {
@@ -1,26 +1,24 @@
1
- import React, { useContext } from 'react';
2
- import { EditorContext } from './EditorContext';
1
+ import React from 'react';
2
+ import { EditorContext, EditorContextOptions } from './EditorContext';
3
3
  import { render } from '@testing-library/react';
4
4
 
5
5
  describe('EditorContext', () => {
6
- const defaultContextFn = jest.fn();
7
- const Component = () => {
8
- defaultContextFn(useContext(EditorContext));
9
- return null;
10
- };
11
-
12
6
  it('Has expected defaults', async () => {
13
- render(<Component />);
7
+ let defaultContext: EditorContextOptions | null = null;
14
8
 
15
- const defaultContext = defaultContextFn.mock.calls[0][0];
9
+ render(
10
+ <EditorContext.Consumer>
11
+ {(value) => {
12
+ defaultContext = value;
13
+ return null;
14
+ }}
15
+ </EditorContext.Consumer>,
16
+ );
16
17
 
17
18
  expect(defaultContext).toEqual({
18
19
  matrix: {
19
- resolveMatrixAsset: expect.any(Function),
20
20
  matrixDomain: '',
21
- matrixIdentifier: '',
22
21
  },
23
22
  });
24
- expect(await defaultContext.matrix.resolveMatrixAsset('fake-asset-id')).toBeNull();
25
23
  });
26
24
  });
@@ -1,25 +1,14 @@
1
1
  import React from 'react';
2
2
 
3
- export type MatrixAsset = {
4
- id: string;
5
- type: string | 'image';
6
- };
7
-
8
- export type MatrixAssetResolver = (assetId: string) => Promise<MatrixAsset | null>;
9
-
10
3
  export type EditorContextOptions = {
11
4
  matrix: {
12
- matrixIdentifier: string;
13
5
  matrixDomain: string;
14
- resolveMatrixAsset: MatrixAssetResolver;
15
6
  };
16
7
  };
17
8
 
18
9
  export const defaultEditorContext: EditorContextOptions = {
19
10
  matrix: {
20
- matrixIdentifier: '',
21
11
  matrixDomain: '',
22
- resolveMatrixAsset: () => Promise.resolve(null),
23
12
  },
24
13
  };
25
14
 
@@ -3,6 +3,7 @@ import { render, screen, act, fireEvent } from '@testing-library/react';
3
3
  import React from 'react';
4
4
  import ImageForm from './ImageForm';
5
5
  import { NodeName } from '../../../../Extensions/Extensions';
6
+ import { mockResourceBrowserContext } from '../../../../../tests';
6
7
 
7
8
  describe('Image Form', () => {
8
9
  const handleSubmit = jest.fn();
@@ -19,27 +20,43 @@ describe('Image Form', () => {
19
20
  it('Renders the form with the relevant fields for arbitrary images', () => {
20
21
  render(<ImageForm data={data} onSubmit={handleSubmit} />);
21
22
 
22
- expect(screen.getByLabelText('Type')).toHaveTextContent('External image');
23
+ expect(document.querySelector('div[data-headlessui-state="selected"]')).toHaveTextContent('From URL');
23
24
  expect(screen.getByLabelText('Source')).toHaveValue('https://httpcats.com/302.jpg');
24
25
  expect(screen.getByLabelText('Alternative description')).toHaveValue('Cat with mouse in mouth');
25
26
  expect(screen.getByLabelText('Width')).toHaveValue(1600);
26
27
  expect(screen.getByLabelText('Height')).toHaveValue(1400);
27
28
  });
28
29
 
29
- it('Renders the form with the relevant fields for asset images', () => {
30
+ it('Renders the form with the relevant fields for asset images', async () => {
31
+ const { MockResourceBrowserContext } = mockResourceBrowserContext({
32
+ sources: [{ id: 'my-source-id' }],
33
+ resources: [
34
+ {
35
+ id: '100',
36
+ name: 'My selected image',
37
+ type: {
38
+ code: 'image',
39
+ name: 'Image',
40
+ },
41
+ },
42
+ ],
43
+ });
44
+
30
45
  render(
31
- <ImageForm
32
- data={{
33
- ...data,
34
- imageType: NodeName.AssetImage,
35
- assetImage: { matrixAssetId: '100' },
36
- }}
37
- onSubmit={handleSubmit}
38
- />,
46
+ <MockResourceBrowserContext>
47
+ <ImageForm
48
+ data={{
49
+ ...data,
50
+ imageType: NodeName.AssetImage,
51
+ assetImage: { matrixAssetId: '100', matrixIdentifier: 'matrix-identifier' },
52
+ }}
53
+ onSubmit={handleSubmit}
54
+ />
55
+ </MockResourceBrowserContext>,
39
56
  );
40
57
 
41
- expect(screen.getByLabelText('Type')).toHaveTextContent('Asset image');
42
- expect(screen.getByLabelText('Asset ID')).toHaveValue('100');
58
+ expect(document.querySelector('div[data-headlessui-state="selected"]')).toHaveTextContent('From source');
59
+ expect(screen.getByText('My selected image')).toBeInTheDocument();
43
60
  });
44
61
 
45
62
  it('calculates the height when width changes and aspect ratio is locked', () => {