@elementor/editor-canvas 0.28.0 → 3.32.0-20

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 (31) hide show
  1. package/CHANGELOG.md +0 -20
  2. package/dist/index.js +167 -36
  3. package/dist/index.mjs +163 -32
  4. package/package.json +15 -15
  5. package/src/__tests__/flex-transformer.test.ts +272 -0
  6. package/src/__tests__/styles-prop-resolver.test.ts +78 -63
  7. package/src/components/__tests__/style-renderer.test.tsx +2 -2
  8. package/src/components/style-renderer.tsx +1 -1
  9. package/src/hooks/__tests__/use-style-items.test.ts +106 -7
  10. package/src/hooks/use-style-items.ts +47 -5
  11. package/src/init-settings-transformers.ts +2 -0
  12. package/src/init-style-transformers.ts +10 -0
  13. package/src/renderers/__tests__/__snapshots__/create-styles-renderer.test.ts.snap +2 -0
  14. package/src/renderers/__tests__/create-styles-renderer.test.ts +98 -0
  15. package/src/renderers/create-styles-renderer.ts +26 -2
  16. package/src/style-commands/__tests__/paste-style.test.ts +30 -0
  17. package/src/style-commands/__tests__/reset-style.test.ts +4 -0
  18. package/src/style-commands/undoable-actions/paste-element-style.ts +2 -1
  19. package/src/transformers/settings/attributes-transformer.ts +25 -0
  20. package/src/transformers/styles/background-overlay-transformer.ts +1 -10
  21. package/src/transformers/styles/create-combine-array-transformer.ts +3 -1
  22. package/src/transformers/styles/filter-transformer.ts +10 -18
  23. package/src/transformers/styles/flex-transformer.ts +55 -0
  24. package/src/transformers/styles/transform-move-transformer.ts +3 -1
  25. package/src/transformers/styles/transform-rotate-transformer.ts +19 -0
  26. package/src/transformers/styles/transform-scale-transformer.ts +11 -0
  27. package/src/transformers/styles/transform-skew-transformer.ts +15 -0
  28. package/src/transformers/styles/transition-transformer.ts +25 -0
  29. package/.turbo/turbo-build.log +0 -22
  30. package/dist/index.js.map +0 -1
  31. package/dist/index.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -198,6 +198,7 @@ function getLinkAttrs(el) {
198
198
 
199
199
  // src/hooks/use-style-items.ts
200
200
  import { useEffect as useEffect4, useMemo as useMemo3, useState as useState2 } from "react";
201
+ import { getBreakpoints } from "@elementor/editor-responsive";
201
202
  import { stylesRepository } from "@elementor/editor-styles-repository";
202
203
  import { registerDataHook } from "@elementor/editor-v1-adapters";
203
204
 
@@ -387,6 +388,10 @@ function useStylePropResolver() {
387
388
  import { useMemo as useMemo2 } from "react";
388
389
  import { useBreakpointsMap } from "@elementor/editor-responsive";
389
390
 
391
+ // src/renderers/create-styles-renderer.ts
392
+ import { EXPERIMENTAL_FEATURES, isExperimentActive } from "@elementor/editor-v1-adapters";
393
+ import { decodeString } from "@elementor/utils";
394
+
390
395
  // src/renderers/errors.ts
391
396
  import { createError } from "@elementor/utils";
392
397
  var UnknownStyleTypeError = createError({
@@ -403,11 +408,13 @@ function createStylesRenderer({ resolve, breakpoints, selectorPrefix = "" }) {
403
408
  const stylesCssPromises = styles.map(async (style) => {
404
409
  const variantCssPromises = Object.values(style.variants).map(async (variant) => {
405
410
  const css = await propsToCss({ props: variant.props, resolve, signal });
406
- return createStyleWrapper().for(style.cssName, style.type).withPrefix(selectorPrefix).withState(variant.meta.state).withMediaQuery(variant.meta.breakpoint ? breakpoints[variant.meta.breakpoint] : null).wrap(css);
411
+ const customCss = customCssToString(variant.custom_css);
412
+ return createStyleWrapper().for(style.cssName, style.type).withPrefix(selectorPrefix).withState(variant.meta.state).withMediaQuery(variant.meta.breakpoint ? breakpoints[variant.meta.breakpoint] : null).wrap(css + customCss);
407
413
  });
408
414
  const variantsCss = await Promise.all(variantCssPromises);
409
415
  return {
410
416
  id: style.id,
417
+ breakpoint: style?.variants[0]?.meta?.breakpoint || "desktop",
411
418
  value: variantsCss.join("")
412
419
  };
413
420
  });
@@ -451,6 +458,16 @@ async function propsToCss({ props, resolve, signal }) {
451
458
  return acc;
452
459
  }, []).join("");
453
460
  }
461
+ function customCssToString(customCss) {
462
+ if (!isExperimentActive(EXPERIMENTAL_FEATURES.CUSTOM_CSS) || !customCss?.raw) {
463
+ return "";
464
+ }
465
+ const decoded = decodeString(customCss.raw);
466
+ if (!decoded.trim()) {
467
+ return "";
468
+ }
469
+ return decoded + "\n";
470
+ }
454
471
 
455
472
  // src/hooks/use-style-renderer.ts
456
473
  var SELECTOR_PREFIX = ".elementor";
@@ -496,7 +513,15 @@ function useStyleItems() {
496
513
  await Promise.all(promises);
497
514
  });
498
515
  });
499
- return Object.values(styleItems).sort(({ provider: providerA }, { provider: providerB }) => providerA.priority - providerB.priority).flatMap(({ items }) => items);
516
+ const breakpointsOrder = getBreakpoints().map((breakpoint) => breakpoint.id);
517
+ return useMemo3(
518
+ () => Object.values(styleItems).sort(({ provider: providerA }, { provider: providerB }) => providerA.priority - providerB.priority).flatMap(({ items }) => items).sort(({ breakpoint: breakpointA }, { breakpoint: breakpointB }) => {
519
+ return breakpointsOrder.indexOf(breakpointA) - breakpointsOrder.indexOf(breakpointB);
520
+ }),
521
+ // eslint-disable-next-line
522
+ // eslint-disable-next-line react-hooks/exhaustive-deps
523
+ [styleItems, breakpointsOrder.join("-")]
524
+ );
500
525
  }
501
526
  function createProviderSubscriber({ provider, renderStyles, setStyleItems }) {
502
527
  return abortPreviousRuns(
@@ -509,7 +534,7 @@ function createProviderSubscriber({ provider, renderStyles, setStyleItems }) {
509
534
  cssName: provider.actions.resolveCssName(style.id)
510
535
  };
511
536
  });
512
- return renderStyles({ styles, signal });
537
+ return renderStyles({ styles: breakToBreakpoints(styles), signal });
513
538
  }).then((items) => {
514
539
  setStyleItems((prev) => ({
515
540
  ...prev,
@@ -517,6 +542,29 @@ function createProviderSubscriber({ provider, renderStyles, setStyleItems }) {
517
542
  }));
518
543
  }).execute()
519
544
  );
545
+ function breakToBreakpoints(styles) {
546
+ return Object.values(
547
+ styles.reduce(
548
+ (acc, style) => {
549
+ style.variants.forEach((variant) => {
550
+ const breakpoint = variant.meta.breakpoint || "desktop";
551
+ if (!acc[style.id]) {
552
+ acc[style.id] = {};
553
+ }
554
+ if (!acc[style.id][breakpoint]) {
555
+ acc[style.id][breakpoint] = {
556
+ ...style,
557
+ variants: []
558
+ };
559
+ }
560
+ acc[style.id][breakpoint].variants.push(variant);
561
+ });
562
+ return acc;
563
+ },
564
+ {}
565
+ )
566
+ ).flatMap((breakpointMap) => Object.values(breakpointMap));
567
+ }
520
568
  }
521
569
 
522
570
  // src/components/style-renderer.tsx
@@ -527,7 +575,7 @@ function StyleRenderer() {
527
575
  if (!container) {
528
576
  return null;
529
577
  }
530
- return /* @__PURE__ */ React3.createElement(Portal, { container }, styleItems.map((item) => /* @__PURE__ */ React3.createElement("style", { "data-e-style-id": item.id, key: item.id }, item.value)), linksAttrs.map((attrs) => /* @__PURE__ */ React3.createElement("link", { ...attrs, key: attrs.id })));
578
+ return /* @__PURE__ */ React3.createElement(Portal, { container }, styleItems.map((item) => /* @__PURE__ */ React3.createElement("style", { "data-e-style-id": item.id, key: `${item.id}-${item.breakpoint}` }, item.value)), linksAttrs.map((attrs) => /* @__PURE__ */ React3.createElement("link", { ...attrs, key: attrs.id })));
531
579
  }
532
580
  function usePortalContainer() {
533
581
  return useListenTo3(commandEndEvent2("editor/documents/attach-preview"), () => getCanvasIframeDocument()?.head);
@@ -536,15 +584,34 @@ function usePortalContainer() {
536
584
  // src/settings-transformers-registry.ts
537
585
  var settingsTransformersRegistry = createTransformersRegistry();
538
586
 
539
- // src/transformers/settings/classes-transformer.ts
540
- import { stylesRepository as stylesRepository2 } from "@elementor/editor-styles-repository";
541
-
542
587
  // src/transformers/create-transformer.ts
543
588
  function createTransformer(cb) {
544
589
  return cb;
545
590
  }
546
591
 
592
+ // src/transformers/settings/attributes-transformer.ts
593
+ function escapeHtmlAttribute(value) {
594
+ const specialChars = {
595
+ "&": "&",
596
+ "<": "&lt;",
597
+ ">": "&gt;",
598
+ "'": "&#39;",
599
+ '"': "&quot;"
600
+ };
601
+ return value.replace(/[&<>'"]/g, (char) => specialChars[char] || char);
602
+ }
603
+ var attributesTransformer = createTransformer((values) => {
604
+ return values.map((value) => {
605
+ if (!value.key || !value.value) {
606
+ return "";
607
+ }
608
+ const escapedValue = escapeHtmlAttribute(value.value);
609
+ return `${value.key}="${escapedValue}"`;
610
+ }).join(" ");
611
+ });
612
+
547
613
  // src/transformers/settings/classes-transformer.ts
614
+ import { stylesRepository as stylesRepository2 } from "@elementor/editor-styles-repository";
548
615
  function createClassesTransformer() {
549
616
  const cache = /* @__PURE__ */ new Map();
550
617
  return createTransformer((value) => {
@@ -614,7 +681,7 @@ var plainTransformer = createTransformer((value) => {
614
681
 
615
682
  // src/init-settings-transformers.ts
616
683
  function initSettingsTransformers() {
617
- settingsTransformersRegistry.register("classes", createClassesTransformer()).register("link", linkTransformer).register("image", imageTransformer).register("image-src", imageSrcTransformer).registerFallback(plainTransformer);
684
+ settingsTransformersRegistry.register("classes", createClassesTransformer()).register("link", linkTransformer).register("image", imageTransformer).register("image-src", imageSrcTransformer).register("key-value-array", attributesTransformer).registerFallback(plainTransformer);
618
685
  }
619
686
 
620
687
  // src/transformers/styles/background-color-overlay-transformer.ts
@@ -657,7 +724,6 @@ var backgroundImageSizeScaleTransformer = createTransformer(
657
724
  );
658
725
 
659
726
  // src/transformers/styles/background-overlay-transformer.ts
660
- import { isExperimentActive } from "@elementor/editor-v1-adapters";
661
727
  var backgroundOverlayTransformer = createTransformer(
662
728
  (value) => {
663
729
  if (!value || value.length === 0) {
@@ -682,7 +748,6 @@ var backgroundOverlayTransformer = createTransformer(
682
748
  }
683
749
  );
684
750
  function normalizeOverlayValues(overlays) {
685
- const isVersion330Active = isExperimentActive("e_v_3_30");
686
751
  const mappedValues = overlays.map((item) => {
687
752
  if (typeof item === "string") {
688
753
  return {
@@ -695,16 +760,12 @@ function normalizeOverlayValues(overlays) {
695
760
  }
696
761
  return item;
697
762
  });
698
- if (!isVersion330Active) {
699
- return mappedValues;
700
- }
701
763
  return mappedValues.filter((item) => item && !!item.src);
702
764
  }
703
765
  function getValuesString(items, prop, defaultValue, preventUnification = false) {
704
- const isVersion330Active = isExperimentActive("e_v_3_30");
705
766
  const isEmpty = items.filter((item) => item?.[prop]).length === 0;
706
767
  if (isEmpty) {
707
- return isVersion330Active ? defaultValue : null;
768
+ return defaultValue;
708
769
  }
709
770
  const formattedValues = items.map((item) => item[prop] ?? defaultValue);
710
771
  if (!preventUnification) {
@@ -732,7 +793,9 @@ var colorStopTransformer = createTransformer(
732
793
 
733
794
  // src/transformers/styles/create-combine-array-transformer.ts
734
795
  var createCombineArrayTransformer = (delimiter) => {
735
- return createTransformer((value) => value.filter(Boolean).join(delimiter));
796
+ return createTransformer(
797
+ (value) => value?.length ? value.filter(Boolean).join(delimiter) : null
798
+ );
736
799
  };
737
800
 
738
801
  // src/transformers/styles/create-multi-props-transformer.ts
@@ -751,23 +814,51 @@ var filterTransformer = createTransformer((filterValues) => {
751
814
  return filterValues.filter(Boolean).map(mapToFilterFunctionString).join(" ");
752
815
  });
753
816
  var mapToFilterFunctionString = (value) => {
754
- if ("radius" in value) {
755
- return value.radius ? `blur(${value.radius})` : "";
756
- }
757
- if ("amount" in value) {
758
- return value.amount ? `brightness(${value.amount})` : "";
759
- }
760
- if ("xAxis" in value && "yAxis" in value && "blur" in value && "color" in value) {
761
- const { xAxis, yAxis, blur, color } = value;
817
+ if (value.func === "drop-shadow") {
818
+ const { xAxis, yAxis, blur, color } = value.args;
762
819
  return `drop-shadow(${xAxis || "0px"} ${yAxis || "0px"} ${blur || "10px"} ${color || "transparent"})`;
763
820
  }
764
- const keys = Object.keys(value);
765
- if (!keys[0]) {
821
+ if (!value.func || !value.args) {
766
822
  return "";
767
823
  }
768
- return value[keys[0]] ? `${keys[0]}(${value[keys[0]]})` : "";
824
+ return `${value.func}(${value.args})`;
769
825
  };
770
826
 
827
+ // src/transformers/styles/flex-transformer.ts
828
+ var flexTransformer = createTransformer((value) => {
829
+ const grow = value.flexGrow;
830
+ const shrink = value.flexShrink;
831
+ const basis = value.flexBasis;
832
+ const hasGrow = grow !== void 0 && grow !== null;
833
+ const hasShrink = shrink !== void 0 && shrink !== null;
834
+ const hasBasis = basis !== void 0 && basis !== null;
835
+ if (!hasGrow && !hasShrink && !hasBasis) {
836
+ return null;
837
+ }
838
+ if (hasGrow && hasShrink && hasBasis) {
839
+ return `${grow} ${shrink} ${typeof basis === "object" && basis.size !== void 0 ? `${basis.size}${basis.unit || ""}` : basis}`;
840
+ }
841
+ if (hasGrow && hasShrink && !hasBasis) {
842
+ return `${grow} ${shrink}`;
843
+ }
844
+ if (hasGrow && !hasShrink && hasBasis) {
845
+ return `${grow} 1 ${typeof basis === "object" && basis.size !== void 0 ? `${basis.size}${basis.unit || ""}` : basis}`;
846
+ }
847
+ if (!hasGrow && hasShrink && hasBasis) {
848
+ return `0 ${shrink} ${typeof basis === "object" && basis.size !== void 0 ? `${basis.size}${basis.unit || ""}` : basis}`;
849
+ }
850
+ if (hasGrow && !hasShrink && !hasBasis) {
851
+ return `${grow}`;
852
+ }
853
+ if (!hasGrow && hasShrink && !hasBasis) {
854
+ return `0 ${shrink}`;
855
+ }
856
+ if (!hasGrow && !hasShrink && hasBasis) {
857
+ return `0 1 ${typeof basis === "object" && basis.size !== void 0 ? `${basis.size}${basis.unit || ""}` : basis}`;
858
+ }
859
+ return null;
860
+ });
861
+
771
862
  // src/transformers/styles/position-transformer.ts
772
863
  var positionTransformer = createTransformer(({ x, y }) => `${x ?? "0px"} ${y ?? "0px"}`);
773
864
 
@@ -792,8 +883,33 @@ var strokeTransformer = createTransformer((value) => {
792
883
  });
793
884
 
794
885
  // src/transformers/styles/transform-move-transformer.ts
886
+ var defaultMove = "0px";
795
887
  var transformMoveTransformer = createTransformer((value) => {
796
- return `translate3d(${value.x}, ${value.y}, ${value.z})`;
888
+ return `translate3d(${value.x ?? defaultMove}, ${value.y ?? defaultMove}, ${value.z ?? defaultMove})`;
889
+ });
890
+
891
+ // src/transformers/styles/transform-rotate-transformer.ts
892
+ var defaultRotate = "0deg";
893
+ var transformRotateTransformer = createTransformer((value) => {
894
+ const transforms = [
895
+ `rotateX(${value?.x ?? defaultRotate})`,
896
+ `rotateY(${value?.y ?? defaultRotate})`,
897
+ `rotateZ(${value?.z ?? defaultRotate})`
898
+ ];
899
+ return transforms.join(" ");
900
+ });
901
+
902
+ // src/transformers/styles/transform-scale-transformer.ts
903
+ var transformScaleTransformer = createTransformer((value) => {
904
+ return `scale3d(${value.x ?? 1}, ${value.y ?? 1}, ${value.z ?? 1})`;
905
+ });
906
+
907
+ // src/transformers/styles/transform-skew-transformer.ts
908
+ var defaultSkew = "0deg";
909
+ var transformSkewTransformer = createTransformer((value) => {
910
+ const x = value?.x ?? defaultSkew;
911
+ const y = value?.y ?? defaultSkew;
912
+ return `skew(${x}, ${y})`;
797
913
  });
798
914
 
799
915
  // src/transformers/styles/transform-transformer.ts
@@ -804,6 +920,20 @@ var transformTransformer = createTransformer((values) => {
804
920
  return values.join(" ");
805
921
  });
806
922
 
923
+ // src/transformers/styles/transition-transformer.ts
924
+ var transitionTransformer = createTransformer((transitionValues) => {
925
+ if (transitionValues?.length < 1) {
926
+ return null;
927
+ }
928
+ return transitionValues.filter(Boolean).map(mapToTransitionString).join(", ");
929
+ });
930
+ var mapToTransitionString = (value) => {
931
+ if (!value.selection || !value.size) {
932
+ return "";
933
+ }
934
+ return `${value.selection.value} ${value.size}`;
935
+ };
936
+
807
937
  // src/init-style-transformers.ts
808
938
  function initStyleTransformers() {
809
939
  styleTransformersRegistry.register("size", sizeTransformer).register("shadow", shadowTransformer).register("stroke", strokeTransformer).register(
@@ -812,10 +942,10 @@ function initStyleTransformers() {
812
942
  ["block-start", "block-end", "inline-start", "inline-end"],
813
943
  ({ propKey, key }) => `${propKey}-${key}`
814
944
  )
815
- ).register("filter", filterTransformer).register("backdrop-filter", filterTransformer).register("box-shadow", createCombineArrayTransformer(",")).register("background", backgroundTransformer).register("background-overlay", backgroundOverlayTransformer).register("background-color-overlay", backgroundColorOverlayTransformer).register("background-image-overlay", backgroundImageOverlayTransformer).register("background-gradient-overlay", backgroundGradientOverlayTransformer).register("gradient-color-stop", createCombineArrayTransformer(",")).register("color-stop", colorStopTransformer).register("background-image-position-offset", positionTransformer).register("background-image-size-scale", backgroundImageSizeScaleTransformer).register("image-src", imageSrcTransformer).register("image", imageTransformer).register("object-position", positionTransformer).register("transform-move", transformMoveTransformer).register("transform", transformTransformer).register(
945
+ ).register("filter", filterTransformer).register("backdrop-filter", filterTransformer).register("box-shadow", createCombineArrayTransformer(",")).register("background", backgroundTransformer).register("background-overlay", backgroundOverlayTransformer).register("background-color-overlay", backgroundColorOverlayTransformer).register("background-image-overlay", backgroundImageOverlayTransformer).register("background-gradient-overlay", backgroundGradientOverlayTransformer).register("gradient-color-stop", createCombineArrayTransformer(",")).register("color-stop", colorStopTransformer).register("background-image-position-offset", positionTransformer).register("background-image-size-scale", backgroundImageSizeScaleTransformer).register("image-src", imageSrcTransformer).register("image", imageTransformer).register("object-position", positionTransformer).register("transform-move", transformMoveTransformer).register("transform-scale", transformScaleTransformer).register("transform-rotate", transformRotateTransformer).register("transform-skew", transformSkewTransformer).register("transform", transformTransformer).register("transition", transitionTransformer).register(
816
946
  "layout-direction",
817
947
  createMultiPropsTransformer(["row", "column"], ({ propKey, key }) => `${key}-${propKey}`)
818
- ).register(
948
+ ).register("flex", flexTransformer).register(
819
949
  "border-width",
820
950
  createMultiPropsTransformer(
821
951
  ["block-start", "block-end", "inline-start", "inline-end"],
@@ -1214,12 +1344,13 @@ var undoablePasteElementStyle = () => undoable(
1214
1344
  originalStyle
1215
1345
  };
1216
1346
  if (styleId) {
1217
- newStyle.variants.forEach(({ meta, props }) => {
1347
+ newStyle.variants.forEach(({ meta, props, custom_css: customCss }) => {
1218
1348
  updateElementStyle({
1219
1349
  elementId,
1220
1350
  styleId,
1221
1351
  meta,
1222
- props
1352
+ props,
1353
+ custom_css: customCss
1223
1354
  });
1224
1355
  });
1225
1356
  } else {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-canvas",
3
3
  "description": "Elementor Editor Canvas",
4
- "version": "0.28.0",
4
+ "version": "3.32.0-20",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -19,11 +19,11 @@
19
19
  },
20
20
  "repository": {
21
21
  "type": "git",
22
- "url": "https://github.com/elementor/elementor-packages.git",
22
+ "url": "https://github.com/elementor/elementor.git",
23
23
  "directory": "packages/core/editor-canvas"
24
24
  },
25
25
  "bugs": {
26
- "url": "https://github.com/elementor/elementor-packages/issues"
26
+ "url": "https://github.com/elementor/elementor/issues"
27
27
  },
28
28
  "publishConfig": {
29
29
  "access": "public"
@@ -37,18 +37,18 @@
37
37
  "react-dom": "^18.3.1"
38
38
  },
39
39
  "dependencies": {
40
- "@elementor/editor": "0.21.1",
41
- "@elementor/editor-notifications": "1.4.1",
42
- "@elementor/editor-elements": "0.9.2",
43
- "@elementor/editor-props": "0.18.0",
44
- "@elementor/editor-responsive": "0.13.6",
45
- "@elementor/editor-styles": "0.6.14",
46
- "@elementor/editor-styles-repository": "0.10.7",
47
- "@elementor/editor-v1-adapters": "0.12.1",
48
- "@elementor/twing": "0.0.2",
49
- "@elementor/ui": "1.36.0",
50
- "@elementor/utils": "0.5.0",
51
- "@elementor/wp-media": "0.6.1",
40
+ "@elementor/editor": "3.32.0-20",
41
+ "@elementor/editor-notifications": "3.32.0-20",
42
+ "@elementor/editor-elements": "3.32.0-20",
43
+ "@elementor/editor-props": "3.32.0-20",
44
+ "@elementor/editor-responsive": "3.32.0-20",
45
+ "@elementor/editor-styles": "3.32.0-20",
46
+ "@elementor/editor-styles-repository": "3.32.0-20",
47
+ "@elementor/editor-v1-adapters": "3.32.0-20",
48
+ "@elementor/twing": "3.32.0-20",
49
+ "@elementor/ui": "1.36.2",
50
+ "@elementor/utils": "3.32.0-20",
51
+ "@elementor/wp-media": "3.32.0-20",
52
52
  "@floating-ui/react": "^0.27.5",
53
53
  "@wordpress/i18n": "^5.13.0"
54
54
  },
@@ -0,0 +1,272 @@
1
+ import { flexTransformer } from '../transformers/styles/flex-transformer';
2
+
3
+ type Flex = {
4
+ flexGrow?: number | null;
5
+ flexShrink?: number | null;
6
+ flexBasis?: { size: number; unit: string } | string | null;
7
+ };
8
+
9
+ describe( 'flexTransformer', () => {
10
+ describe( 'when no values are provided', () => {
11
+ it( 'should return null when all values are null', () => {
12
+ // Arrange.
13
+ const value: Flex = {
14
+ flexGrow: null,
15
+ flexShrink: null,
16
+ flexBasis: null,
17
+ };
18
+
19
+ // Act.
20
+ const result = flexTransformer( value, { key: 'flex' } );
21
+
22
+ // Assert.
23
+ expect( result ).toBeNull();
24
+ } );
25
+
26
+ it( 'should return null when all values are undefined', () => {
27
+ // Arrange.
28
+ const value: Flex = {};
29
+
30
+ // Act.
31
+ const result = flexTransformer( value, { key: 'flex' } );
32
+
33
+ // Assert.
34
+ expect( result ).toBeNull();
35
+ } );
36
+ } );
37
+
38
+ describe( 'when all three values are provided', () => {
39
+ it( 'should return "grow shrink basis" when all values are set', () => {
40
+ // Arrange.
41
+ const value: Flex = {
42
+ flexGrow: 2,
43
+ flexShrink: 1,
44
+ flexBasis: { size: 100, unit: 'px' },
45
+ };
46
+
47
+ // Act.
48
+ const result = flexTransformer( value, { key: 'flex' } );
49
+
50
+ // Assert.
51
+ expect( result ).toBe( '2 1 100px' );
52
+ } );
53
+
54
+ it( 'should handle string basis value', () => {
55
+ // Arrange.
56
+ const value: Flex = {
57
+ flexGrow: 1,
58
+ flexShrink: 0,
59
+ flexBasis: 'auto',
60
+ };
61
+
62
+ // Act.
63
+ const result = flexTransformer( value, { key: 'flex' } );
64
+
65
+ // Assert.
66
+ expect( result ).toBe( '1 0 auto' );
67
+ } );
68
+ } );
69
+
70
+ describe( 'when only two values are provided', () => {
71
+ it( 'should return "grow shrink" when grow and shrink are set', () => {
72
+ // Arrange.
73
+ const value: Flex = {
74
+ flexGrow: 1,
75
+ flexShrink: 2,
76
+ flexBasis: null,
77
+ };
78
+
79
+ // Act.
80
+ const result = flexTransformer( value, { key: 'flex' } );
81
+
82
+ // Assert.
83
+ expect( result ).toBe( '1 2' );
84
+ } );
85
+
86
+ it( 'should return "grow 1 basis" when grow and basis are set', () => {
87
+ // Arrange.
88
+ const value: Flex = {
89
+ flexGrow: 3,
90
+ flexShrink: null,
91
+ flexBasis: { size: 50, unit: '%' },
92
+ };
93
+
94
+ // Act.
95
+ const result = flexTransformer( value, { key: 'flex' } );
96
+
97
+ // Assert.
98
+ expect( result ).toBe( '3 1 50%' );
99
+ } );
100
+
101
+ it( 'should return "0 shrink basis" when shrink and basis are set', () => {
102
+ // Arrange.
103
+ const value: Flex = {
104
+ flexGrow: null,
105
+ flexShrink: 1,
106
+ flexBasis: { size: 200, unit: 'px' },
107
+ };
108
+
109
+ // Act.
110
+ const result = flexTransformer( value, { key: 'flex' } );
111
+
112
+ // Assert.
113
+ expect( result ).toBe( '0 1 200px' );
114
+ } );
115
+ } );
116
+
117
+ describe( 'when only one value is provided', () => {
118
+ it( 'should return "grow" when only grow is set', () => {
119
+ // Arrange.
120
+ const value: Flex = {
121
+ flexGrow: 1,
122
+ flexShrink: null,
123
+ flexBasis: null,
124
+ };
125
+
126
+ // Act.
127
+ const result = flexTransformer( value, { key: 'flex' } );
128
+
129
+ // Assert.
130
+ expect( result ).toBe( '1' );
131
+ } );
132
+
133
+ it( 'should return "0 shrink" when only shrink is set', () => {
134
+ // Arrange.
135
+ const value: Flex = {
136
+ flexGrow: null,
137
+ flexShrink: 2,
138
+ flexBasis: null,
139
+ };
140
+
141
+ // Act.
142
+ const result = flexTransformer( value, { key: 'flex' } );
143
+
144
+ // Assert.
145
+ expect( result ).toBe( '0 2' );
146
+ } );
147
+
148
+ it( 'should return "0 1 basis" when only basis is set', () => {
149
+ // Arrange.
150
+ const value: Flex = {
151
+ flexGrow: null,
152
+ flexShrink: null,
153
+ flexBasis: { size: 300, unit: 'px' },
154
+ };
155
+
156
+ // Act.
157
+ const result = flexTransformer( value, { key: 'flex' } );
158
+
159
+ // Assert.
160
+ expect( result ).toBe( '0 1 300px' );
161
+ } );
162
+
163
+ it( 'should handle string basis when only basis is set', () => {
164
+ // Arrange.
165
+ const value: Flex = {
166
+ flexGrow: null,
167
+ flexShrink: null,
168
+ flexBasis: 'content',
169
+ };
170
+
171
+ // Act.
172
+ const result = flexTransformer( value, { key: 'flex' } );
173
+
174
+ // Assert.
175
+ expect( result ).toBe( '0 1 content' );
176
+ } );
177
+ } );
178
+
179
+ describe( 'edge cases', () => {
180
+ it( 'should handle zero values correctly', () => {
181
+ // Arrange.
182
+ const value: Flex = {
183
+ flexGrow: 0,
184
+ flexShrink: 0,
185
+ flexBasis: { size: 0, unit: 'px' },
186
+ };
187
+
188
+ // Act.
189
+ const result = flexTransformer( value, { key: 'flex' } );
190
+
191
+ // Assert.
192
+ expect( result ).toBe( '0 0 0px' );
193
+ } );
194
+
195
+ it( 'should handle basis with empty unit', () => {
196
+ // Arrange.
197
+ const value: Flex = {
198
+ flexGrow: 1,
199
+ flexShrink: 1,
200
+ flexBasis: { size: 100, unit: '' },
201
+ };
202
+
203
+ // Act.
204
+ const result = flexTransformer( value, { key: 'flex' } );
205
+
206
+ // Assert.
207
+ expect( result ).toBe( '1 1 100' );
208
+ } );
209
+
210
+ it( 'should handle basis with undefined unit', () => {
211
+ // Arrange.
212
+ const value: Flex = {
213
+ flexGrow: 1,
214
+ flexShrink: 1,
215
+ flexBasis: { size: 100, unit: '' },
216
+ };
217
+
218
+ // Act.
219
+ const result = flexTransformer( value, { key: 'flex' } );
220
+
221
+ // Assert.
222
+ expect( result ).toBe( '1 1 100' );
223
+ } );
224
+
225
+ it( 'should handle basis object without size property', () => {
226
+ // Arrange.
227
+ const value: Flex = {
228
+ flexGrow: 1,
229
+ flexShrink: 1,
230
+ flexBasis: { unit: 'px' } as { size: number; unit: string },
231
+ };
232
+
233
+ // Act.
234
+ const result = flexTransformer( value, { key: 'flex' } );
235
+
236
+ // Assert.
237
+ expect( result ).toBe( '1 1 [object Object]' );
238
+ } );
239
+ } );
240
+
241
+ describe( 'CSS shorthand behavior', () => {
242
+ it( 'should follow CSS flex shorthand rules for single value', () => {
243
+ // Arrange.
244
+ const value: Flex = {
245
+ flexGrow: 2,
246
+ flexShrink: null,
247
+ flexBasis: null,
248
+ };
249
+
250
+ // Act.
251
+ const result = flexTransformer( value, { key: 'flex' } );
252
+
253
+ // Assert.
254
+ expect( result ).toBe( '2' );
255
+ } );
256
+
257
+ it( 'should follow CSS flex shorthand rules for two values', () => {
258
+ // Arrange.
259
+ const value: Flex = {
260
+ flexGrow: 1,
261
+ flexShrink: 2,
262
+ flexBasis: null,
263
+ };
264
+
265
+ // Act.
266
+ const result = flexTransformer( value, { key: 'flex' } );
267
+
268
+ // Assert.
269
+ expect( result ).toBe( '1 2' );
270
+ } );
271
+ } );
272
+ } );