@builder.io/sdk-react 0.2.1 → 0.2.2

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 (152) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/react/blocks/symbol/symbol.js +2 -1
  3. package/dist/react/components/render-block/render-block.js +2 -3
  4. package/dist/react/components/render-content/render-content.js +8 -7
  5. package/dist/react/components/render-content-variants/helpers.js +98 -0
  6. package/dist/react/components/render-content-variants/render-content-variants.js +45 -0
  7. package/dist/react/functions/get-block-properties.js +4 -1
  8. package/dist/react/functions/get-content/index.js +22 -15
  9. package/dist/react/functions/track/index.js +2 -1
  10. package/dist/react/helpers/logger.js +7 -0
  11. package/dist/rsc/blocks/symbol/symbol.js +2 -1
  12. package/dist/rsc/components/render-block/render-block.js +2 -3
  13. package/dist/rsc/components/render-content/render-content.js +1 -0
  14. package/dist/rsc/components/render-content-variants/helpers.js +99 -0
  15. package/dist/rsc/components/render-content-variants/render-content-variants.js +45 -0
  16. package/dist/rsc/functions/get-block-properties.js +4 -1
  17. package/dist/rsc/functions/get-content/index.js +22 -15
  18. package/dist/rsc/functions/track/index.js +2 -1
  19. package/dist/rsc/helpers/logger.js +7 -0
  20. package/package.json +1 -1
  21. package/packages/react/src/blocks/symbol/symbol.jsx +2 -1
  22. package/packages/react/src/components/render-block/render-block.jsx +2 -3
  23. package/packages/react/src/components/render-content/render-content.jsx +11 -8
  24. package/packages/react/src/functions/get-block-properties.js +4 -1
  25. package/packages/react/src/functions/get-content/index.js +21 -15
  26. package/packages/react/src/functions/track/index.js +2 -1
  27. package/packages/react/src/helpers/logger.js +9 -0
  28. package/packages/rsc/src/blocks/symbol/symbol.jsx +2 -4
  29. package/packages/rsc/src/components/render-block/render-block.jsx +2 -3
  30. package/packages/rsc/src/components/render-content/render-content.jsx +1 -0
  31. package/packages/rsc/src/functions/get-block-properties.js +4 -1
  32. package/packages/rsc/src/functions/get-content/index.js +21 -15
  33. package/packages/rsc/src/functions/track/index.js +2 -1
  34. package/packages/rsc/src/helpers/logger.js +9 -0
  35. package/src/blocks/BaseText.jsx +0 -9
  36. package/src/blocks/button/button.jsx +0 -27
  37. package/src/blocks/button/component-info.js +0 -41
  38. package/src/blocks/columns/columns.jsx +0 -172
  39. package/src/blocks/columns/component-info.js +0 -242
  40. package/src/blocks/custom-code/component-info.js +0 -31
  41. package/src/blocks/custom-code/custom-code.jsx +0 -64
  42. package/src/blocks/embed/component-info.js +0 -44
  43. package/src/blocks/embed/embed.jsx +0 -49
  44. package/src/blocks/embed/helpers.js +0 -9
  45. package/src/blocks/form/component-info.js +0 -262
  46. package/src/blocks/form/form.jsx +0 -253
  47. package/src/blocks/fragment/component-info.js +0 -11
  48. package/src/blocks/fragment/fragment.jsx +0 -5
  49. package/src/blocks/image/component-info.js +0 -151
  50. package/src/blocks/image/image.helpers.js +0 -48
  51. package/src/blocks/image/image.jsx +0 -122
  52. package/src/blocks/img/component-info.js +0 -20
  53. package/src/blocks/img/img.jsx +0 -17
  54. package/src/blocks/input/component-info.js +0 -74
  55. package/src/blocks/input/input.jsx +0 -19
  56. package/src/blocks/raw-text/component-info.js +0 -16
  57. package/src/blocks/raw-text/raw-text.jsx +0 -10
  58. package/src/blocks/section/component-info.js +0 -49
  59. package/src/blocks/section/section.jsx +0 -27
  60. package/src/blocks/select/component-info.js +0 -59
  61. package/src/blocks/select/select.jsx +0 -20
  62. package/src/blocks/submit-button/component-info.js +0 -28
  63. package/src/blocks/submit-button/submit-button.jsx +0 -9
  64. package/src/blocks/symbol/component-info.js +0 -43
  65. package/src/blocks/symbol/symbol.jsx +0 -84
  66. package/src/blocks/text/component-info.js +0 -24
  67. package/src/blocks/text/text.jsx +0 -10
  68. package/src/blocks/textarea/component-info.js +0 -47
  69. package/src/blocks/textarea/textarea.jsx +0 -13
  70. package/src/blocks/util.js +0 -8
  71. package/src/blocks/video/component-info.js +0 -106
  72. package/src/blocks/video/video.jsx +0 -58
  73. package/src/components/render-block/block-styles.jsx +0 -69
  74. package/src/components/render-block/render-block.helpers.js +0 -23
  75. package/src/components/render-block/render-block.jsx +0 -252
  76. package/src/components/render-block/render-component-with-context.jsx +0 -38
  77. package/src/components/render-block/render-component.jsx +0 -33
  78. package/src/components/render-block/render-repeated-block.jsx +0 -33
  79. package/src/components/render-block/types.js +0 -0
  80. package/src/components/render-blocks.jsx +0 -89
  81. package/src/components/render-content/builder-editing.jsx +0 -5
  82. package/src/components/render-content/components/render-styles.jsx +0 -64
  83. package/src/components/render-content/index.js +0 -4
  84. package/src/components/render-content/render-content.jsx +0 -370
  85. package/src/components/render-inlined-styles.jsx +0 -28
  86. package/src/constants/builder-registered-components.js +0 -54
  87. package/src/constants/device-sizes.js +0 -48
  88. package/src/constants/target.js +0 -4
  89. package/src/context/builder.context.js +0 -12
  90. package/src/context/types.js +0 -0
  91. package/src/functions/camel-to-kebab-case.js +0 -4
  92. package/src/functions/evaluate.js +0 -29
  93. package/src/functions/event-handler-name.js +0 -7
  94. package/src/functions/extract-text-styles.js +0 -22
  95. package/src/functions/fast-clone.js +0 -4
  96. package/src/functions/get-block-actions-handler.js +0 -11
  97. package/src/functions/get-block-actions.js +0 -18
  98. package/src/functions/get-block-component-options.js +0 -28
  99. package/src/functions/get-block-properties.js +0 -50
  100. package/src/functions/get-block-tag.js +0 -6
  101. package/src/functions/get-builder-search-params/fn.test.js +0 -13
  102. package/src/functions/get-builder-search-params/index.js +0 -38
  103. package/src/functions/get-content/ab-testing.js +0 -99
  104. package/src/functions/get-content/generate-content-url.js +0 -55
  105. package/src/functions/get-content/generate-content-url.test.js +0 -31
  106. package/src/functions/get-content/index.js +0 -65
  107. package/src/functions/get-content/types.js +0 -0
  108. package/src/functions/get-fetch.js +0 -14
  109. package/src/functions/get-global-this.js +0 -18
  110. package/src/functions/get-processed-block.js +0 -59
  111. package/src/functions/get-processed-block.test.js +0 -32
  112. package/src/functions/get-react-native-block-styles.js +0 -33
  113. package/src/functions/if-target.js +0 -15
  114. package/src/functions/is-browser.js +0 -6
  115. package/src/functions/is-editing.js +0 -7
  116. package/src/functions/is-iframe.js +0 -7
  117. package/src/functions/is-previewing.js +0 -14
  118. package/src/functions/on-change.js +0 -27
  119. package/src/functions/on-change.test.js +0 -19
  120. package/src/functions/register-component.js +0 -68
  121. package/src/functions/register.js +0 -29
  122. package/src/functions/sanitize-react-native-block-styles.js +0 -66
  123. package/src/functions/set-editor-settings.js +0 -15
  124. package/src/functions/set.js +0 -11
  125. package/src/functions/set.test.js +0 -16
  126. package/src/functions/track.js +0 -124
  127. package/src/functions/transform-block-properties.js +0 -6
  128. package/src/functions/transform-block.js +0 -6
  129. package/src/helpers/ab-tests.js +0 -16
  130. package/src/helpers/cookie.js +0 -81
  131. package/src/helpers/css.js +0 -34
  132. package/src/helpers/flatten.js +0 -34
  133. package/src/helpers/localStorage.js +0 -35
  134. package/src/helpers/nullable.js +0 -4
  135. package/src/helpers/sessionId.js +0 -52
  136. package/src/helpers/time.js +0 -5
  137. package/src/helpers/url.js +0 -13
  138. package/src/helpers/url.test.js +0 -21
  139. package/src/helpers/uuid.js +0 -13
  140. package/src/helpers/visitorId.js +0 -34
  141. package/src/index-helpers/blocks-exports.js +0 -22
  142. package/src/index-helpers/top-of-file.js +0 -4
  143. package/src/index.js +0 -14
  144. package/src/scripts/init-editing.js +0 -93
  145. package/src/types/builder-block.js +0 -0
  146. package/src/types/builder-content.js +0 -0
  147. package/src/types/can-track.js +0 -0
  148. package/src/types/components.js +0 -0
  149. package/src/types/deep-partial.js +0 -0
  150. package/src/types/element.js +0 -0
  151. package/src/types/targets.js +0 -0
  152. package/src/types/typescript.js +0 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ### 0.2.2
2
+
3
+ - Fix: dynamic bindings for Link URLs.
4
+ - Fix: previewing content that includes a symbol whose `model` property is a `page`.
5
+ - Fix: "Show If"/"Hide If" bindings when the initial value is `undefined`.
6
+
1
7
  ### 0.2.1
2
8
 
3
9
  - No Changes.
@@ -4,6 +4,7 @@ import RenderContent from "../../components/render-content/render-content";
4
4
  import BuilderContext from "../../context/builder.context.js";
5
5
  import { getContent } from "../../functions/get-content/index.js";
6
6
  import { TARGET } from "../../constants/target";
7
+ import { logger } from "../../helpers/logger";
7
8
  function Symbol(props) {
8
9
  const [className, setClassName] = useState(() => [
9
10
  ...(TARGET === "vue2" || TARGET === "vue3"
@@ -46,7 +47,7 @@ function Symbol(props) {
46
47
  }
47
48
  })
48
49
  .catch((err) => {
49
- console.error("[Builder.io]: Could not fetch symbol content: ", err);
50
+ logger.error("Could not fetch symbol content: ", err);
50
51
  });
51
52
  }
52
53
  }
@@ -11,7 +11,6 @@ import { TARGET } from "../../constants/target.js";
11
11
  import { extractTextStyles } from "../../functions/extract-text-styles.js";
12
12
  import RenderComponent from "./render-component";
13
13
  import { getReactNativeBlockStyles } from "../../functions/get-react-native-block-styles.js";
14
- import { checkIsDefined } from "../../helpers/nullable.js";
15
14
  function RenderBlock(props) {
16
15
  const [component, setComponent] = useState(() => getComponent({
17
16
  block: props.block,
@@ -33,10 +32,10 @@ function RenderBlock(props) {
33
32
  }
34
33
  const [tag, setTag] = useState(() => props.block.tagName || "div");
35
34
  function canShowBlock() {
36
- if (checkIsDefined(useBlock().hide)) {
35
+ if ("hide" in useBlock()) {
37
36
  return !useBlock().hide;
38
37
  }
39
- if (checkIsDefined(useBlock().show)) {
38
+ if ("show" in useBlock()) {
40
39
  return useBlock().show;
41
40
  }
42
41
  return true;
@@ -17,6 +17,7 @@ import { checkIsDefined } from "../../helpers/nullable.js";
17
17
  import { getInteractionPropertiesForEvent } from "../../functions/track/interaction.js";
18
18
  import { getContentInitialValue, getContextStateInitialValue, } from "./render-content.helpers.js";
19
19
  import { TARGET } from "../../constants/target.js";
20
+ import { logger } from "../../helpers/logger.js";
20
21
  function RenderContent(props) {
21
22
  const elementRef = useRef(null);
22
23
  const [forceReRenderCount, setForceReRenderCount] = useState(() => 0);
@@ -186,7 +187,7 @@ function RenderContent(props) {
186
187
  }
187
188
  useEffect(() => {
188
189
  if (!props.apiKey) {
189
- console.error("[Builder.io]: No API key provided to `RenderContent` component. This can cause issues. Please provide an API key using the `apiKey` prop.");
190
+ logger.error("No API key provided to `RenderContent` component. This can cause issues. Please provide an API key using the `apiKey` prop.");
190
191
  }
191
192
  if (isBrowser()) {
192
193
  if (isEditing()) {
@@ -225,19 +226,19 @@ function RenderContent(props) {
225
226
  // override normal content in preview mode
226
227
  if (isPreviewing()) {
227
228
  const searchParams = new URL(location.href).searchParams;
228
- const searchParamPreview = searchParams.get("builder.preview");
229
+ const searchParamPreviewModel = searchParams.get("builder.preview");
230
+ const searchParamPreviewId = searchParams.get(`builder.preview.${searchParamPreviewModel}`);
229
231
  const previewApiKey = searchParams.get("apiKey") || searchParams.get("builder.space");
230
232
  /**
231
233
  * Make sure that:
232
234
  * - the preview model name is the same as the one we're rendering, since there can be multiple models rendered * at the same time, e.g. header/page/footer. * - the API key is the same, since we don't want to preview content from other organizations.
233
- *
234
- * TO-DO: should we check that the preview item ID is the same as the initial one being rendered? Or would
235
- * this break scenarios where the item is not published yet?
235
+ * - if there is content, that the preview ID is the same as that of the one we receive.
236
236
  *
237
237
  * TO-DO: should we only update the state when there is a change?
238
238
  **/
239
- if (searchParamPreview === props.model &&
240
- previewApiKey === props.apiKey) {
239
+ if (searchParamPreviewModel === props.model &&
240
+ previewApiKey === props.apiKey &&
241
+ (!props.content || searchParamPreviewId === props.content.id)) {
241
242
  getContent({
242
243
  model: props.model,
243
244
  apiKey: props.apiKey,
@@ -0,0 +1,98 @@
1
+ const variantScriptFn = function main(contentId, variants) {
2
+ console.log("Running variant script", JSON.stringify({ contentId, variants }));
3
+ if (!navigator.cookieEnabled) {
4
+ console.log("Cookies are disabled, skipping variant script");
5
+ return;
6
+ }
7
+ const templateSelectorById = (id) => `template[data-template-variant-id="${id}"]`;
8
+ function removeTemplates() {
9
+ console.log("Removing variants");
10
+ variants.forEach((template) => {
11
+ const el2 = document.querySelector(templateSelectorById(template.id));
12
+ if (el2) {
13
+ console.log("Removing variant", template.id);
14
+ el2.remove();
15
+ }
16
+ });
17
+ const el = document.getElementById(`variants-script-${contentId}`);
18
+ if (el) {
19
+ console.log("Removing variant script");
20
+ el.remove();
21
+ }
22
+ }
23
+ if (typeof document.createElement("template").content === "undefined") {
24
+ console.log("Template polyfill not found, skipping variant script");
25
+ removeTemplates();
26
+ return;
27
+ }
28
+ function setCookie(name, value, days) {
29
+ let expires = "";
30
+ if (days) {
31
+ const date = new Date();
32
+ date.setTime(date.getTime() + days * 24 * 60 * 60 * 1e3);
33
+ expires = "; expires=" + date.toUTCString();
34
+ }
35
+ document.cookie = name + "=" + (value || "") + expires + "; path=/; Secure; SameSite=None";
36
+ }
37
+ function getCookie(name) {
38
+ const nameEQ = name + "=";
39
+ const ca = document.cookie.split(";");
40
+ for (let i = 0; i < ca.length; i++) {
41
+ let c = ca[i];
42
+ while (c.charAt(0) == " ")
43
+ c = c.substring(1, c.length);
44
+ if (c.indexOf(nameEQ) == 0)
45
+ return c.substring(nameEQ.length, c.length);
46
+ }
47
+ return null;
48
+ }
49
+ function getAndSetVariantId() {
50
+ const cookieName = `builder.tests.${contentId}`;
51
+ const variantInCookie = getCookie(cookieName);
52
+ const availableIDs = variants.map((vr) => vr.id).concat(contentId);
53
+ if (variantInCookie && availableIDs.indexOf(variantInCookie) > -1) {
54
+ return variantInCookie;
55
+ }
56
+ let n = 0;
57
+ const random = Math.random();
58
+ for (let i = 0; i < variants.length; i++) {
59
+ const variant = variants[i];
60
+ const testRatio = variant.testRatio;
61
+ n += testRatio;
62
+ if (random < n) {
63
+ setCookie(cookieName, variant.id);
64
+ return variant.id;
65
+ }
66
+ }
67
+ setCookie(cookieName, contentId);
68
+ return contentId;
69
+ }
70
+ const variantId = getAndSetVariantId();
71
+ if (variantId !== contentId) {
72
+ console.log("Variant found", variantId);
73
+ const winningTemplate = document.querySelector(templateSelectorById(variantId));
74
+ console.log("winningTemplate", winningTemplate);
75
+ if (!winningTemplate) {
76
+ console.log("No template found for variant", variantId);
77
+ console.log("selector: ", templateSelectorById(variantId));
78
+ return;
79
+ }
80
+ const templatesParent = winningTemplate.parentNode;
81
+ if (templatesParent) {
82
+ templatesParent.replaceChild(winningTemplate.content.firstElementChild, winningTemplate);
83
+ }
84
+ }
85
+ else if (variants.length > 0) {
86
+ console.log("No variant found, removing all variants");
87
+ }
88
+ removeTemplates();
89
+ console.log("Variant script complete");
90
+ };
91
+ const getVariantsScriptString = (variants, contentId) => {
92
+ const fnStr = variantScriptFn.toString().replace(/\s+/g, " ");
93
+ return `
94
+ ${fnStr}
95
+ main("${contentId}", ${JSON.stringify(variants)})
96
+ `;
97
+ };
98
+ export { getVariantsScriptString };
@@ -0,0 +1,45 @@
1
+ import * as React from "react";
2
+ import { useState, useEffect } from "react";
3
+ import { isBrowser } from "../../functions/is-browser";
4
+ import { getVariantsScriptString } from "./helpers";
5
+ import RenderContent from "../render-content/render-content";
6
+ import { checkIsDefined } from "../../helpers/nullable";
7
+ import { handleABTestingSync } from "../../helpers/ab-tests";
8
+ import { TARGET } from "../../constants/target";
9
+ function RenderContentVariants(props) {
10
+ const [variantScriptStr, setVariantScriptStr] = useState(() => getVariantsScriptString(Object.values(props.content?.variations || {}).map((value) => ({
11
+ id: value.id,
12
+ testRatio: value.testRatio,
13
+ })), props.content?.id || ""));
14
+ const [variants, setVariants] = useState(() => [
15
+ ...Object.values(props.content?.variations || {}),
16
+ props.content,
17
+ ]);
18
+ const [contentToUse, setContentToUse] = useState(() => ((isBrowser() && props.canTrack) || !isBrowser()) &&
19
+ Object.keys(props.content?.variations || {}).length > 0
20
+ ? undefined
21
+ : handleABTestingSync({
22
+ item: props.content,
23
+ canTrack: checkIsDefined(props.canTrack) ? props.canTrack : true,
24
+ }));
25
+ const [shouldRenderVariants, setShouldRenderVariants] = useState(() => ((isBrowser() && props.canTrack) || !isBrowser()) &&
26
+ Object.keys(props.content?.variations || {}).length > 0);
27
+ useEffect(() => {
28
+ if (TARGET === "svelte" && shouldRenderVariants) {
29
+ // get first template in loop
30
+ const templates = document.querySelectorAll(`template[data-template-variant-id="${variants[0]?.id}"]`);
31
+ const lastTemplate = templates[templates.length - 1];
32
+ // create and append script as sibling of template
33
+ const script = document.createElement("script");
34
+ script.id = `variants-script-${props.content?.id}`;
35
+ script.innerHTML = variantScriptStr;
36
+ lastTemplate?.parentNode?.append(script, lastTemplate);
37
+ }
38
+ }, []);
39
+ return (React.createElement(React.Fragment, null, shouldRenderVariants ? (React.createElement(React.Fragment, null,
40
+ variants?.map((variant) => (React.createElement("template", { key: variant?.id, "data-template-variant-id": variant?.id },
41
+ React.createElement(RenderContent, { content: variant, apiKey: props.apiKey, apiVersion: props.apiVersion, canTrack: props.canTrack, customComponents: props.customComponents })))),
42
+ React.createElement("script", { id: `variants-script-${props.content?.id}`, dangerouslySetInnerHTML: { __html: variantScriptStr } }),
43
+ React.createElement(RenderContent, { content: contentToUse, apiKey: props.apiKey, apiVersion: props.apiVersion, canTrack: props.canTrack, customComponents: props.customComponents }))) : (React.createElement(RenderContent, { content: contentToUse, apiKey: props.apiKey, apiVersion: props.apiVersion, canTrack: props.canTrack, customComponents: props.customComponents }))));
44
+ }
45
+ export default RenderContentVariants;
@@ -20,9 +20,12 @@ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
20
  import { TARGET } from "../constants/target.js";
21
21
  import { convertStyleMapToCSSArray } from "../helpers/css.js";
22
22
  import { transformBlockProperties } from "./transform-block-properties.js";
23
+ const extractRelevantRootBlockProperties = (block) => {
24
+ return { href: block.href };
25
+ };
23
26
  function getBlockProperties(block) {
24
27
  var _a;
25
- const properties = __spreadProps(__spreadValues({}, block.properties), {
28
+ const properties = __spreadProps(__spreadValues(__spreadValues({}, extractRelevantRootBlockProperties(block)), block.properties), {
26
29
  "builder-id": block.id,
27
30
  style: getStyleAttribute(block.style),
28
31
  class: [block.id, "builder-block", block.class, (_a = block.properties) == null ? void 0 : _a.class].filter(Boolean).join(" ")
@@ -39,13 +39,14 @@ var __async = (__this, __arguments, generator) => {
39
39
  step((generator = generator.apply(__this, __arguments)).next());
40
40
  });
41
41
  };
42
+ import { logger } from "../../helpers/logger.js";
42
43
  import { fetch } from "../get-fetch.js";
43
44
  import { handleABTesting } from "./ab-testing.js";
44
45
  import { generateContentUrl } from "./generate-content-url.js";
45
46
  function getContent(options) {
46
47
  return __async(this, null, function* () {
47
48
  const allContent = yield getAllContent(__spreadProps(__spreadValues({}, options), { limit: 1 }));
48
- if ("results" in allContent) {
49
+ if (allContent && "results" in allContent) {
49
50
  return (allContent == null ? void 0 : allContent.results[0]) || null;
50
51
  }
51
52
  return null;
@@ -53,25 +54,31 @@ function getContent(options) {
53
54
  }
54
55
  function getAllContent(options) {
55
56
  return __async(this, null, function* () {
56
- const url = generateContentUrl(options);
57
- const res = yield fetch(url.href);
58
- const content = yield res.json();
59
- if ("status" in content && !("results" in content)) {
60
- console.error("[Builder.io]: Error fetching data. ", content, options);
61
- return content;
62
- }
63
- const canTrack = options.canTrack !== false;
64
57
  try {
65
- if (canTrack && Array.isArray(content.results)) {
66
- for (const item of content.results) {
67
- yield handleABTesting({ item, canTrack });
58
+ const url = generateContentUrl(options);
59
+ const res = yield fetch(url.href);
60
+ const content = yield res.json();
61
+ if ("status" in content && !("results" in content)) {
62
+ logger.error("Error fetching data. ", content, options);
63
+ return content;
64
+ }
65
+ const canTrack = options.canTrack !== false;
66
+ try {
67
+ if (canTrack && Array.isArray(content.results)) {
68
+ for (const item of content.results) {
69
+ yield handleABTesting({ item, canTrack });
70
+ }
68
71
  }
69
72
  }
73
+ catch (e) {
74
+ logger.error("Could not setup A/B testing. ", e);
75
+ }
76
+ return content;
70
77
  }
71
- catch (e) {
72
- console.error("[Builder.io]: Could not setup A/B testing. ", e);
78
+ catch (error) {
79
+ logger.error("Error fetching data. ", error);
80
+ return null;
73
81
  }
74
- return content;
75
82
  });
76
83
  }
77
84
  export { getAllContent, getContent };
@@ -52,6 +52,7 @@ var __async = (__this, __arguments, generator) => {
52
52
  });
53
53
  };
54
54
  import { TARGET } from "../../constants/target.js";
55
+ import { logger } from "../../helpers/logger.js";
55
56
  import { getSessionId } from "../../helpers/sessionId.js";
56
57
  import { getVisitorId } from "../../helpers/visitorId.js";
57
58
  import { isBrowser } from "../is-browser.js";
@@ -90,7 +91,7 @@ const createEvent = (_a) => __async(void 0, null, function* () {
90
91
  function _track(eventProps) {
91
92
  return __async(this, null, function* () {
92
93
  if (!eventProps.apiKey) {
93
- console.error("[Builder.io]: Missing API key for track call. Please provide your API key.");
94
+ logger.error("Missing API key for track call. Please provide your API key.");
94
95
  return;
95
96
  }
96
97
  if (!eventProps.canTrack) {
@@ -0,0 +1,7 @@
1
+ const MSG_PREFIX = "[Builder.io]: ";
2
+ const logger = {
3
+ log: (...message) => console.log(MSG_PREFIX, ...message),
4
+ error: (...message) => console.error(MSG_PREFIX, ...message),
5
+ warn: (...message) => console.warn(MSG_PREFIX, ...message)
6
+ };
7
+ export { logger };
@@ -3,6 +3,7 @@ import RenderContent from "../../components/render-content/render-content";
3
3
  import BuilderContext from "../../context/builder.context.js";
4
4
  import { getContent } from "../../functions/get-content/index.js";
5
5
  import { TARGET } from "../../constants/target";
6
+ import { logger } from "../../helpers/logger";
6
7
  function Symbol(props) {
7
8
  const _context = { ...props["_context"] };
8
9
  const state = {
@@ -47,7 +48,7 @@ function Symbol(props) {
47
48
  }
48
49
  })
49
50
  .catch((err) => {
50
- console.error("[Builder.io]: Could not fetch symbol content: ", err);
51
+ logger.error("Could not fetch symbol content: ", err);
51
52
  });
52
53
  }
53
54
  },
@@ -10,7 +10,6 @@ import { TARGET } from "../../constants/target.js";
10
10
  import { extractTextStyles } from "../../functions/extract-text-styles.js";
11
11
  import RenderComponent from "./render-component";
12
12
  import { getReactNativeBlockStyles } from "../../functions/get-react-native-block-styles.js";
13
- import { checkIsDefined } from "../../helpers/nullable.js";
14
13
  function RenderBlock(props) {
15
14
  const _context = { ...props["_context"] };
16
15
  const state = {
@@ -34,10 +33,10 @@ function RenderBlock(props) {
34
33
  },
35
34
  tag: props.block.tagName || "div",
36
35
  get canShowBlock() {
37
- if (checkIsDefined(state.useBlock.hide)) {
36
+ if ("hide" in state.useBlock) {
38
37
  return !state.useBlock.hide;
39
38
  }
40
- if (checkIsDefined(state.useBlock.show)) {
39
+ if ("show" in state.useBlock) {
41
40
  return state.useBlock.show;
42
41
  }
43
42
  return true;
@@ -16,6 +16,7 @@ import { checkIsDefined } from "../../helpers/nullable.js";
16
16
  import { getInteractionPropertiesForEvent } from "../../functions/track/interaction.js";
17
17
  import { getContentInitialValue, getContextStateInitialValue, } from "./render-content.helpers.js";
18
18
  import { TARGET } from "../../constants/target.js";
19
+ import { logger } from "../../helpers/logger.js";
19
20
  function RenderContent(props) {
20
21
  const _context = { ...props["_context"] };
21
22
  const state = {
@@ -0,0 +1,99 @@
1
+ const variantScriptFn = function main(contentId, variants) {
2
+ console.log("Running variant script", JSON.stringify({ contentId, variants }));
3
+ if (!navigator.cookieEnabled) {
4
+ console.log("Cookies are disabled, skipping variant script");
5
+ return;
6
+ }
7
+ const templateSelectorById = (id) => `template[data-template-variant-id="${id}"]`;
8
+ function removeTemplates() {
9
+ console.log("Removing variants");
10
+ variants.forEach((template) => {
11
+ const el2 = document.querySelector(templateSelectorById(template.id));
12
+ if (el2) {
13
+ console.log("Removing variant", template.id);
14
+ el2.remove();
15
+ }
16
+ });
17
+ const el = document.getElementById(`variants-script-${contentId}`);
18
+ if (el) {
19
+ console.log("Removing variant script");
20
+ el.remove();
21
+ }
22
+ }
23
+ if (typeof document.createElement("template").content === "undefined") {
24
+ console.log("Template polyfill not found, skipping variant script");
25
+ removeTemplates();
26
+ return;
27
+ }
28
+ function setCookie(name, value, days) {
29
+ let expires = "";
30
+ if (days) {
31
+ const date = new Date();
32
+ date.setTime(date.getTime() + days * 24 * 60 * 60 * 1e3);
33
+ expires = "; expires=" + date.toUTCString();
34
+ }
35
+ document.cookie = name + "=" + (value || "") + expires + "; path=/; Secure; SameSite=None";
36
+ }
37
+ function getCookie(name) {
38
+ const nameEQ = name + "=";
39
+ const ca = document.cookie.split(";");
40
+ for (let i = 0; i < ca.length; i++) {
41
+ let c = ca[i];
42
+ while (c.charAt(0) == " ")
43
+ c = c.substring(1, c.length);
44
+ if (c.indexOf(nameEQ) == 0)
45
+ return c.substring(nameEQ.length, c.length);
46
+ }
47
+ return null;
48
+ }
49
+ function getAndSetVariantId() {
50
+ const cookieName = `builder.tests.${contentId}`;
51
+ const variantInCookie = getCookie(cookieName);
52
+ const availableIDs = variants.map((vr) => vr.id).concat(contentId);
53
+ if (variantInCookie && availableIDs.indexOf(variantInCookie) > -1) {
54
+ return variantInCookie;
55
+ }
56
+ let n = 0;
57
+ const random = Math.random();
58
+ for (let i = 0; i < variants.length; i++) {
59
+ const variant = variants[i];
60
+ const testRatio = variant.testRatio;
61
+ n += testRatio;
62
+ if (random < n) {
63
+ setCookie(cookieName, variant.id);
64
+ return variant.id;
65
+ }
66
+ }
67
+ setCookie(cookieName, contentId);
68
+ return contentId;
69
+ }
70
+ const variantId = getAndSetVariantId();
71
+ if (variantId !== contentId) {
72
+ console.log("Variant found", variantId);
73
+ const winningTemplate = document.querySelector(templateSelectorById(variantId));
74
+ console.log("winningTemplate", winningTemplate);
75
+ if (!winningTemplate) {
76
+ console.log("No template found for variant", variantId);
77
+ console.log("selector: ", templateSelectorById(variantId));
78
+ return;
79
+ }
80
+ const templatesParent = winningTemplate.parentNode;
81
+ if (templatesParent) {
82
+ const defaultContent = templatesParent.querySelector(`div[builder-content-id="${contentId}"]`);
83
+ templatesParent.replaceChild(winningTemplate.content.firstElementChild, defaultContent);
84
+ }
85
+ }
86
+ else if (variants.length > 0) {
87
+ console.log("No variant found, removing all variants");
88
+ }
89
+ removeTemplates();
90
+ console.log("Variant script complete");
91
+ };
92
+ const getVariantsScriptString = (variants, contentId) => {
93
+ const fnStr = variantScriptFn.toString().replace(/\s+/g, " ");
94
+ return `
95
+ ${fnStr}
96
+ main("${contentId}", ${JSON.stringify(variants)})
97
+ `;
98
+ };
99
+ export { getVariantsScriptString };
@@ -0,0 +1,45 @@
1
+ import * as React from "react";
2
+ import { useEffect } from "react";
3
+ import { isBrowser } from "../../functions/is-browser";
4
+ import { getVariantsScriptString } from "./helpers";
5
+ import RenderContent from "../render-content/render-content";
6
+ import { checkIsDefined } from "../../helpers/nullable";
7
+ import { handleABTestingSync } from "../../helpers/ab-tests";
8
+ import { TARGET } from "../../constants/target";
9
+ function RenderContentVariants(props) {
10
+ const _context = { ...props["_context"] };
11
+ const state = {
12
+ variantScriptStr: getVariantsScriptString(Object.values(props.content?.variations || {}).map((value) => ({
13
+ id: value.id,
14
+ testRatio: value.testRatio,
15
+ })), props.content?.id || ""),
16
+ contentToUse: ((isBrowser() && props.canTrack) || !isBrowser()) &&
17
+ Object.keys(props.content?.variations || {}).length > 0
18
+ ? undefined
19
+ : handleABTestingSync({
20
+ item: props.content,
21
+ canTrack: checkIsDefined(props.canTrack) ? props.canTrack : true,
22
+ }),
23
+ shouldRenderVariants: ((isBrowser() && props.canTrack) || !isBrowser()) &&
24
+ Object.keys(props.content?.variations || {}).length > 0,
25
+ variants: null,
26
+ };
27
+ useEffect(() => {
28
+ if (TARGET === "svelte" && state.shouldRenderVariants) {
29
+ // get first template in loop
30
+ const templates = document.querySelectorAll(`template[data-template-variant-id="${state.variants[0]?.id}"]`);
31
+ const lastTemplate = templates[templates.length - 1];
32
+ // create and append script as sibling of template
33
+ const script = document.createElement("script");
34
+ script.id = `variants-script-${props.content?.id}`;
35
+ script.innerHTML = state.variantScriptStr;
36
+ lastTemplate?.parentNode?.append(script, lastTemplate);
37
+ }
38
+ }, []);
39
+ return (React.createElement(React.Fragment, null, state.shouldRenderVariants ? (React.createElement(React.Fragment, null,
40
+ Object.values(props.content.variations)?.map((variant) => (React.createElement("template", { key: variant?.id, "data-template-variant-id": variant?.id },
41
+ React.createElement(RenderContent, { content: variant, apiKey: props.apiKey, apiVersion: props.apiVersion, canTrack: props.canTrack, customComponents: props.customComponents, _context: _context })))),
42
+ React.createElement("script", { id: `variants-script-${props.content?.id}`, dangerouslySetInnerHTML: { __html: state.variantScriptStr } }),
43
+ React.createElement(RenderContent, { content: state.contentToUse, apiKey: props.apiKey, apiVersion: props.apiVersion, canTrack: props.canTrack, customComponents: props.customComponents, _context: _context }))) : (React.createElement(RenderContent, { content: state.contentToUse, apiKey: props.apiKey, apiVersion: props.apiVersion, canTrack: props.canTrack, customComponents: props.customComponents, _context: _context }))));
44
+ }
45
+ export default RenderContentVariants;
@@ -20,9 +20,12 @@ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
20
  import { TARGET } from "../constants/target.js";
21
21
  import { convertStyleMapToCSSArray } from "../helpers/css.js";
22
22
  import { transformBlockProperties } from "./transform-block-properties.js";
23
+ const extractRelevantRootBlockProperties = (block) => {
24
+ return { href: block.href };
25
+ };
23
26
  function getBlockProperties(block) {
24
27
  var _a;
25
- const properties = __spreadProps(__spreadValues({}, block.properties), {
28
+ const properties = __spreadProps(__spreadValues(__spreadValues({}, extractRelevantRootBlockProperties(block)), block.properties), {
26
29
  "builder-id": block.id,
27
30
  style: getStyleAttribute(block.style),
28
31
  class: [block.id, "builder-block", block.class, (_a = block.properties) == null ? void 0 : _a.class].filter(Boolean).join(" ")
@@ -39,13 +39,14 @@ var __async = (__this, __arguments, generator) => {
39
39
  step((generator = generator.apply(__this, __arguments)).next());
40
40
  });
41
41
  };
42
+ import { logger } from "../../helpers/logger.js";
42
43
  import { fetch } from "../get-fetch.js";
43
44
  import { handleABTesting } from "./ab-testing.js";
44
45
  import { generateContentUrl } from "./generate-content-url.js";
45
46
  function getContent(options) {
46
47
  return __async(this, null, function* () {
47
48
  const allContent = yield getAllContent(__spreadProps(__spreadValues({}, options), { limit: 1 }));
48
- if ("results" in allContent) {
49
+ if (allContent && "results" in allContent) {
49
50
  return (allContent == null ? void 0 : allContent.results[0]) || null;
50
51
  }
51
52
  return null;
@@ -53,25 +54,31 @@ function getContent(options) {
53
54
  }
54
55
  function getAllContent(options) {
55
56
  return __async(this, null, function* () {
56
- const url = generateContentUrl(options);
57
- const res = yield fetch(url.href);
58
- const content = yield res.json();
59
- if ("status" in content && !("results" in content)) {
60
- console.error("[Builder.io]: Error fetching data. ", content, options);
61
- return content;
62
- }
63
- const canTrack = options.canTrack !== false;
64
57
  try {
65
- if (canTrack && Array.isArray(content.results)) {
66
- for (const item of content.results) {
67
- yield handleABTesting({ item, canTrack });
58
+ const url = generateContentUrl(options);
59
+ const res = yield fetch(url.href);
60
+ const content = yield res.json();
61
+ if ("status" in content && !("results" in content)) {
62
+ logger.error("Error fetching data. ", content, options);
63
+ return content;
64
+ }
65
+ const canTrack = options.canTrack !== false;
66
+ try {
67
+ if (canTrack && Array.isArray(content.results)) {
68
+ for (const item of content.results) {
69
+ yield handleABTesting({ item, canTrack });
70
+ }
68
71
  }
69
72
  }
73
+ catch (e) {
74
+ logger.error("Could not setup A/B testing. ", e);
75
+ }
76
+ return content;
70
77
  }
71
- catch (e) {
72
- console.error("[Builder.io]: Could not setup A/B testing. ", e);
78
+ catch (error) {
79
+ logger.error("Error fetching data. ", error);
80
+ return null;
73
81
  }
74
- return content;
75
82
  });
76
83
  }
77
84
  export { getAllContent, getContent };
@@ -52,6 +52,7 @@ var __async = (__this, __arguments, generator) => {
52
52
  });
53
53
  };
54
54
  import { TARGET } from "../../constants/target.js";
55
+ import { logger } from "../../helpers/logger.js";
55
56
  import { getSessionId } from "../../helpers/sessionId.js";
56
57
  import { getVisitorId } from "../../helpers/visitorId.js";
57
58
  import { isBrowser } from "../is-browser.js";
@@ -90,7 +91,7 @@ const createEvent = (_a) => __async(void 0, null, function* () {
90
91
  function _track(eventProps) {
91
92
  return __async(this, null, function* () {
92
93
  if (!eventProps.apiKey) {
93
- console.error("[Builder.io]: Missing API key for track call. Please provide your API key.");
94
+ logger.error("Missing API key for track call. Please provide your API key.");
94
95
  return;
95
96
  }
96
97
  if (!eventProps.canTrack) {
@@ -0,0 +1,7 @@
1
+ const MSG_PREFIX = "[Builder.io]: ";
2
+ const logger = {
3
+ log: (...message) => console.log(MSG_PREFIX, ...message),
4
+ error: (...message) => console.error(MSG_PREFIX, ...message),
5
+ warn: (...message) => console.warn(MSG_PREFIX, ...message)
6
+ };
7
+ export { logger };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@builder.io/sdk-react",
3
3
  "description": "Builder.io SDK for React",
4
- "version": "0.2.1",
4
+ "version": "0.2.2",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "exports": {