@frontify/guideline-blocks-settings 0.27.0 → 0.28.0

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 (214) hide show
  1. package/.eslintrc.js +1 -1
  2. package/CHANGELOG.md +15 -0
  3. package/README.md +24 -5
  4. package/package.json +46 -10
  5. package/postcss.config.js +8 -0
  6. package/setupTests.ts +13 -0
  7. package/src/components/Attachments/AttachmentItem.tsx +257 -0
  8. package/src/components/Attachments/Attachments.spec.ct.tsx +151 -0
  9. package/src/components/Attachments/Attachments.tsx +221 -0
  10. package/src/components/Attachments/index.ts +4 -0
  11. package/src/components/Attachments/types.ts +30 -0
  12. package/src/components/BlockInjectButton/BlockInjectButton.spec.ct.tsx +48 -0
  13. package/src/components/BlockInjectButton/BlockInjectButton.tsx +212 -0
  14. package/src/components/BlockInjectButton/index.ts +4 -0
  15. package/src/components/BlockInjectButton/types.ts +18 -0
  16. package/src/components/BlockItemWrapper/BlockItemWrapper.spec.ct.tsx +146 -0
  17. package/src/components/BlockItemWrapper/BlockItemWrapper.tsx +76 -0
  18. package/src/components/BlockItemWrapper/Toolbar.tsx +128 -0
  19. package/src/components/BlockItemWrapper/constants.ts +4 -0
  20. package/src/components/BlockItemWrapper/index.ts +5 -0
  21. package/src/components/BlockItemWrapper/types.ts +46 -0
  22. package/src/components/DownloadButton/DownloadButton.spec.ct.tsx +20 -0
  23. package/src/components/DownloadButton/DownloadButton.tsx +36 -0
  24. package/src/components/DownloadButton/index.ts +3 -0
  25. package/src/components/DownloadButton/types.ts +5 -0
  26. package/src/components/RichTextEditor/RichTextEditor.spec.ct.tsx +204 -0
  27. package/src/components/RichTextEditor/RichTextEditor.tsx +62 -0
  28. package/src/components/RichTextEditor/SerializedText.tsx +25 -0
  29. package/src/components/RichTextEditor/constants.ts +3 -0
  30. package/src/components/RichTextEditor/index.ts +6 -0
  31. package/src/components/RichTextEditor/pluginPresets/defaultPluginsWithLinkChooser.tsx +53 -0
  32. package/src/components/RichTextEditor/pluginPresets/index.ts +3 -0
  33. package/src/components/RichTextEditor/plugins/ButtonPlugin/ButtonMarkupElement/ButtonMarkupElementNode.tsx +74 -0
  34. package/src/components/RichTextEditor/plugins/ButtonPlugin/ButtonMarkupElement/index.ts +11 -0
  35. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/ButtonButton.tsx +20 -0
  36. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/ButtonToolbarButton.tsx +56 -0
  37. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/CustomFloatingButton.tsx +19 -0
  38. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/EditButtonModal/EditModal.tsx +42 -0
  39. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/FloatingButton.tsx +37 -0
  40. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/FloatingButtonEditButton.tsx +22 -0
  41. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/FloatingButtonUrlInput.tsx +30 -0
  42. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/InsertButtonModal/InsertButtonModal.tsx +81 -0
  43. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/InsertButtonModal/types.ts +13 -0
  44. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/InsertButtonModal/useInsertModal.ts +143 -0
  45. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/UnlinkButton.tsx +31 -0
  46. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/floatingButtonStore.ts +46 -0
  47. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/index.ts +12 -0
  48. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/useFloatingButtonEdit.ts +113 -0
  49. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/useFloatingButtonEnter.ts +21 -0
  50. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/useFloatingButtonEscape.ts +30 -0
  51. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/useFloatingButtonInsert.ts +71 -0
  52. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/FloatingButton/useVirtualFloatingButton.ts +22 -0
  53. package/src/components/RichTextEditor/plugins/ButtonPlugin/components/index.ts +3 -0
  54. package/src/components/RichTextEditor/plugins/ButtonPlugin/createButtonPlugin.ts +116 -0
  55. package/src/components/RichTextEditor/plugins/ButtonPlugin/index.ts +7 -0
  56. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/index.ts +8 -0
  57. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/insertButton.ts +17 -0
  58. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/submitFloatingButton.ts +40 -0
  59. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/unwrapButton.ts +68 -0
  60. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/upsertButton.ts +198 -0
  61. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/upsertButtonText.ts +40 -0
  62. package/src/components/RichTextEditor/plugins/ButtonPlugin/transforms/wrapButton.ts +30 -0
  63. package/src/components/RichTextEditor/plugins/ButtonPlugin/types.ts +13 -0
  64. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/createButtonNode.ts +28 -0
  65. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/getButtonStyle.ts +14 -0
  66. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/getUrl.ts +18 -0
  67. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/index.ts +8 -0
  68. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/styles.ts +77 -0
  69. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/triggerFloatingButton.ts +23 -0
  70. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/triggerFloatingButtonEdit.ts +30 -0
  71. package/src/components/RichTextEditor/plugins/ButtonPlugin/utils/triggerFloatingButtonInsert.ts +45 -0
  72. package/src/components/RichTextEditor/plugins/ButtonPlugin/withButton.ts +106 -0
  73. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/CustomFloatingLink.tsx +26 -0
  74. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/EditLinkModal/EditModal.tsx +43 -0
  75. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/EditLinkModal/index.ts +4 -0
  76. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/EditLinkModal/useFloatingLinkEdit.ts +113 -0
  77. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/FloatingLink.tsx +45 -0
  78. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/InsertLinkModal/InsertLinkModal.tsx +5 -0
  79. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/InsertLinkModal/InsertModal.tsx +105 -0
  80. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/InsertLinkModal/index.ts +4 -0
  81. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/InsertLinkModal/types.ts +16 -0
  82. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/InsertLinkModal/useFloatingLinkInsert.ts +73 -0
  83. package/src/components/RichTextEditor/plugins/LinkPlugin/FloatingLink/InsertLinkModal/useInsertModal.ts +136 -0
  84. package/src/components/RichTextEditor/plugins/LinkPlugin/LinkButton.tsx +38 -0
  85. package/src/components/RichTextEditor/plugins/LinkPlugin/LinkMarkupElement/LinkMarkupElementNode.tsx +36 -0
  86. package/src/components/RichTextEditor/plugins/LinkPlugin/LinkMarkupElement/index.ts +11 -0
  87. package/src/components/RichTextEditor/plugins/LinkPlugin/id.ts +3 -0
  88. package/src/components/RichTextEditor/plugins/LinkPlugin/index.ts +48 -0
  89. package/src/components/RichTextEditor/plugins/LinkPlugin/types.ts +12 -0
  90. package/src/components/RichTextEditor/plugins/LinkPlugin/utils/getUrl.ts +30 -0
  91. package/src/components/RichTextEditor/plugins/LinkPlugin/utils/index.ts +4 -0
  92. package/src/components/RichTextEditor/plugins/LinkPlugin/utils/relativeUrlRegex.spec.ts +35 -0
  93. package/src/components/RichTextEditor/plugins/LinkPlugin/utils/relativeUrlRegex.ts +3 -0
  94. package/src/components/RichTextEditor/plugins/LinkPlugin/utils/url.spec.ts +75 -0
  95. package/src/components/RichTextEditor/plugins/LinkPlugin/utils/url.ts +21 -0
  96. package/src/components/RichTextEditor/plugins/TextStylePlugins/custom1Plugin.tsx +61 -0
  97. package/src/components/RichTextEditor/plugins/TextStylePlugins/custom2Plugin.tsx +61 -0
  98. package/src/components/RichTextEditor/plugins/TextStylePlugins/custom3Plugin.tsx +62 -0
  99. package/src/components/RichTextEditor/plugins/TextStylePlugins/heading1Plugin.tsx +61 -0
  100. package/src/components/RichTextEditor/plugins/TextStylePlugins/heading2Plugin.tsx +58 -0
  101. package/src/components/RichTextEditor/plugins/TextStylePlugins/heading3Plugin.tsx +58 -0
  102. package/src/components/RichTextEditor/plugins/TextStylePlugins/heading4Plugin.tsx +59 -0
  103. package/src/components/RichTextEditor/plugins/TextStylePlugins/helpers.tsx +44 -0
  104. package/src/components/RichTextEditor/plugins/TextStylePlugins/imageCaptionPlugin.tsx +61 -0
  105. package/src/components/RichTextEditor/plugins/TextStylePlugins/imageTitlePlugin.tsx +61 -0
  106. package/src/components/RichTextEditor/plugins/TextStylePlugins/index.ts +15 -0
  107. package/src/components/RichTextEditor/plugins/TextStylePlugins/paragraphPlugin.tsx +58 -0
  108. package/src/components/RichTextEditor/plugins/TextStylePlugins/quotePlugin.tsx +62 -0
  109. package/src/components/RichTextEditor/plugins/index.ts +6 -0
  110. package/src/components/RichTextEditor/plugins/shared/LinkSelector/DocumentLink.tsx +80 -0
  111. package/src/components/RichTextEditor/plugins/shared/LinkSelector/DocumentLinks.tsx +97 -0
  112. package/src/components/RichTextEditor/plugins/shared/LinkSelector/LinkSelector.spec.ct.tsx +138 -0
  113. package/src/components/RichTextEditor/plugins/shared/LinkSelector/LinkSelector.tsx +80 -0
  114. package/src/components/RichTextEditor/plugins/shared/LinkSelector/PageLink.tsx +83 -0
  115. package/src/components/RichTextEditor/plugins/shared/LinkSelector/PageLinks.tsx +68 -0
  116. package/src/components/RichTextEditor/plugins/shared/LinkSelector/SectionLink.tsx +37 -0
  117. package/src/components/RichTextEditor/plugins/shared/LinkSelector/index.ts +3 -0
  118. package/src/components/RichTextEditor/plugins/styles.ts +179 -0
  119. package/src/components/RichTextEditor/serializer/index.ts +3 -0
  120. package/src/components/RichTextEditor/serializer/nodes/button.ts +25 -0
  121. package/src/components/RichTextEditor/serializer/nodes/checkItemNode.ts +29 -0
  122. package/src/components/RichTextEditor/serializer/nodes/default.ts +52 -0
  123. package/src/components/RichTextEditor/serializer/nodes/link.ts +25 -0
  124. package/src/components/RichTextEditor/serializer/nodes/mentionHtmlNode.ts +17 -0
  125. package/src/components/RichTextEditor/serializer/serializeNodesToHtmlRecursive.ts +134 -0
  126. package/src/components/RichTextEditor/serializer/serializeToHtml.ts +49 -0
  127. package/src/components/RichTextEditor/serializer/utlis/reactCssPropsToCss.ts +21 -0
  128. package/src/components/RichTextEditor/serializer/utlis/serializeLeafToHtml.ts +32 -0
  129. package/src/components/RichTextEditor/types.ts +23 -0
  130. package/src/components/index.ts +7 -0
  131. package/src/helpers/addHttps.spec.ts +42 -0
  132. package/src/helpers/addHttps.ts +15 -0
  133. package/src/helpers/convertToRichTextValue.spec.ts +32 -0
  134. package/src/helpers/convertToRichTextValue.ts +6 -0
  135. package/src/helpers/customCoordinatesGetterFactory.spec.ts +69 -0
  136. package/src/helpers/customCoordinatesGetterFactory.ts +39 -0
  137. package/src/helpers/hasRichTextValue.spec.ts +63 -0
  138. package/src/helpers/hasRichTextValue.ts +29 -0
  139. package/src/helpers/index.ts +8 -0
  140. package/src/helpers/isDownloadable.spec.ts +47 -0
  141. package/src/helpers/isDownloadable.ts +7 -0
  142. package/src/helpers/mapColorPalettes.spec.ts +146 -0
  143. package/src/helpers/mapColorPalettes.ts +22 -0
  144. package/src/hooks/index.ts +4 -0
  145. package/src/hooks/useAttachments.spec.ts +79 -0
  146. package/src/hooks/useAttachments.ts +46 -0
  147. package/src/hooks/useDndSensors.spec.ts +40 -0
  148. package/src/hooks/useDndSensors.ts +23 -0
  149. package/src/index.ts +8 -0
  150. package/src/settings/background.spec.ts +173 -0
  151. package/src/settings/background.ts +49 -0
  152. package/src/settings/border.spec.ts +76 -0
  153. package/src/settings/border.ts +90 -0
  154. package/src/settings/borderRadius.spec.ts +30 -0
  155. package/src/settings/borderRadius.ts +73 -0
  156. package/src/settings/borderRadiusExtended.spec.ts +52 -0
  157. package/src/settings/borderRadiusExtended.ts +84 -0
  158. package/src/settings/defaultValues.ts +21 -0
  159. package/src/settings/gutter.spec.ts +60 -0
  160. package/src/settings/gutter.ts +75 -0
  161. package/src/settings/index.ts +14 -0
  162. package/src/settings/margin.spec.ts +42 -0
  163. package/src/settings/margin.ts +72 -0
  164. package/src/settings/marginExtended.spec.ts +45 -0
  165. package/src/settings/marginExtended.ts +91 -0
  166. package/src/settings/padding.spec.ts +42 -0
  167. package/src/settings/padding.ts +73 -0
  168. package/src/settings/paddingExtended.spec.ts +45 -0
  169. package/src/settings/paddingExtended.ts +91 -0
  170. package/src/settings/security.spec.ts +87 -0
  171. package/src/settings/security.ts +61 -0
  172. package/src/settings/securityDownloadable.spec.ts +46 -0
  173. package/src/settings/securityDownloadable.ts +33 -0
  174. package/src/settings/securityGlobalControl.ts +42 -0
  175. package/src/settings/types.ts +128 -0
  176. package/src/utilities/color/getReadableColor.spec.ts +32 -0
  177. package/src/utilities/color/getReadableColor.ts +34 -0
  178. package/src/utilities/color/index.ts +10 -0
  179. package/src/utilities/color/isDark.spec.ts +33 -0
  180. package/src/utilities/color/isDark.ts +29 -0
  181. package/src/utilities/color/setAlpha.spec.ts +28 -0
  182. package/src/utilities/color/setAlpha.ts +14 -0
  183. package/src/utilities/color/toColorObject.spec.ts +19 -0
  184. package/src/utilities/color/toColorObject.ts +16 -0
  185. package/src/utilities/color/toHex8String.spec.ts +17 -0
  186. package/src/utilities/color/toHex8String.ts +14 -0
  187. package/src/utilities/color/toHexString.spec.ts +17 -0
  188. package/src/utilities/color/toHexString.ts +10 -0
  189. package/src/utilities/color/toRgbaString.spec.ts +12 -0
  190. package/src/utilities/color/toRgbaString.ts +14 -0
  191. package/src/utilities/color/toShortRgba.spec.ts +16 -0
  192. package/src/utilities/color/toShortRgba.ts +35 -0
  193. package/src/utilities/index.ts +5 -0
  194. package/src/utilities/moveItemInArray.spec.ts +17 -0
  195. package/src/utilities/moveItemInArray.ts +21 -0
  196. package/src/utilities/react/getBackgroundColorStyles.spec.ts +18 -0
  197. package/src/utilities/react/getBackgroundColorStyles.ts +11 -0
  198. package/src/utilities/react/getBorderStyles.spec.ts +39 -0
  199. package/src/utilities/react/getBorderStyles.ts +21 -0
  200. package/src/utilities/react/getRadiusStyles.spec.ts +25 -0
  201. package/src/utilities/react/getRadiusStyles.ts +8 -0
  202. package/src/utilities/react/index.ts +6 -0
  203. package/src/utilities/react/joinClassNames.spec.ts +18 -0
  204. package/src/utilities/react/joinClassNames.ts +10 -0
  205. package/tailwind.config.js +27 -0
  206. package/tsconfig.json +3 -1
  207. package/vite.config.ts +11 -1
  208. package/dist/index.cjs.js +0 -2
  209. package/dist/index.cjs.js.map +0 -1
  210. package/dist/index.d.ts +0 -147
  211. package/dist/index.es.js +0 -9
  212. package/dist/index.es.js.map +0 -1
  213. package/dist/index.umd.js +0 -2
  214. package/dist/index.umd.js.map +0 -1
@@ -0,0 +1,15 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { relativeUrlRegex } from '../components/RichTextEditor/plugins/LinkPlugin/utils';
4
+
5
+ export const addHttps = (url: string) => {
6
+ if (relativeUrlRegex.test(url)) {
7
+ return url;
8
+ }
9
+ try {
10
+ new URL(url);
11
+ return url;
12
+ } catch {
13
+ return `https://${url}`;
14
+ }
15
+ };
@@ -0,0 +1,32 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import type { TextStyles } from '@frontify/fondue';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { convertToRteValue } from './convertToRichTextValue';
6
+
7
+ describe('String converted to Richtext value', () => {
8
+ it('should return rich text value with correct textStyle', () => {
9
+ const text = 'with text';
10
+ const result = convertToRteValue('p' as TextStyles, text);
11
+ expect(result).toBe('[{"type":"p","children":[{"text":"with text","textStyle":"p"}]}]');
12
+ });
13
+
14
+ it('should return rich text value with correct textStyle', () => {
15
+ const text = 'Heading 1';
16
+ const result = convertToRteValue('heading1' as TextStyles, text);
17
+ expect(result).toBe('[{"type":"heading1","children":[{"text":"Heading 1","textStyle":"heading1"}]}]');
18
+ });
19
+
20
+ it('should return rich text value with correct alignment', () => {
21
+ const text = 'text';
22
+ const result = convertToRteValue('p' as TextStyles, text, 'center');
23
+ expect(result).toBe('[{"type":"p","children":[{"text":"text","textStyle":"p"}],"align":"center"}]');
24
+ });
25
+
26
+ it('should test if value is rich text value, it returns the rich text Value', () => {
27
+ const text =
28
+ '[{"type":"heading1","children":[{"text":"Hoi","bold":true,"underline":true}]},{"type":"heading1","children":[{"bold":true,"underline":true,"text":" "}]},{"type":"heading1","children":[{"text":"With Text"}]},{"type":"custom2","children":[{"text":""}]}]';
29
+ const result = convertToRteValue('p' as TextStyles, text);
30
+ expect(result).toBe(text);
31
+ });
32
+ });
@@ -0,0 +1,6 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { hasRichTextValue } from './hasRichTextValue';
4
+
5
+ export const convertToRteValue = (textStyle = 'p', text = '', align?: 'center' | 'right' | 'left' | 'justify') =>
6
+ hasRichTextValue(text) ? text : JSON.stringify([{ type: textStyle, children: [{ text, textStyle }], align }]);
@@ -0,0 +1,69 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { beforeEach, describe, expect, it } from 'vitest';
4
+ import { KeyboardCode, KeyboardCoordinateGetter, SensorContext } from '@dnd-kit/core';
5
+ import { customCoordinatesGetterFactory } from './customCoordinatesGetterFactory';
6
+
7
+ /**
8
+ * @vitest-environment happy-dom
9
+ */
10
+ describe('customCoordinatesGetterFactory', () => {
11
+ const initialCoordinates = { x: 0, y: 0 };
12
+ const context = {
13
+ activeNode: {
14
+ offsetWidth: 100,
15
+ offsetHeight: 200,
16
+ },
17
+ } as SensorContext;
18
+
19
+ const active = 'mock-active-id';
20
+ let customCoordinatesGetter: KeyboardCoordinateGetter;
21
+ beforeEach(() => {
22
+ customCoordinatesGetter = customCoordinatesGetterFactory(10, 20);
23
+ });
24
+
25
+ it('should respond to ArrowRight', () => {
26
+ const result = customCoordinatesGetter(new KeyboardEvent(KeyboardCode.Right, { code: KeyboardCode.Right }), {
27
+ active,
28
+ currentCoordinates: initialCoordinates,
29
+ context,
30
+ });
31
+ expect(result).toEqual({ x: 110, y: 0 });
32
+ });
33
+
34
+ it('should respond to ArrowLeft', () => {
35
+ const result = customCoordinatesGetter(new KeyboardEvent(KeyboardCode.Left, { code: KeyboardCode.Left }), {
36
+ active,
37
+ currentCoordinates: initialCoordinates,
38
+ context,
39
+ });
40
+ expect(result).toEqual({ x: -110, y: 0 });
41
+ });
42
+
43
+ it('should respond to ArrowDown', () => {
44
+ const result = customCoordinatesGetter(new KeyboardEvent(KeyboardCode.Down, { code: KeyboardCode.Down }), {
45
+ active,
46
+ currentCoordinates: initialCoordinates,
47
+ context,
48
+ });
49
+ expect(result).toEqual({ x: 0, y: 220 });
50
+ });
51
+
52
+ it('should respond to ArrowUp', () => {
53
+ const result = customCoordinatesGetter(new KeyboardEvent(KeyboardCode.Up, { code: KeyboardCode.Up }), {
54
+ active,
55
+ currentCoordinates: initialCoordinates,
56
+ context,
57
+ });
58
+ expect(result).toEqual({ x: 0, y: -220 });
59
+ });
60
+
61
+ it('should return undefined for other keys', () => {
62
+ const result = customCoordinatesGetter(new KeyboardEvent(KeyboardCode.Enter, { code: KeyboardCode.Enter }), {
63
+ active,
64
+ currentCoordinates: initialCoordinates,
65
+ context,
66
+ });
67
+ expect(result).toBeUndefined();
68
+ });
69
+ });
@@ -0,0 +1,39 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { KeyboardCode, KeyboardCoordinateGetter } from '@dnd-kit/core';
4
+
5
+ const directions: string[] = [KeyboardCode.Down, KeyboardCode.Right, KeyboardCode.Up, KeyboardCode.Left];
6
+
7
+ export const customCoordinatesGetterFactory =
8
+ (columnGap: number, rowGap: number): KeyboardCoordinateGetter =>
9
+ (event, { currentCoordinates, context: { activeNode } }) => {
10
+ event.preventDefault();
11
+ if (directions.includes(event.code)) {
12
+ const width = activeNode?.offsetWidth ?? 0;
13
+ const height = activeNode?.offsetHeight ?? 0;
14
+
15
+ switch (event.code) {
16
+ case KeyboardCode.Right:
17
+ return {
18
+ ...currentCoordinates,
19
+ x: currentCoordinates.x + width + columnGap,
20
+ };
21
+ case KeyboardCode.Left:
22
+ return {
23
+ ...currentCoordinates,
24
+ x: currentCoordinates.x - width - columnGap,
25
+ };
26
+ case KeyboardCode.Down:
27
+ return {
28
+ ...currentCoordinates,
29
+ y: currentCoordinates.y + height + rowGap,
30
+ };
31
+ case KeyboardCode.Up:
32
+ return {
33
+ ...currentCoordinates,
34
+ y: currentCoordinates.y - height - rowGap,
35
+ };
36
+ }
37
+ }
38
+ return undefined;
39
+ };
@@ -0,0 +1,63 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { describe, expect, it } from 'vitest';
4
+ import { hasRichTextValue } from './hasRichTextValue';
5
+
6
+ describe('Richtext has value', () => {
7
+ // Check if richtext has value
8
+ it('should return true if Richtext has value', () => {
9
+ const text = '[{"type":"p","children":[{"text":"with text"}]}]';
10
+ const result = hasRichTextValue(text);
11
+ expect(result).toBeTruthy();
12
+ });
13
+
14
+ it('should return true if Richtext has value', () => {
15
+ const text =
16
+ '[{"type":"heading1","children":[{"text":"Hoi","bold":true,"underline":true}]},{"type":"heading1","children":[{"bold":true,"underline":true,"text":" "}]},{"type":"heading1","children":[{"text":"With Text"}]},{"type":"custom2","children":[{"text":""}]}]';
17
+ const result = hasRichTextValue(text);
18
+ expect(result).toBeTruthy();
19
+ });
20
+
21
+ it('should return true if value has a space it', () => {
22
+ const text = '[{"type":"p","children":[{"text":" "}]}]';
23
+ const result = hasRichTextValue(text);
24
+ expect(result).toBeTruthy();
25
+ });
26
+
27
+ // Check if richtext has no value
28
+ it('should return false if RTE is empty', () => {
29
+ const text = '[{"type":"p","children":[{"text":""}]}]';
30
+ const result = hasRichTextValue(text);
31
+ expect(result).toBeFalsy();
32
+ });
33
+
34
+ it('should return false if RTE is empty 2.0', () => {
35
+ const text = '[{"type":"heading1","children":[{"text":"","bold":true,"underline":true}]}]';
36
+ const result = hasRichTextValue(text);
37
+ expect(result).toBeFalsy();
38
+ });
39
+
40
+ it('should return false if value is an invalid json', () => {
41
+ const text = 'abcd';
42
+ const result = hasRichTextValue(text);
43
+ expect(result).toBeFalsy();
44
+ });
45
+
46
+ it('should return false if RTE has only new lines', () => {
47
+ const text =
48
+ '[{"type":"quote","children":[{"text":""}]},{"type":"quote","children":[{"text":""}]},{"type":"quote","children":[{"text":""}]},{"type":"quote","children":[{"text":""}]},{"type":"quote","children":[{"text":""}]}]';
49
+ const result = hasRichTextValue(text);
50
+ expect(result).toBeFalsy();
51
+ });
52
+
53
+ it('should return false on empty value', () => {
54
+ const text = '';
55
+ const result = hasRichTextValue(text);
56
+ expect(result).toBeFalsy();
57
+ });
58
+
59
+ it('should return false on undefined text value', () => {
60
+ const result = hasRichTextValue(undefined);
61
+ expect(result).toBeFalsy();
62
+ });
63
+ });
@@ -0,0 +1,29 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ type TextElement = {
4
+ text: string;
5
+ children?: TextElement[];
6
+ };
7
+
8
+ export const hasRichTextValue = (string?: string): boolean => {
9
+ if (!string) {
10
+ return false;
11
+ }
12
+ const hasText = (children: TextElement[]): boolean =>
13
+ children.some((child: TextElement) => {
14
+ if (child.text) {
15
+ return child.text !== '';
16
+ }
17
+ if (child.children) {
18
+ return hasText(child.children);
19
+ }
20
+ return false;
21
+ });
22
+
23
+ try {
24
+ const json = JSON.parse(string);
25
+ return hasText(json);
26
+ } catch (error) {
27
+ return false;
28
+ }
29
+ };
@@ -0,0 +1,8 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ export * from './convertToRichTextValue';
4
+ export * from './customCoordinatesGetterFactory';
5
+ export * from './hasRichTextValue';
6
+ export * from './isDownloadable';
7
+ export * from './mapColorPalettes';
8
+ export * from './addHttps';
@@ -0,0 +1,47 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { describe, expect, it } from 'vitest';
4
+ import { isDownloadable } from './isDownloadable';
5
+ import { Security } from '../settings/types';
6
+
7
+ describe('isDownloadable', () => {
8
+ it('should return false if global is false and downloadable is false', () => {
9
+ const result = isDownloadable(Security.Global, false, false);
10
+ expect(result).toBeFalsy();
11
+ });
12
+
13
+ it('should return true if global is true and downloadable is true', () => {
14
+ const result = isDownloadable(Security.Global, true, true);
15
+ expect(result).toBeTruthy();
16
+ });
17
+
18
+ it('should return true if global is true and downloadable is false', () => {
19
+ const result = isDownloadable(Security.Global, false, true);
20
+ expect(result).toBeTruthy();
21
+ });
22
+
23
+ it('should return false if global is false and downloadable is true', () => {
24
+ const result = isDownloadable(Security.Global, true, false);
25
+ expect(result).toBeFalsy();
26
+ });
27
+
28
+ it('should return true if global is false but its security is custom and downloadable is true ', () => {
29
+ const result = isDownloadable(Security.Custom, true, false);
30
+ expect(result).toBeTruthy();
31
+ });
32
+
33
+ it('should return false if global is true but its security is custom and downloadable is false ', () => {
34
+ const result = isDownloadable(Security.Custom, false, true);
35
+ expect(result).toBeFalsy();
36
+ });
37
+
38
+ it('should return false if global is true but its security is custom and downloadable is false ', () => {
39
+ const result = isDownloadable(Security.Custom, false, false);
40
+ expect(result).toBeFalsy();
41
+ });
42
+
43
+ it('should return true if global is true but its security is custom and downloadable is false ', () => {
44
+ const result = isDownloadable(Security.Custom, true, true);
45
+ expect(result).toBeTruthy();
46
+ });
47
+ });
@@ -0,0 +1,7 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { Security } from '../settings/types';
4
+
5
+ export const isDownloadable = (security: Security, downloadable: boolean, globalAssetDownloadEnabled: boolean) => {
6
+ return security === Security.Custom ? downloadable : globalAssetDownloadEnabled;
7
+ };
@@ -0,0 +1,146 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { describe, expect, it } from 'vitest';
4
+ import { mapAppBridgeColorPalettesToFonduePalettes } from './mapColorPalettes';
5
+
6
+ describe('mapAppBridgeColorPalettesToFonduePalettes', () => {
7
+ it('should map app bridge color palette to fondue palette', () => {
8
+ const result = mapAppBridgeColorPalettesToFonduePalettes([
9
+ {
10
+ id: 19,
11
+ name: 'Awesome color palette',
12
+ description: 'This is an awesome color palette',
13
+ colors: [
14
+ {
15
+ id: 6,
16
+ name: 'Tarawera',
17
+ sort: 1,
18
+ nameCss: 'tarawera',
19
+ hex: '0b486b',
20
+ red: 11,
21
+ green: 72,
22
+ blue: 107,
23
+ alpha: 255,
24
+ hue: 202,
25
+ saturation: 81,
26
+ lightness: 23,
27
+ c: 90,
28
+ m: 33,
29
+ y: 0,
30
+ k: 58,
31
+ pantone: null,
32
+ ral: null,
33
+ oracal: null,
34
+ pantoneCoated: null,
35
+ pantoneUncoated: null,
36
+ cmykCoated: null,
37
+ cmykUncoated: null,
38
+ cmykNewspaper: null,
39
+ ncs: null,
40
+ pantoneCp: null,
41
+ pantonePlastics: null,
42
+ pantoneTextile: null,
43
+ hks: null,
44
+ threeM: null,
45
+ lab: null,
46
+ },
47
+ {
48
+ id: 7,
49
+ name: 'Oracle',
50
+ sort: 2,
51
+ nameCss: 'oracle',
52
+ hex: '3b8686',
53
+ red: 59,
54
+ green: 134,
55
+ blue: 134,
56
+ alpha: 255,
57
+ hue: 180,
58
+ saturation: 39,
59
+ lightness: 38,
60
+ c: 56,
61
+ m: 0,
62
+ y: 0,
63
+ k: 47,
64
+ pantone: null,
65
+ ral: null,
66
+ oracal: null,
67
+ pantoneCoated: null,
68
+ pantoneUncoated: null,
69
+ cmykCoated: null,
70
+ cmykUncoated: null,
71
+ cmykNewspaper: null,
72
+ ncs: null,
73
+ pantoneCp: null,
74
+ pantonePlastics: null,
75
+ pantoneTextile: null,
76
+ hks: null,
77
+ threeM: null,
78
+ lab: null,
79
+ },
80
+ {
81
+ id: 8,
82
+ name: 'Silver Tree',
83
+ sort: 3,
84
+ nameCss: 'silver-tree',
85
+ hex: '79bd9a',
86
+ red: 121,
87
+ green: 189,
88
+ blue: 154,
89
+ alpha: 255,
90
+ hue: 149,
91
+ saturation: 34,
92
+ lightness: 61,
93
+ c: 36,
94
+ m: 0,
95
+ y: 19,
96
+ k: 26,
97
+ pantone: null,
98
+ ral: null,
99
+ oracal: null,
100
+ pantoneCoated: null,
101
+ pantoneUncoated: null,
102
+ cmykCoated: null,
103
+ cmykUncoated: null,
104
+ cmykNewspaper: null,
105
+ ncs: null,
106
+ pantoneCp: null,
107
+ pantonePlastics: null,
108
+ pantoneTextile: null,
109
+ hks: null,
110
+ threeM: null,
111
+ lab: null,
112
+ },
113
+ ],
114
+ },
115
+ ]);
116
+ expect(result).toEqual([
117
+ {
118
+ id: 19,
119
+ title: 'Awesome color palette',
120
+ colors: [
121
+ {
122
+ alpha: 1,
123
+ red: 11,
124
+ green: 72,
125
+ blue: 107,
126
+ name: 'Tarawera',
127
+ },
128
+ {
129
+ alpha: 1,
130
+ red: 59,
131
+ green: 134,
132
+ blue: 134,
133
+ name: 'Oracle',
134
+ },
135
+ {
136
+ alpha: 1,
137
+ red: 121,
138
+ green: 189,
139
+ blue: 154,
140
+ name: 'Silver Tree',
141
+ },
142
+ ],
143
+ },
144
+ ]);
145
+ });
146
+ });
@@ -0,0 +1,22 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { ColorPalette } from '@frontify/app-bridge';
4
+ import { Palette } from '@frontify/fondue';
5
+
6
+ export const mapAppBridgeColorPalettesToFonduePalettes = (colorPalettes: ColorPalette[]): Palette[] => {
7
+ return colorPalettes.map(mapAppBridgeColorPaletteToFonduePalette);
8
+ };
9
+
10
+ export const mapAppBridgeColorPaletteToFonduePalette = (colorPalette: ColorPalette): Palette => {
11
+ return {
12
+ id: colorPalette.id,
13
+ title: colorPalette.name,
14
+ colors: colorPalette.colors.map((color) => ({
15
+ alpha: color.alpha ? color.alpha / 255 : 1,
16
+ red: color.red ?? 0,
17
+ green: color.green ?? 0,
18
+ blue: color.blue ?? 0,
19
+ name: color.name ?? '',
20
+ })),
21
+ };
22
+ };
@@ -0,0 +1,4 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ export * from './useAttachments';
4
+ export * from './useDndSensors';
@@ -0,0 +1,79 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { AssetDummy, getAppBridgeBlockStub } from '@frontify/app-bridge';
4
+ import { renderHook, waitFor } from '@testing-library/react';
5
+
6
+ import { describe, expect, it } from 'vitest';
7
+ import { useAttachments } from './useAttachments';
8
+
9
+ const MOCK_SETTINGS_ID = 'attachments';
10
+
11
+ /**
12
+ * @vitest-environment happy-dom
13
+ */
14
+
15
+ describe('useAttachments', () => {
16
+ it('should have 1 attachment if attachment is added', async () => {
17
+ const STUB_WITH_NO_ASSETS = getAppBridgeBlockStub({
18
+ blockId: 1,
19
+ blockAssets: { [MOCK_SETTINGS_ID]: [] },
20
+ });
21
+ const { result } = renderHook(() => useAttachments(STUB_WITH_NO_ASSETS, MOCK_SETTINGS_ID));
22
+
23
+ await result.current.onAddAttachments([AssetDummy.with(1)]);
24
+ await waitFor(() => {
25
+ expect(result.current.attachments).toHaveLength(1);
26
+ });
27
+ });
28
+
29
+ it('should decrease the attachment count from 3 to 2', async () => {
30
+ const STUB_WITH_THREE_ASSETS = getAppBridgeBlockStub({
31
+ blockId: 2,
32
+ blockAssets: { [MOCK_SETTINGS_ID]: [AssetDummy.with(1), AssetDummy.with(2), AssetDummy.with(3)] },
33
+ });
34
+ const { result } = renderHook(() => useAttachments(STUB_WITH_THREE_ASSETS, MOCK_SETTINGS_ID));
35
+
36
+ const initialValue = result.current;
37
+ await waitFor(() => {
38
+ expect(result.current).not.toBe(initialValue);
39
+ });
40
+ await result.current.onAttachmentDelete(AssetDummy.with(1));
41
+ await waitFor(() => {
42
+ expect(result.current.attachments).toHaveLength(2);
43
+ });
44
+ });
45
+
46
+ it('should replace the attachment in the same position', async () => {
47
+ const STUB_WITH_THREE_ASSETS = getAppBridgeBlockStub({
48
+ blockId: 2,
49
+ blockAssets: { [MOCK_SETTINGS_ID]: [AssetDummy.with(1), AssetDummy.with(2), AssetDummy.with(3)] },
50
+ });
51
+ const { result } = renderHook(() => useAttachments(STUB_WITH_THREE_ASSETS, MOCK_SETTINGS_ID));
52
+ const initialValue = result.current;
53
+ await waitFor(() => {
54
+ expect(result.current).not.toBe(initialValue);
55
+ });
56
+ await result.current.onAttachmentReplace(AssetDummy.with(2), AssetDummy.with(10));
57
+ await waitFor(() => {
58
+ expect(result.current.attachments[1].id).toBe(10);
59
+ });
60
+ });
61
+
62
+ it('should reorder the attachments', async () => {
63
+ const STUB_WITH_THREE_ASSETS = getAppBridgeBlockStub({
64
+ blockId: 2,
65
+ blockAssets: { [MOCK_SETTINGS_ID]: [AssetDummy.with(1), AssetDummy.with(2), AssetDummy.with(3)] },
66
+ });
67
+ const { result } = renderHook(() => useAttachments(STUB_WITH_THREE_ASSETS, MOCK_SETTINGS_ID));
68
+ const initialValue = result.current;
69
+ await waitFor(() => {
70
+ expect(result.current).not.toBe(initialValue);
71
+ });
72
+ await result.current.onAttachmentsSorted([AssetDummy.with(3), AssetDummy.with(2), AssetDummy.with(1)]);
73
+ await waitFor(() => {
74
+ expect(result.current.attachments[0].id).toBe(3);
75
+ expect(result.current.attachments[1].id).toBe(2);
76
+ expect(result.current.attachments[2].id).toBe(1);
77
+ });
78
+ });
79
+ });
@@ -0,0 +1,46 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { AppBridgeBlock, Asset, useBlockAssets } from '@frontify/app-bridge';
4
+
5
+ export const useAttachments = (appBridge: AppBridgeBlock, assetId: string) => {
6
+ const { blockAssets, updateAssetIdsFromKey } = useBlockAssets(appBridge);
7
+ const attachments = blockAssets?.[assetId] || [];
8
+
9
+ const onAddAttachments = async (newAssets: Asset[]) => {
10
+ const newAssetIds = attachments.map((attachment) => attachment.id);
11
+ for (const asset of newAssets) {
12
+ newAssetIds.push(asset.id);
13
+ }
14
+ await updateAssetIdsFromKey(assetId, newAssetIds);
15
+ };
16
+
17
+ const onAttachmentDelete = async (assetToDelete: Asset) => {
18
+ const newAssetIds = attachments
19
+ .filter((attachment) => attachment.id !== assetToDelete.id)
20
+ .map((attachment) => attachment.id);
21
+
22
+ await updateAssetIdsFromKey(assetId, newAssetIds);
23
+ };
24
+
25
+ const onAttachmentReplace = async (attachmentToReplace: Asset, newAsset: Asset) => {
26
+ const newAssetIds = attachments.map((attachment) =>
27
+ attachment.id === attachmentToReplace.id ? newAsset.id : attachment.id
28
+ );
29
+
30
+ await updateAssetIdsFromKey(assetId, newAssetIds);
31
+ };
32
+
33
+ const onAttachmentsSorted = async (assets: Asset[]) => {
34
+ const newAssetIds = assets.map((asset) => asset.id);
35
+
36
+ await updateAssetIdsFromKey(assetId, newAssetIds);
37
+ };
38
+
39
+ return {
40
+ onAddAttachments,
41
+ onAttachmentDelete,
42
+ onAttachmentReplace,
43
+ onAttachmentsSorted,
44
+ attachments,
45
+ };
46
+ };
@@ -0,0 +1,40 @@
1
+ /* (c) Copyright Frontify Ltd., all rights reserved. */
2
+
3
+ import { renderHook } from '@testing-library/react';
4
+ import { useDndSensors } from './useDndSensors';
5
+ import { describe, expect, it } from 'vitest';
6
+ import { KeyboardSensorOptions } from '@dnd-kit/core';
7
+
8
+ /**
9
+ * @vitest-environment happy-dom
10
+ */
11
+ describe('useDndSensors', () => {
12
+ it('should create a PointerSensor and a KeyboardSensor with a coordinate getter function and custom keyboardCodes', () => {
13
+ const { result } = renderHook(() => useDndSensors());
14
+ expect(result.current.length).toBe(2);
15
+ expect(result.current[0].sensor.name).toBe('PointerSensor');
16
+ expect(result.current[1].sensor.name).toBe('KeyboardSensor');
17
+ expect(typeof (result.current[1].options as KeyboardSensorOptions).coordinateGetter).toBe('function');
18
+ expect((result.current[1].options as KeyboardSensorOptions).keyboardCodes).toEqual({
19
+ start: ['Space', 'Enter'],
20
+ cancel: [],
21
+ end: ['Space', 'Enter', 'Escape'],
22
+ });
23
+ });
24
+
25
+ it('should create a PointerSensor and a KeyboardSensor with a coordinate getter with columnGap', () => {
26
+ const { result } = renderHook(() => useDndSensors(100));
27
+ expect(result.current.length).toBe(2);
28
+ expect(result.current[0].sensor.name).toBe('PointerSensor');
29
+ expect(result.current[1].sensor.name).toBe('KeyboardSensor');
30
+ expect(typeof (result.current[1].options as KeyboardSensorOptions).coordinateGetter).toBe('function');
31
+ });
32
+
33
+ it('should create a PointerSensor and a KeyboardSensor with a coordinate getter with columnGap and rowGap', () => {
34
+ const { result } = renderHook(() => useDndSensors(100, 150));
35
+ expect(result.current.length).toBe(2);
36
+ expect(result.current[0].sensor.name).toBe('PointerSensor');
37
+ expect(result.current[1].sensor.name).toBe('KeyboardSensor');
38
+ expect(typeof (result.current[1].options as KeyboardSensorOptions).coordinateGetter).toBe('function');
39
+ });
40
+ });