@builder.io/sdk-react 0.4.3 → 0.4.5

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 (64) hide show
  1. package/dist/sdk/blocks/columns/columns.js +6 -6
  2. package/dist/sdk/blocks/symbol/symbol.js +7 -5
  3. package/dist/sdk/blocks/text/text.d.ts +1 -1
  4. package/dist/sdk/blocks/text/text.js +1 -1
  5. package/dist/sdk/components/block/block.d.ts +11 -0
  6. package/dist/sdk/components/block/block.helpers.d.ts +12 -0
  7. package/dist/sdk/components/block/block.helpers.js +86 -0
  8. package/dist/sdk/components/block/block.js +124 -0
  9. package/dist/sdk/components/block/components/block-styles.d.ts +9 -0
  10. package/dist/sdk/components/block/components/block-styles.js +65 -0
  11. package/dist/sdk/components/block/components/component.d.ts +20 -0
  12. package/dist/sdk/components/block/components/component.js +11 -0
  13. package/dist/sdk/components/block/components/repeated-block.d.ts +11 -0
  14. package/dist/sdk/components/block/components/repeated-block.js +11 -0
  15. package/dist/sdk/components/block/types.d.ts +6 -0
  16. package/dist/sdk/components/block/types.js +1 -0
  17. package/dist/sdk/components/blocks/blocks-wrapper.d.ts +13 -0
  18. package/dist/sdk/components/blocks/blocks-wrapper.js +38 -0
  19. package/dist/sdk/components/blocks/blocks.d.ts +10 -0
  20. package/dist/sdk/components/blocks/blocks.js +11 -0
  21. package/dist/sdk/components/content/components/content-styles.d.ts +9 -0
  22. package/dist/sdk/components/content/components/content-styles.helpers.d.ts +15 -0
  23. package/dist/sdk/components/content/components/content-styles.helpers.js +59 -0
  24. package/dist/sdk/components/content/components/content-styles.js +32 -0
  25. package/dist/sdk/components/content/components/enable-editor.d.ts +13 -0
  26. package/dist/sdk/components/content/components/enable-editor.js +278 -0
  27. package/dist/sdk/components/content/content.d.ts +4 -0
  28. package/dist/sdk/components/content/content.helpers.d.ts +7 -0
  29. package/dist/sdk/components/content/content.helpers.js +30 -0
  30. package/dist/sdk/components/content/content.js +97 -0
  31. package/dist/sdk/components/content/content.types.d.ts +38 -0
  32. package/dist/sdk/components/content/content.types.js +1 -0
  33. package/dist/sdk/components/content/index.d.ts +1 -0
  34. package/dist/sdk/components/content/index.js +1 -0
  35. package/dist/sdk/components/content/wrap-component-ref.d.ts +6 -0
  36. package/dist/sdk/components/content/wrap-component-ref.js +6 -0
  37. package/dist/sdk/components/content-variants/content-variants.d.ts +5 -0
  38. package/dist/sdk/components/content-variants/content-variants.js +37 -0
  39. package/dist/sdk/components/content-variants/helpers.d.ts +17 -0
  40. package/dist/sdk/components/content-variants/helpers.js +184 -0
  41. package/dist/sdk/components/inlined-script.d.ts +7 -0
  42. package/dist/sdk/components/inlined-script.js +6 -0
  43. package/dist/sdk/components/inlined-styles.d.ts +7 -0
  44. package/dist/sdk/components/inlined-styles.js +6 -0
  45. package/dist/sdk/components/render-block/block-styles.js +2 -2
  46. package/dist/sdk/components/render-content/components/render-styles.js +2 -2
  47. package/dist/sdk/components/render-content/render-content.js +8 -6
  48. package/dist/sdk/components/render-content/render-content.types.d.ts +9 -23
  49. package/dist/sdk/components/render-content-variants/helpers.d.ts +27 -3
  50. package/dist/sdk/components/render-content-variants/helpers.js +38 -24
  51. package/dist/sdk/components/render-content-variants/render-content-variants.d.ts +7 -2
  52. package/dist/sdk/components/render-content-variants/render-content-variants.js +29 -21
  53. package/dist/sdk/components/render-content-variants/render-content-variants.types.d.ts +20 -0
  54. package/dist/sdk/components/render-content-variants/render-content-variants.types.js +1 -0
  55. package/dist/sdk/constants/sdk-version.d.ts +1 -1
  56. package/dist/sdk/constants/sdk-version.js +1 -1
  57. package/dist/sdk/functions/track/index.js +1 -1
  58. package/dist/sdk/helpers/ab-tests.js +6 -0
  59. package/dist/sdk/types/builder-props.d.ts +10 -0
  60. package/dist/sdk/types/builder-props.js +1 -0
  61. package/dist/sdk/types/enforced-partials.d.ts +21 -0
  62. package/dist/sdk/types/enforced-partials.js +1 -0
  63. package/dist/sdk/types/typescript.d.ts +3 -0
  64. package/package.json +1 -1
@@ -0,0 +1,184 @@
1
+ import { TARGET } from '../../constants/target';
2
+ import { isBrowser } from '../../functions/is-browser';
3
+ export const getVariants = (content) => Object.values(content?.variations || {});
4
+ export const checkShouldRunVariants = ({ canTrack, content, }) => {
5
+ const hasVariants = getVariants(content).length > 0;
6
+ if (!hasVariants) {
7
+ return false;
8
+ }
9
+ if (!canTrack) {
10
+ return false;
11
+ }
12
+ if (isBrowser()) {
13
+ return false;
14
+ }
15
+ return true;
16
+ };
17
+ /**
18
+ * NOTE: when this function is stringified, single-line comments can cause weird issues when compiled by Sveltekit.
19
+ * Make sure to write multi-line comments only.
20
+ */
21
+ function bldrAbTest(contentId, variants, isHydrationTarget) {
22
+ function getAndSetVariantId() {
23
+ function setCookie(name, value, days) {
24
+ let expires = '';
25
+ if (days) {
26
+ const date = new Date();
27
+ date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
28
+ expires = '; expires=' + date.toUTCString();
29
+ }
30
+ document.cookie =
31
+ name +
32
+ '=' +
33
+ (value || '') +
34
+ expires +
35
+ '; path=/' +
36
+ '; Secure; SameSite=None';
37
+ }
38
+ function getCookie(name) {
39
+ const nameEQ = name + '=';
40
+ const ca = document.cookie.split(';');
41
+ for (let i = 0; i < ca.length; i++) {
42
+ let c = ca[i];
43
+ while (c.charAt(0) === ' ')
44
+ c = c.substring(1, c.length);
45
+ if (c.indexOf(nameEQ) === 0)
46
+ return c.substring(nameEQ.length, c.length);
47
+ }
48
+ return null;
49
+ }
50
+ const cookieName = `builder.tests.${contentId}`;
51
+ const variantInCookie = getCookie(cookieName);
52
+ const availableIDs = variants.map((vr) => vr.id).concat(contentId);
53
+ /**
54
+ * cookie already exists
55
+ */
56
+ if (variantInCookie && availableIDs.includes(variantInCookie)) {
57
+ return variantInCookie;
58
+ }
59
+ /**
60
+ * no cookie exists, find variant
61
+ */
62
+ let n = 0;
63
+ const random = Math.random();
64
+ for (let i = 0; i < variants.length; i++) {
65
+ const variant = variants[i];
66
+ const testRatio = variant.testRatio;
67
+ n += testRatio;
68
+ if (random < n) {
69
+ setCookie(cookieName, variant.id);
70
+ return variant.id;
71
+ }
72
+ }
73
+ /**
74
+ * no variant found, assign default content
75
+ */
76
+ setCookie(cookieName, contentId);
77
+ return contentId;
78
+ }
79
+ const winningVariantId = getAndSetVariantId();
80
+ const styleEl = document.getElementById(`variants-styles-${contentId}`);
81
+ /**
82
+ * For React to work, we need hydration to match SSR, so we completely remove this node and the styles tag.
83
+ */
84
+ if (isHydrationTarget) {
85
+ styleEl.remove();
86
+ const thisScriptEl = document.getElementById(`variants-script-${contentId}`);
87
+ thisScriptEl?.remove();
88
+ }
89
+ else {
90
+ /* update styles to hide all variants except the winning variant */
91
+ const newStyleStr = variants
92
+ .concat({ id: contentId })
93
+ .filter((variant) => variant.id !== winningVariantId)
94
+ .map((value) => {
95
+ return `.variant-${value.id} { display: none; }
96
+ `;
97
+ })
98
+ .join('');
99
+ /* TO-DO: check if this actually updates the style */
100
+ styleEl.innerHTML = newStyleStr;
101
+ }
102
+ }
103
+ /**
104
+ * NOTE: when this function is stringified, single-line comments can cause weird issues when compiled by Sveltekit.
105
+ * Make sure to write multi-line comments only.
106
+ */
107
+ function bldrCntntScrpt(variantContentId, defaultContentId, isHydrationTarget) {
108
+ if (!navigator.cookieEnabled) {
109
+ return;
110
+ }
111
+ function getCookie(name) {
112
+ const nameEQ = name + '=';
113
+ const ca = document.cookie.split(';');
114
+ for (let i = 0; i < ca.length; i++) {
115
+ let c = ca[i];
116
+ while (c.charAt(0) === ' ')
117
+ c = c.substring(1, c.length);
118
+ if (c.indexOf(nameEQ) === 0)
119
+ return c.substring(nameEQ.length, c.length);
120
+ }
121
+ return null;
122
+ }
123
+ const cookieName = `builder.tests.${defaultContentId}`;
124
+ const variantId = getCookie(cookieName);
125
+ /** get parent div by searching on `builder-content-id` attr */
126
+ const parentDiv = document.querySelector(`[builder-content-id="${variantContentId}"]`);
127
+ const variantIsDefaultContent = variantContentId === defaultContentId;
128
+ if (variantId === variantContentId) {
129
+ if (variantIsDefaultContent) {
130
+ /** the default content is already visible, no need to do anything */
131
+ return;
132
+ }
133
+ /** this is the winning variant and not already visible: remove `hidden` and `aria-hidden` attr */
134
+ parentDiv?.removeAttribute('hidden');
135
+ parentDiv?.removeAttribute('aria-hidden');
136
+ }
137
+ else {
138
+ if (variantIsDefaultContent) {
139
+ if (isHydrationTarget) {
140
+ /**
141
+ * For React to work, we need to support hydration, in which case the first CSR will have none of the hidden variants.
142
+ * So we completely remove that node.
143
+ */
144
+ parentDiv?.remove();
145
+ }
146
+ else {
147
+ /** this is not the winning variant, add `hidden` attr */
148
+ parentDiv?.setAttribute('hidden', 'true');
149
+ parentDiv?.setAttribute('aria-hidden', 'true');
150
+ }
151
+ }
152
+ /** This is not the winning variant, and it's not the default content.
153
+ * There's no need to hide it, because it's already hidden.
154
+ */
155
+ return;
156
+ }
157
+ return;
158
+ }
159
+ const getIsHydrationTarget = (target) => target === 'react' ||
160
+ target === 'reactNative' ||
161
+ target === 'vue3' ||
162
+ target === 'vue2';
163
+ const isHydrationTarget = getIsHydrationTarget(TARGET);
164
+ /**
165
+ * We hardcode explicit function names here, because the `.toString()` of a function can change depending on the bundler.
166
+ * Some bundlers will minify the fn name, etc.
167
+ *
168
+ * So we hardcode the function names here, and then use those names in the script string to make sure the function names are consistent.
169
+ */
170
+ const AB_TEST_FN_NAME = 'bldrAbTest';
171
+ const CONTENT_FN_NAME = 'bldrCntntScrpt';
172
+ export const getVariantsScriptString = (variants, contentId) => {
173
+ const fnStr = bldrAbTest.toString().replace(/\s+/g, ' ');
174
+ const fnStr2 = bldrCntntScrpt.toString().replace(/\s+/g, ' ');
175
+ return `
176
+ const ${AB_TEST_FN_NAME} = ${fnStr}
177
+ const ${CONTENT_FN_NAME} = ${fnStr2}
178
+ ${AB_TEST_FN_NAME}("${contentId}", ${JSON.stringify(variants)}, ${isHydrationTarget})
179
+ `;
180
+ };
181
+ export const getRenderContentScriptString = ({ parentContentId, contentId, }) => {
182
+ return `
183
+ ${CONTENT_FN_NAME}("${contentId}", "${parentContentId}", ${isHydrationTarget})`;
184
+ };
@@ -0,0 +1,7 @@
1
+ /// <reference types="react" />
2
+ interface Props {
3
+ scriptStr: string;
4
+ id?: string;
5
+ }
6
+ declare function InlinedScript(props: Props): JSX.Element;
7
+ export default InlinedScript;
@@ -0,0 +1,6 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ function InlinedScript(props) {
4
+ return (React.createElement("script", { dangerouslySetInnerHTML: { __html: props.scriptStr }, id: props.id }));
5
+ }
6
+ export default InlinedScript;
@@ -0,0 +1,7 @@
1
+ /// <reference types="react" />
2
+ interface Props {
3
+ styles: string;
4
+ id?: string;
5
+ }
6
+ declare function InlinedStyles(props: Props): JSX.Element;
7
+ export default InlinedStyles;
@@ -0,0 +1,6 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ function InlinedStyles(props) {
4
+ return (React.createElement("style", { dangerouslySetInnerHTML: { __html: props.styles }, id: props.id }));
5
+ }
6
+ export default InlinedStyles;
@@ -5,7 +5,7 @@ import { TARGET } from "../../constants/target.js";
5
5
  import { getProcessedBlock } from "../../functions/get-processed-block.js";
6
6
  import { createCssClass } from "../../helpers/css.js";
7
7
  import { checkIsDefined } from "../../helpers/nullable.js";
8
- import RenderInlinedStyles from "../render-inlined-styles";
8
+ import InlinedStyles from "../inlined-styles";
9
9
  function BlockStyles(props) {
10
10
  function useBlock() {
11
11
  return getProcessedBlock({
@@ -61,6 +61,6 @@ function BlockStyles(props) {
61
61
  return [largeStylesClass, mediumStylesClass, smallStylesClass].join(" ");
62
62
  }
63
63
  return (React.createElement(React.Fragment, null, TARGET !== "reactNative" && css() && canShowBlock() ? (React.createElement(React.Fragment, null,
64
- React.createElement(RenderInlinedStyles, { styles: css() }))) : null));
64
+ React.createElement(InlinedStyles, { styles: css() }))) : null));
65
65
  }
66
66
  export default BlockStyles;
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
3
  import { useState } from "react";
4
- import RenderInlinedStyles from "../../render-inlined-styles";
4
+ import InlinedStyles from "../../inlined-styles";
5
5
  import { getCss } from "./render-styles.helpers";
6
6
  import { getFontCss } from "./render-styles.helpers";
7
7
  function RenderContentStyles(props) {
@@ -27,6 +27,6 @@ ${getFontCss({
27
27
  font-family: inherit;
28
28
  }
29
29
  `.trim());
30
- return React.createElement(RenderInlinedStyles, { styles: injectedStyles });
30
+ return React.createElement(InlinedStyles, { styles: injectedStyles });
31
31
  }
32
32
  export default RenderContentStyles;
@@ -21,6 +21,7 @@ import { TARGET } from "../../constants/target.js";
21
21
  import { logger } from "../../helpers/logger.js";
22
22
  import { getRenderContentScriptString } from "../render-content-variants/helpers.js";
23
23
  import { wrapComponentRef } from "./wrap-component-ref.js";
24
+ import InlinedScript from "../inlined-script";
24
25
  function RenderContent(props) {
25
26
  const elementRef = useRef(null);
26
27
  const [forceReRenderCount, setForceReRenderCount] = useState(() => 0);
@@ -181,9 +182,10 @@ function RenderContent(props) {
181
182
  }
182
183
  }
183
184
  const [scriptStr, setScriptStr] = useState(() => getRenderContentScriptString({
185
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain
186
+ variationId: props.content?.testVariationId,
184
187
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain
185
188
  contentId: props.content?.id,
186
- parentContentId: props.parentContentId,
187
189
  }));
188
190
  const [builderContextSignal, setBuilderContextSignal] = useState(() => ({
189
191
  content: getContentInitialValue({
@@ -326,14 +328,14 @@ function RenderContent(props) {
326
328
  "builder-content-id": "",
327
329
  },
328
330
  }
329
- : {}), ...(props.hideContent
330
- ? {
331
+ : {}), ...(props.showContent
332
+ ? {}
333
+ : {
331
334
  hidden: true,
332
335
  "aria-hidden": true,
333
- }
334
- : {}), className: props.classNameProp },
336
+ }), className: props.classNameProp },
335
337
  props.isSsrAbTest ? (React.createElement(React.Fragment, null,
336
- React.createElement("script", { dangerouslySetInnerHTML: { __html: scriptStr } }))) : null,
338
+ React.createElement(InlinedScript, { scriptStr: scriptStr }))) : null,
337
339
  TARGET !== "reactNative" ? (React.createElement(React.Fragment, null,
338
340
  React.createElement(RenderContentStyles, { contentId: builderContextSignal.content?.id, cssCode: builderContextSignal.content?.data?.cssCode, customFonts: builderContextSignal.content?.data?.customFonts }))) : null,
339
341
  React.createElement(RenderBlocks, { blocks: builderContextSignal.content?.data?.blocks, key: forceReRenderCount })))) : null));
@@ -1,30 +1,15 @@
1
- import type { BuilderRenderContext, RegisteredComponent, BuilderRenderState } from '../../context/types';
2
- import type { BuilderContent } from '../../types/builder-content';
3
- import type { Nullable } from '../../types/typescript';
4
- import type { ApiVersion } from '../../types/api-version';
5
- export interface RenderContentProps {
6
- content?: Nullable<BuilderContent>;
7
- model?: string;
8
- data?: {
9
- [key: string]: any;
10
- };
11
- context?: BuilderRenderContext;
12
- apiKey: string;
13
- apiVersion?: ApiVersion;
14
- customComponents?: RegisteredComponent[];
15
- canTrack?: boolean;
16
- locale?: string;
17
- /** @deprecated use `enrich` instead **/
18
- includeRefs?: boolean;
19
- enrich?: boolean;
1
+ import type { BuilderRenderState } from '../../context/types';
2
+ import type { EnforcePartials } from '../../types/enforced-partials';
3
+ import type { RenderContentVariantsProps } from '../render-content-variants/render-content-variants.types';
4
+ interface InternalRenderProps {
20
5
  /**
21
6
  * TO-DO: improve qwik generator to not remap this name for non-HTML tags, then name it `className`
22
7
  */
23
- classNameProp?: string;
24
- hideContent?: boolean;
25
- parentContentId?: string;
26
- isSsrAbTest?: boolean;
8
+ classNameProp: string | undefined;
9
+ showContent: boolean;
10
+ isSsrAbTest: boolean;
27
11
  }
12
+ export type RenderContentProps = InternalRenderProps & EnforcePartials<RenderContentVariantsProps>;
28
13
  export interface BuilderComponentStateChange {
29
14
  state: BuilderRenderState;
30
15
  ref: {
@@ -36,3 +21,4 @@ export interface BuilderComponentStateChange {
36
21
  };
37
22
  };
38
23
  }
24
+ export {};
@@ -1,6 +1,29 @@
1
1
  import type { Nullable } from '../../helpers/nullable';
2
2
  import type { BuilderContent } from '../../types/builder-content';
3
- export declare const getVariants: (content: Nullable<BuilderContent>) => import("../../types/builder-content").BuilderContentVariation[];
3
+ export declare const getVariants: (content: Nullable<BuilderContent>) => {
4
+ testVariationId: string;
5
+ id: string;
6
+ data?: {
7
+ [key: string]: any;
8
+ title?: string;
9
+ blocks?: import("../../types/builder-block").BuilderBlock[];
10
+ inputs?: import("../../types/input").Input[];
11
+ state?: {
12
+ [key: string]: any;
13
+ };
14
+ jsCode?: string;
15
+ tsCode?: string;
16
+ httpRequests?: {
17
+ [key: string]: string;
18
+ };
19
+ };
20
+ name?: string;
21
+ testRatio?: number;
22
+ meta?: {
23
+ [key: string]: any;
24
+ breakpoints?: import("../../types/builder-content").Breakpoints;
25
+ };
26
+ }[];
4
27
  export declare const checkShouldRunVariants: ({ canTrack, content, }: {
5
28
  canTrack: Nullable<boolean>;
6
29
  content: Nullable<BuilderContent>;
@@ -9,9 +32,10 @@ type VariantData = {
9
32
  id: string;
10
33
  testRatio?: number;
11
34
  };
35
+ export declare const getScriptString: () => string;
12
36
  export declare const getVariantsScriptString: (variants: VariantData[], contentId: string) => string;
13
- export declare const getRenderContentScriptString: ({ parentContentId, contentId, }: {
37
+ export declare const getRenderContentScriptString: ({ contentId, variationId, }: {
38
+ variationId: string;
14
39
  contentId: string;
15
- parentContentId: string;
16
40
  }) => string;
17
41
  export {};
@@ -1,17 +1,32 @@
1
1
  import { TARGET } from '../../constants/target';
2
2
  import { isBrowser } from '../../functions/is-browser';
3
- export const getVariants = (content) => Object.values(content?.variations || {});
3
+ export const getVariants = (content) => Object.values(content?.variations || {}).map((variant) => ({
4
+ ...variant,
5
+ testVariationId: variant.id,
6
+ id: content?.id,
7
+ }));
4
8
  export const checkShouldRunVariants = ({ canTrack, content, }) => {
5
9
  const hasVariants = getVariants(content).length > 0;
6
- if (!hasVariants) {
10
+ /**
11
+ * We cannot SSR in React-Native.
12
+ */
13
+ if (TARGET === 'reactNative')
7
14
  return false;
8
- }
9
- if (!canTrack) {
15
+ if (!hasVariants)
10
16
  return false;
11
- }
12
- if (isBrowser()) {
17
+ if (!canTrack)
18
+ return false;
19
+ /**
20
+ * For Vue 2 and Vue 3, we need to (initially) render the variants. This is to avoid hydration mismatch errors.
21
+ * Unlike React, Vue's hydration checks are shallow and do not check the attributes/contents of each element, so we
22
+ * are able to modify the `hidden` HTML attributes and `display` CSS properties without causing a hydration mismatch error.
23
+ *
24
+ * NOTE: after the app is hydrated, we strip the variants from the DOM (on mount) to reduce the amount of HTML in the DOM.
25
+ */
26
+ if (TARGET === 'vue2' || TARGET === 'vue3')
27
+ return true;
28
+ if (isBrowser())
13
29
  return false;
14
- }
15
30
  return true;
16
31
  };
17
32
  /**
@@ -77,13 +92,14 @@ function bldrAbTest(contentId, variants, isHydrationTarget) {
77
92
  return contentId;
78
93
  }
79
94
  const winningVariantId = getAndSetVariantId();
80
- const styleEl = document.getElementById(`variants-styles-${contentId}`);
95
+ const styleEl = document.currentScript
96
+ ?.previousElementSibling;
81
97
  /**
82
98
  * For React to work, we need hydration to match SSR, so we completely remove this node and the styles tag.
83
99
  */
84
100
  if (isHydrationTarget) {
85
101
  styleEl.remove();
86
- const thisScriptEl = document.getElementById(`variants-script-${contentId}`);
102
+ const thisScriptEl = document.currentScript;
87
103
  thisScriptEl?.remove();
88
104
  }
89
105
  else {
@@ -96,7 +112,6 @@ function bldrAbTest(contentId, variants, isHydrationTarget) {
96
112
  `;
97
113
  })
98
114
  .join('');
99
- /* TO-DO: check if this actually updates the style */
100
115
  styleEl.innerHTML = newStyleStr;
101
116
  }
102
117
  }
@@ -122,8 +137,7 @@ function bldrCntntScrpt(variantContentId, defaultContentId, isHydrationTarget) {
122
137
  }
123
138
  const cookieName = `builder.tests.${defaultContentId}`;
124
139
  const variantId = getCookie(cookieName);
125
- /** get parent div by searching on `builder-content-id` attr */
126
- const parentDiv = document.querySelector(`[builder-content-id="${variantContentId}"]`);
140
+ const parentDiv = document.currentScript?.parentElement;
127
141
  const variantIsDefaultContent = variantContentId === defaultContentId;
128
142
  if (variantId === variantContentId) {
129
143
  if (variantIsDefaultContent) {
@@ -156,10 +170,7 @@ function bldrCntntScrpt(variantContentId, defaultContentId, isHydrationTarget) {
156
170
  }
157
171
  return;
158
172
  }
159
- const getIsHydrationTarget = (target) => target === 'react' ||
160
- target === 'reactNative' ||
161
- target === 'vue3' ||
162
- target === 'vue2';
173
+ const getIsHydrationTarget = (target) => target === 'react' || target === 'reactNative';
163
174
  const isHydrationTarget = getIsHydrationTarget(TARGET);
164
175
  /**
165
176
  * We hardcode explicit function names here, because the `.toString()` of a function can change depending on the bundler.
@@ -167,18 +178,21 @@ const isHydrationTarget = getIsHydrationTarget(TARGET);
167
178
  *
168
179
  * So we hardcode the function names here, and then use those names in the script string to make sure the function names are consistent.
169
180
  */
170
- const AB_TEST_FN_NAME = 'bldrAbTest';
171
- const CONTENT_FN_NAME = 'bldrCntntScrpt';
172
- export const getVariantsScriptString = (variants, contentId) => {
181
+ const AB_TEST_FN_NAME = 'builderIoAbTest';
182
+ const CONTENT_FN_NAME = 'builderIoRenderContent';
183
+ export const getScriptString = () => {
173
184
  const fnStr = bldrAbTest.toString().replace(/\s+/g, ' ');
174
185
  const fnStr2 = bldrCntntScrpt.toString().replace(/\s+/g, ' ');
175
186
  return `
176
- const ${AB_TEST_FN_NAME} = ${fnStr}
177
- const ${CONTENT_FN_NAME} = ${fnStr2}
178
- ${AB_TEST_FN_NAME}("${contentId}", ${JSON.stringify(variants)}, ${isHydrationTarget})
187
+ window.${AB_TEST_FN_NAME} = ${fnStr}
188
+ window.${CONTENT_FN_NAME} = ${fnStr2}
179
189
  `;
180
190
  };
181
- export const getRenderContentScriptString = ({ parentContentId, contentId, }) => {
191
+ export const getVariantsScriptString = (variants, contentId) => {
192
+ return `
193
+ window.${AB_TEST_FN_NAME}("${contentId}",${JSON.stringify(variants)}, ${isHydrationTarget})`;
194
+ };
195
+ export const getRenderContentScriptString = ({ contentId, variationId, }) => {
182
196
  return `
183
- ${CONTENT_FN_NAME}("${contentId}", "${parentContentId}", ${isHydrationTarget})`;
197
+ window.${CONTENT_FN_NAME}("${variationId}", "${contentId}", ${isHydrationTarget})`;
184
198
  };
@@ -1,5 +1,10 @@
1
1
  /// <reference types="react" />
2
- type VariantsProviderProps = RenderContentProps;
3
- import type { RenderContentProps } from "../render-content/render-content.types";
2
+ type VariantsProviderProps = RenderContentVariantsProps & {
3
+ /**
4
+ * For internal use only. Do not provide this prop.
5
+ */
6
+ __isNestedRender?: boolean;
7
+ };
8
+ import type { RenderContentVariantsProps } from "./render-content-variants.types";
4
9
  declare function RenderContentVariants(props: VariantsProviderProps): JSX.Element;
5
10
  export default RenderContentVariants;
@@ -1,37 +1,45 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
- import { useState } from "react";
4
- import { checkShouldRunVariants, getVariants, getVariantsScriptString, } from "./helpers";
3
+ import { useState, useEffect } from "react";
4
+ import { checkShouldRunVariants, getScriptString, getVariants, getVariantsScriptString, } from "./helpers";
5
5
  import RenderContent from "../render-content/render-content";
6
6
  import { getDefaultCanTrack } from "../../helpers/canTrack";
7
- import RenderInlinedStyles from "../render-inlined-styles";
7
+ import InlinedStyles from "../inlined-styles";
8
8
  import { handleABTestingSync } from "../../helpers/ab-tests";
9
+ import InlinedScript from "../inlined-script";
10
+ import { TARGET } from "../../constants/target";
9
11
  function RenderContentVariants(props) {
10
- const [variantScriptStr, setVariantScriptStr] = useState(() => getVariantsScriptString(getVariants(props.content).map((value) => ({
11
- id: value.id,
12
- testRatio: value.testRatio,
13
- })), props.content?.id || ""));
14
12
  const [shouldRenderVariants, setShouldRenderVariants] = useState(() => checkShouldRunVariants({
15
13
  canTrack: getDefaultCanTrack(props.canTrack),
16
14
  content: props.content,
17
15
  }));
16
+ const [variantScriptStr, setVariantScriptStr] = useState(() => getVariantsScriptString(getVariants(props.content).map((value) => ({
17
+ id: value.testVariationId,
18
+ testRatio: value.testRatio,
19
+ })), props.content?.id || ""));
18
20
  const [hideVariantsStyleString, setHideVariantsStyleString] = useState(() => getVariants(props.content)
19
- .map((value) => `.variant-${value.id} { display: none; } `)
21
+ .map((value) => `.variant-${value.testVariationId} { display: none; } `)
20
22
  .join(""));
21
- const [contentToRender, setContentToRender] = useState(() => checkShouldRunVariants({
22
- canTrack: getDefaultCanTrack(props.canTrack),
23
- content: props.content,
24
- })
25
- ? props.content
26
- : handleABTestingSync({
27
- item: props.content,
28
- canTrack: getDefaultCanTrack(props.canTrack),
29
- }));
23
+ useEffect(() => {
24
+ /**
25
+ * We unmount the non-winning variants post-hydration in Vue.
26
+ */
27
+ if (TARGET === "vue2" || TARGET === "vue3") {
28
+ setShouldRenderVariants(false);
29
+ }
30
+ }, []);
30
31
  return (React.createElement(React.Fragment, null,
32
+ !props.__isNestedRender && TARGET !== "reactNative" ? (React.createElement(React.Fragment, null,
33
+ React.createElement(InlinedScript, { scriptStr: getScriptString() }))) : null,
31
34
  shouldRenderVariants ? (React.createElement(React.Fragment, null,
32
- React.createElement(RenderInlinedStyles, { id: `variants-styles-${props.content?.id}`, styles: hideVariantsStyleString }),
33
- React.createElement("script", { id: `variants-script-${props.content?.id}`, dangerouslySetInnerHTML: { __html: variantScriptStr } }),
34
- getVariants(props.content)?.map((variant) => (React.createElement(RenderContent, { key: variant.id, content: variant, apiKey: props.apiKey, apiVersion: props.apiVersion, canTrack: props.canTrack, customComponents: props.customComponents, hideContent: true, parentContentId: props.content?.id, isSsrAbTest: shouldRenderVariants }))))) : null,
35
- React.createElement(RenderContent, { model: props.model, content: contentToRender, apiKey: props.apiKey, apiVersion: props.apiVersion, canTrack: props.canTrack, customComponents: props.customComponents, classNameProp: `variant-${props.content?.id}`, parentContentId: props.content?.id, isSsrAbTest: shouldRenderVariants })));
35
+ React.createElement(InlinedStyles, { id: `variants-styles-${props.content?.id}`, styles: hideVariantsStyleString }),
36
+ React.createElement(InlinedScript, { scriptStr: variantScriptStr }),
37
+ getVariants(props.content)?.map((variant) => (React.createElement(RenderContent, { key: variant.testVariationId, content: variant, showContent: false, classNameProp: undefined, model: props.model, data: props.data, context: props.context, apiKey: props.apiKey, apiVersion: props.apiVersion, customComponents: props.customComponents, canTrack: props.canTrack, locale: props.locale, includeRefs: props.includeRefs, enrich: props.enrich, isSsrAbTest: shouldRenderVariants }))))) : null,
38
+ React.createElement(RenderContent, { ...{}, content: shouldRenderVariants
39
+ ? props.content
40
+ : handleABTestingSync({
41
+ item: props.content,
42
+ canTrack: getDefaultCanTrack(props.canTrack),
43
+ }), classNameProp: `variant-${props.content?.id}`, showContent: true, model: props.model, data: props.data, context: props.context, apiKey: props.apiKey, apiVersion: props.apiVersion, customComponents: props.customComponents, canTrack: props.canTrack, locale: props.locale, includeRefs: props.includeRefs, enrich: props.enrich, isSsrAbTest: shouldRenderVariants })));
36
44
  }
37
45
  export default RenderContentVariants;
@@ -0,0 +1,20 @@
1
+ import type { BuilderRenderContext, RegisteredComponent } from '../../context/types';
2
+ import type { ApiVersion } from '../../types/api-version';
3
+ import type { BuilderContent } from '../../types/builder-content';
4
+ import type { Nullable } from '../../types/typescript';
5
+ export interface RenderContentVariantsProps {
6
+ content?: Nullable<BuilderContent>;
7
+ model?: string;
8
+ data?: {
9
+ [key: string]: any;
10
+ };
11
+ context?: BuilderRenderContext;
12
+ apiKey: string;
13
+ apiVersion?: ApiVersion;
14
+ customComponents?: RegisteredComponent[];
15
+ canTrack?: boolean;
16
+ locale?: string;
17
+ /** @deprecated use `enrich` instead **/
18
+ includeRefs?: boolean;
19
+ enrich?: boolean;
20
+ }
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "0.4.3";
1
+ export declare const SDK_VERSION = "0.4.5";
@@ -1 +1 @@
1
- export const SDK_VERSION = "0.4.3";
1
+ export const SDK_VERSION = "0.4.5";
@@ -43,7 +43,7 @@ export async function _track(eventProps) {
43
43
  if (!(isBrowser() || TARGET === 'reactNative')) {
44
44
  return;
45
45
  }
46
- return fetch(`https://builder.io/api/v1/track`, {
46
+ return fetch(`https://cdn.builder.io/api/v1/track`, {
47
47
  method: 'POST',
48
48
  body: JSON.stringify({
49
49
  events: [await createEvent(eventProps)],
@@ -1,6 +1,7 @@
1
1
  import { getCookie, getCookieSync, setCookie } from './cookie.js';
2
2
  import { checkIsDefined } from '../helpers/nullable.js';
3
3
  import { logger } from './logger.js';
4
+ import { TARGET } from '../constants/target.js';
4
5
  const BUILDER_STORE_PREFIX = 'builder.tests';
5
6
  const getContentTestKey = (id) => `${BUILDER_STORE_PREFIX}.${id}`;
6
7
  const getContentVariationCookie = ({ contentId }) => getCookie({ name: getContentTestKey(contentId), canTrack: true });
@@ -59,6 +60,11 @@ const getTestFields = ({ item, testGroupId, }) => {
59
60
  }
60
61
  };
61
62
  export const handleABTestingSync = ({ item, canTrack, }) => {
63
+ /**
64
+ * We cannot SSR in React-Native.
65
+ */
66
+ if (TARGET === 'reactNative')
67
+ return item;
62
68
  if (!canTrack) {
63
69
  return item;
64
70
  }
@@ -0,0 +1,10 @@
1
+ import type { BuilderContextInterface, RegisteredComponent } from '../context/types';
2
+ import type { BuilderBlock } from './builder-block';
3
+ import type { Dictionary } from './typescript';
4
+ export type PropsWithBuilderData<T> = T & {
5
+ builderBlock: BuilderBlock;
6
+ builderContext: BuilderContextInterface;
7
+ };
8
+ export type BuilderComponentsProp = {
9
+ builderComponents: Dictionary<RegisteredComponent>;
10
+ };
@@ -0,0 +1 @@
1
+ export {};