@builder.io/sdk-solid 0.2.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/package.json +2 -5
- package/src/blocks/columns/columns.jsx +1 -1
- package/src/blocks/symbol/symbol.jsx +1 -1
- package/src/components/render-block/block-styles.jsx +3 -1
- package/src/components/render-block/render-block.helpers.js +7 -19
- package/src/components/render-block/render-block.jsx +23 -23
- package/src/components/render-block/render-repeated-block.jsx +3 -2
- package/src/components/render-content/components/render-styles.jsx +4 -2
- package/src/components/render-content/render-content.jsx +52 -9
- package/src/components/render-content/wrap-component-ref.js +4 -0
- package/src/components/render-content-variants/helpers.js +139 -0
- package/src/components/render-content-variants/render-content-variants.jsx +94 -0
- package/src/components/render-inlined-styles.jsx +1 -23
- package/src/constants/sdk-version.js +1 -0
- package/src/context/builder.context.js +3 -2
- package/src/functions/evaluate.js +27 -3
- package/src/functions/evaluate.test.js +17 -0
- package/src/functions/get-block-actions-handler.js +3 -1
- package/src/functions/get-content/generate-content-url.js +2 -1
- package/src/functions/get-content/generate-content-url.test.js +15 -0
- package/src/functions/get-content/index.js +35 -18
- package/src/functions/get-processed-block.js +20 -4
- package/src/functions/get-processed-block.test.js +3 -1
- package/src/helpers/ab-tests.js +132 -10
- package/src/helpers/canTrack.js +5 -0
- package/src/helpers/cookie.js +9 -4
- package/src/helpers/logger.js +2 -1
- package/src/index-helpers/blocks-exports.js +10 -10
- package/src/index.js +18 -7
- package/src/scripts/init-editing.js +2 -0
- package/src/functions/get-content/ab-testing.js +0 -99
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
### 0.4.0
|
|
2
|
+
|
|
3
|
+
- Feature: A/B tests are now rendered correctly during server-side rendering (SSR) when applicable. This behaviour is backwards compatible with previous versions.
|
|
4
|
+
|
|
5
|
+
### 0.3.1
|
|
6
|
+
|
|
7
|
+
- Feature: Added SDK version to data sent to visual editor for improved debugging.
|
|
8
|
+
- Fix: Columns block: removed redundant margin-left in first column.
|
|
9
|
+
|
|
10
|
+
### 0.3.0
|
|
11
|
+
|
|
12
|
+
- No Changes.
|
|
13
|
+
|
|
1
14
|
### 0.2.3
|
|
2
15
|
|
|
3
16
|
- No Changes.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@builder.io/sdk-solid",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./solid-index.jsx",
|
|
@@ -12,10 +12,7 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
|
-
"build": "echo 'no need to build solid SDK'"
|
|
16
|
-
"release:patch": "yarn run build && npm version patch && npm publish",
|
|
17
|
-
"release:minor": "yarn run build && npm version minor && npm publish",
|
|
18
|
-
"release:dev": "yarn run build && npm version prerelease && npm publish --tag dev"
|
|
15
|
+
"build": "echo 'no need to build solid SDK'"
|
|
19
16
|
},
|
|
20
17
|
"dependencies": {
|
|
21
18
|
"solid-styled-components": "^0.27.6"
|
|
@@ -85,7 +85,7 @@ function Symbol(props) {
|
|
|
85
85
|
customComponents={Object.values(builderContext.registeredComponents)}
|
|
86
86
|
data={{
|
|
87
87
|
...props.symbol?.data,
|
|
88
|
-
...builderContext.
|
|
88
|
+
...builderContext.localState,
|
|
89
89
|
...contentToUse()?.data?.state,
|
|
90
90
|
}}
|
|
91
91
|
model={props.symbol?.model}
|
|
@@ -14,7 +14,9 @@ function BlockStyles(props) {
|
|
|
14
14
|
function useBlock() {
|
|
15
15
|
return getProcessedBlock({
|
|
16
16
|
block: props.block,
|
|
17
|
-
|
|
17
|
+
localState: props.context.localState,
|
|
18
|
+
rootState: props.context.rootState,
|
|
19
|
+
rootSetState: props.context.rootSetState,
|
|
18
20
|
context: props.context.context,
|
|
19
21
|
shouldEvaluateBindings: true,
|
|
20
22
|
});
|
|
@@ -58,7 +58,9 @@ const getComponent = ({
|
|
|
58
58
|
var _a;
|
|
59
59
|
const componentName = (_a = getProcessedBlock({
|
|
60
60
|
block,
|
|
61
|
-
|
|
61
|
+
localState: context.localState,
|
|
62
|
+
rootState: context.rootState,
|
|
63
|
+
rootSetState: context.rootSetState,
|
|
62
64
|
context: context.context,
|
|
63
65
|
shouldEvaluateBindings: false
|
|
64
66
|
}).component) == null ? void 0 : _a.name;
|
|
@@ -85,7 +87,9 @@ const getRepeatItemData = ({
|
|
|
85
87
|
}
|
|
86
88
|
const itemsArray = evaluate({
|
|
87
89
|
code: repeat.collection,
|
|
88
|
-
|
|
90
|
+
localState: context.localState,
|
|
91
|
+
rootState: context.rootState,
|
|
92
|
+
rootSetState: context.rootSetState,
|
|
89
93
|
context: context.context
|
|
90
94
|
});
|
|
91
95
|
if (!Array.isArray(itemsArray)) {
|
|
@@ -95,7 +99,7 @@ const getRepeatItemData = ({
|
|
|
95
99
|
const itemNameToUse = repeat.itemName || (collectionName ? collectionName + "Item" : "item");
|
|
96
100
|
const repeatArray = itemsArray.map((item, index) => ({
|
|
97
101
|
context: __spreadProps(__spreadValues({}, context), {
|
|
98
|
-
|
|
102
|
+
localState: __spreadProps(__spreadValues({}, context.localState), {
|
|
99
103
|
$index: index,
|
|
100
104
|
$item: item,
|
|
101
105
|
[itemNameToUse]: item,
|
|
@@ -106,24 +110,8 @@ const getRepeatItemData = ({
|
|
|
106
110
|
}));
|
|
107
111
|
return repeatArray;
|
|
108
112
|
};
|
|
109
|
-
const getProxyState = (context) => {
|
|
110
|
-
if (typeof Proxy === "undefined") {
|
|
111
|
-
console.error("no Proxy available in this environment, cannot proxy state.");
|
|
112
|
-
return context.state;
|
|
113
|
-
}
|
|
114
|
-
const useState = new Proxy(context.state, {
|
|
115
|
-
set: (obj, prop, value) => {
|
|
116
|
-
var _a;
|
|
117
|
-
obj[prop] = value;
|
|
118
|
-
(_a = context.setState) == null ? void 0 : _a.call(context, obj);
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
return useState;
|
|
123
|
-
};
|
|
124
113
|
export {
|
|
125
114
|
getComponent,
|
|
126
|
-
getProxyState,
|
|
127
115
|
getRepeatItemData,
|
|
128
116
|
isEmptyHtmlElement
|
|
129
117
|
};
|
|
@@ -8,7 +8,6 @@ import { getProcessedBlock } from "../../functions/get-processed-block.js";
|
|
|
8
8
|
import BlockStyles from "./block-styles.jsx";
|
|
9
9
|
import {
|
|
10
10
|
getComponent,
|
|
11
|
-
getProxyState,
|
|
12
11
|
getRepeatItemData,
|
|
13
12
|
isEmptyHtmlElement,
|
|
14
13
|
} from "./render-block.helpers.js";
|
|
@@ -26,25 +25,23 @@ function RenderBlock(props) {
|
|
|
26
25
|
})
|
|
27
26
|
);
|
|
28
27
|
|
|
29
|
-
const [
|
|
30
|
-
|
|
28
|
+
const [Tag, setTag] = createSignal(props.block.tagName || "div");
|
|
29
|
+
|
|
30
|
+
function repeatItem() {
|
|
31
|
+
return getRepeatItemData({
|
|
31
32
|
block: props.block,
|
|
32
33
|
context: props.context,
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const [tag, setTag] = createSignal(props.block.tagName || "div");
|
|
37
|
-
|
|
38
|
-
const [proxyState, setProxyState] = createSignal(
|
|
39
|
-
getProxyState(props.context)
|
|
40
|
-
);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
41
36
|
|
|
42
37
|
function useBlock() {
|
|
43
|
-
return
|
|
38
|
+
return repeatItem()
|
|
44
39
|
? props.block
|
|
45
40
|
: getProcessedBlock({
|
|
46
41
|
block: props.block,
|
|
47
|
-
|
|
42
|
+
localState: props.context.localState,
|
|
43
|
+
rootState: props.context.rootState,
|
|
44
|
+
rootSetState: props.context.rootSetState,
|
|
48
45
|
context: props.context.context,
|
|
49
46
|
shouldEvaluateBindings: true,
|
|
50
47
|
});
|
|
@@ -63,7 +60,9 @@ function RenderBlock(props) {
|
|
|
63
60
|
function actions() {
|
|
64
61
|
return getBlockActions({
|
|
65
62
|
block: useBlock(),
|
|
66
|
-
|
|
63
|
+
rootState: props.context.rootState,
|
|
64
|
+
rootSetState: props.context.rootSetState,
|
|
65
|
+
localState: props.context.localState,
|
|
67
66
|
context: props.context.context,
|
|
68
67
|
});
|
|
69
68
|
}
|
|
@@ -92,7 +91,7 @@ function RenderBlock(props) {
|
|
|
92
91
|
* blocks, and the children will be repeated within those blocks.
|
|
93
92
|
*/
|
|
94
93
|
const shouldRenderChildrenOutsideRef =
|
|
95
|
-
!component()?.component && !
|
|
94
|
+
!component()?.component && !repeatItem();
|
|
96
95
|
return shouldRenderChildrenOutsideRef ? useBlock().children ?? [] : [];
|
|
97
96
|
}
|
|
98
97
|
|
|
@@ -112,10 +111,11 @@ function RenderBlock(props) {
|
|
|
112
111
|
return {
|
|
113
112
|
apiKey: props.context.apiKey,
|
|
114
113
|
apiVersion: props.context.apiVersion,
|
|
115
|
-
|
|
114
|
+
localState: props.context.localState,
|
|
115
|
+
rootState: props.context.rootState,
|
|
116
|
+
rootSetState: props.context.rootSetState,
|
|
116
117
|
content: props.context.content,
|
|
117
118
|
context: props.context.context,
|
|
118
|
-
setState: props.context.setState,
|
|
119
119
|
registeredComponents: props.context.registeredComponents,
|
|
120
120
|
inheritedStyles: getInheritedTextStyles(),
|
|
121
121
|
};
|
|
@@ -152,11 +152,11 @@ function RenderBlock(props) {
|
|
|
152
152
|
}
|
|
153
153
|
when={!component()?.noWrap}
|
|
154
154
|
>
|
|
155
|
-
<Show when={isEmptyHtmlElement(
|
|
156
|
-
<Dynamic {...attributes()} {...actions()} component={
|
|
155
|
+
<Show when={isEmptyHtmlElement(Tag())}>
|
|
156
|
+
<Dynamic {...attributes()} {...actions()} component={Tag()}></Dynamic>
|
|
157
157
|
</Show>
|
|
158
|
-
<Show when={!isEmptyHtmlElement(
|
|
159
|
-
<For each={
|
|
158
|
+
<Show when={!isEmptyHtmlElement(Tag()) && repeatItem()}>
|
|
159
|
+
<For each={repeatItem()}>
|
|
160
160
|
{(data, _index) => {
|
|
161
161
|
const index = _index();
|
|
162
162
|
return (
|
|
@@ -169,8 +169,8 @@ function RenderBlock(props) {
|
|
|
169
169
|
}}
|
|
170
170
|
</For>
|
|
171
171
|
</Show>
|
|
172
|
-
<Show when={!isEmptyHtmlElement(
|
|
173
|
-
<Dynamic {...attributes()} {...actions()} component={
|
|
172
|
+
<Show when={!isEmptyHtmlElement(Tag()) && !repeatItem()}>
|
|
173
|
+
<Dynamic {...attributes()} {...actions()} component={Tag()}>
|
|
174
174
|
<RenderComponent {...renderComponentProps()}></RenderComponent>
|
|
175
175
|
<For each={childrenWithoutParentComponent()}>
|
|
176
176
|
{(child, _index) => {
|
|
@@ -6,8 +6,9 @@ function RenderRepeatedBlock(props) {
|
|
|
6
6
|
<BuilderContext.Provider
|
|
7
7
|
value={{
|
|
8
8
|
content: props.repeatContext.content,
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
localState: props.repeatContext.localState,
|
|
10
|
+
rootState: props.repeatContext.rootState,
|
|
11
|
+
rootSetState: props.repeatContext.rootSetState,
|
|
11
12
|
context: props.repeatContext.context,
|
|
12
13
|
apiKey: props.repeatContext.apiKey,
|
|
13
14
|
registeredComponents: props.repeatContext.registeredComponents,
|
|
@@ -5,7 +5,8 @@ import { getCss } from "./render-styles.helpers";
|
|
|
5
5
|
import { getFontCss } from "./render-styles.helpers";
|
|
6
6
|
|
|
7
7
|
function RenderContentStyles(props) {
|
|
8
|
-
const [injectedStyles, setInjectedStyles] = createSignal(
|
|
8
|
+
const [injectedStyles, setInjectedStyles] = createSignal(
|
|
9
|
+
`
|
|
9
10
|
${getCss({
|
|
10
11
|
cssCode: props.cssCode,
|
|
11
12
|
contentId: props.contentId,
|
|
@@ -26,7 +27,8 @@ ${getFontCss({
|
|
|
26
27
|
text-align: inherit;
|
|
27
28
|
font-family: inherit;
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
+
`.trim()
|
|
31
|
+
);
|
|
30
32
|
|
|
31
33
|
return <RenderInlinedStyles styles={injectedStyles()}></RenderInlinedStyles>;
|
|
32
34
|
}
|
|
@@ -29,6 +29,8 @@ import {
|
|
|
29
29
|
} from "./render-content.helpers.js";
|
|
30
30
|
import { TARGET } from "../../constants/target.js";
|
|
31
31
|
import { logger } from "../../helpers/logger.js";
|
|
32
|
+
import { getRenderContentScriptString } from "../render-content-variants/helpers.js";
|
|
33
|
+
import { wrapComponentRef } from "./wrap-component-ref.js";
|
|
32
34
|
|
|
33
35
|
function RenderContent(props) {
|
|
34
36
|
const [forceReRenderCount, setForceReRenderCount] = createSignal(0);
|
|
@@ -60,9 +62,13 @@ function RenderContent(props) {
|
|
|
60
62
|
...components,
|
|
61
63
|
...(props.customComponents || []),
|
|
62
64
|
].reduce(
|
|
63
|
-
(acc, curr) => ({
|
|
65
|
+
(acc, { component, ...curr }) => ({
|
|
64
66
|
...acc,
|
|
65
|
-
[curr.name]:
|
|
67
|
+
[curr.name]: {
|
|
68
|
+
component:
|
|
69
|
+
TARGET === "vue3" ? wrapComponentRef(component) : component,
|
|
70
|
+
...curr,
|
|
71
|
+
},
|
|
66
72
|
}),
|
|
67
73
|
{}
|
|
68
74
|
)
|
|
@@ -72,6 +78,14 @@ function RenderContent(props) {
|
|
|
72
78
|
|
|
73
79
|
const [clicked, setClicked] = createSignal(false);
|
|
74
80
|
|
|
81
|
+
const [scriptStr, setScriptStr] = createSignal(
|
|
82
|
+
getRenderContentScriptString({
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain
|
|
84
|
+
contentId: props.content?.id,
|
|
85
|
+
parentContentId: props.parentContentId,
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
|
|
75
89
|
function mergeNewContent(newContent) {
|
|
76
90
|
setUseContent({
|
|
77
91
|
...useContent,
|
|
@@ -99,8 +113,8 @@ function RenderContent(props) {
|
|
|
99
113
|
});
|
|
100
114
|
}
|
|
101
115
|
|
|
102
|
-
function
|
|
103
|
-
setContentState(
|
|
116
|
+
function contentSetState(newRootState) {
|
|
117
|
+
setContentState(newRootState);
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
function processMessage(event) {
|
|
@@ -149,7 +163,9 @@ function RenderContent(props) {
|
|
|
149
163
|
evaluate({
|
|
150
164
|
code: jsCode,
|
|
151
165
|
context: props.context || {},
|
|
152
|
-
|
|
166
|
+
localState: undefined,
|
|
167
|
+
rootState: contentState(),
|
|
168
|
+
rootSetState: contentSetState,
|
|
153
169
|
});
|
|
154
170
|
}
|
|
155
171
|
}
|
|
@@ -178,7 +194,9 @@ function RenderContent(props) {
|
|
|
178
194
|
evaluate({
|
|
179
195
|
code: group,
|
|
180
196
|
context: props.context || {},
|
|
181
|
-
|
|
197
|
+
localState: undefined,
|
|
198
|
+
rootState: contentState(),
|
|
199
|
+
rootSetState: contentSetState,
|
|
182
200
|
})
|
|
183
201
|
);
|
|
184
202
|
}
|
|
@@ -191,7 +209,7 @@ function RenderContent(props) {
|
|
|
191
209
|
...contentState(),
|
|
192
210
|
[key]: json,
|
|
193
211
|
};
|
|
194
|
-
|
|
212
|
+
contentSetState(newState);
|
|
195
213
|
})
|
|
196
214
|
.catch((err) => {
|
|
197
215
|
console.error("error fetching dynamic data", url, err);
|
|
@@ -255,6 +273,11 @@ function RenderContent(props) {
|
|
|
255
273
|
includeRefs: props.includeRefs,
|
|
256
274
|
}
|
|
257
275
|
: {}),
|
|
276
|
+
...(props.enrich
|
|
277
|
+
? {
|
|
278
|
+
enrich: props.enrich,
|
|
279
|
+
}
|
|
280
|
+
: {}),
|
|
258
281
|
});
|
|
259
282
|
Object.values(allRegisteredComponents()).forEach(
|
|
260
283
|
(registeredComponent) => {
|
|
@@ -349,8 +372,9 @@ function RenderContent(props) {
|
|
|
349
372
|
<builderContext.Provider
|
|
350
373
|
value={{
|
|
351
374
|
content: useContent,
|
|
352
|
-
|
|
353
|
-
|
|
375
|
+
localState: undefined,
|
|
376
|
+
rootState: contentState(),
|
|
377
|
+
rootSetState: TARGET === "qwik" ? undefined : contentSetState,
|
|
354
378
|
context: props.context || {},
|
|
355
379
|
apiKey: props.apiKey,
|
|
356
380
|
apiVersion: props.apiVersion,
|
|
@@ -360,11 +384,30 @@ function RenderContent(props) {
|
|
|
360
384
|
>
|
|
361
385
|
<Show when={useContent}>
|
|
362
386
|
<div
|
|
387
|
+
class={props.classNameProp}
|
|
363
388
|
ref={elementRef}
|
|
364
389
|
onClick={(event) => onClick(event)}
|
|
365
390
|
builder-content-id={useContent?.id}
|
|
366
391
|
builder-model={props.model}
|
|
392
|
+
{...(TARGET === "reactNative"
|
|
393
|
+
? {
|
|
394
|
+
dataSet: {
|
|
395
|
+
// currently, we can't set the actual ID here.
|
|
396
|
+
// we don't need it right now, we just need to identify content divs for testing.
|
|
397
|
+
"builder-content-id": "",
|
|
398
|
+
},
|
|
399
|
+
}
|
|
400
|
+
: {})}
|
|
401
|
+
{...(props.hideContent
|
|
402
|
+
? {
|
|
403
|
+
hidden: true,
|
|
404
|
+
"aria-hidden": true,
|
|
405
|
+
}
|
|
406
|
+
: {})}
|
|
367
407
|
>
|
|
408
|
+
<Show when={props.isSsrAbTest}>
|
|
409
|
+
<script innerHTML={scriptStr()}></script>
|
|
410
|
+
</Show>
|
|
368
411
|
<Show when={TARGET !== "reactNative"}>
|
|
369
412
|
<RenderContentStyles
|
|
370
413
|
contentId={useContent?.id}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { isBrowser } from "../../functions/is-browser";
|
|
2
|
+
const getVariants = (content) => Object.values((content == null ? void 0 : content.variations) || {});
|
|
3
|
+
const checkShouldRunVariants = ({
|
|
4
|
+
canTrack,
|
|
5
|
+
content
|
|
6
|
+
}) => {
|
|
7
|
+
const hasVariants = getVariants(content).length > 0;
|
|
8
|
+
if (!hasVariants) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
if (!canTrack) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
if (isBrowser()) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
};
|
|
19
|
+
function bldrAbTest(contentId, variants, isHydrationTarget2) {
|
|
20
|
+
function getAndSetVariantId() {
|
|
21
|
+
function setCookie(name, value, days) {
|
|
22
|
+
let expires = "";
|
|
23
|
+
if (days) {
|
|
24
|
+
const date = new Date();
|
|
25
|
+
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1e3);
|
|
26
|
+
expires = "; expires=" + date.toUTCString();
|
|
27
|
+
}
|
|
28
|
+
document.cookie = name + "=" + (value || "") + expires + "; path=/; Secure; SameSite=None";
|
|
29
|
+
}
|
|
30
|
+
function getCookie(name) {
|
|
31
|
+
const nameEQ = name + "=";
|
|
32
|
+
const ca = document.cookie.split(";");
|
|
33
|
+
for (let i = 0; i < ca.length; i++) {
|
|
34
|
+
let c = ca[i];
|
|
35
|
+
while (c.charAt(0) === " ")
|
|
36
|
+
c = c.substring(1, c.length);
|
|
37
|
+
if (c.indexOf(nameEQ) === 0)
|
|
38
|
+
return c.substring(nameEQ.length, c.length);
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const cookieName = `builder.tests.${contentId}`;
|
|
43
|
+
const variantInCookie = getCookie(cookieName);
|
|
44
|
+
const availableIDs = variants.map((vr) => vr.id).concat(contentId);
|
|
45
|
+
if (variantInCookie && availableIDs.includes(variantInCookie)) {
|
|
46
|
+
return variantInCookie;
|
|
47
|
+
}
|
|
48
|
+
let n = 0;
|
|
49
|
+
const random = Math.random();
|
|
50
|
+
for (let i = 0; i < variants.length; i++) {
|
|
51
|
+
const variant = variants[i];
|
|
52
|
+
const testRatio = variant.testRatio;
|
|
53
|
+
n += testRatio;
|
|
54
|
+
if (random < n) {
|
|
55
|
+
setCookie(cookieName, variant.id);
|
|
56
|
+
return variant.id;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
setCookie(cookieName, contentId);
|
|
60
|
+
return contentId;
|
|
61
|
+
}
|
|
62
|
+
const winningVariantId = getAndSetVariantId();
|
|
63
|
+
const styleEl = document.getElementById(`variants-styles-${contentId}`);
|
|
64
|
+
if (isHydrationTarget2) {
|
|
65
|
+
styleEl.remove();
|
|
66
|
+
const thisScriptEl = document.getElementById(`variants-script-${contentId}`);
|
|
67
|
+
thisScriptEl == null ? void 0 : thisScriptEl.remove();
|
|
68
|
+
} else {
|
|
69
|
+
const newStyleStr = variants.concat({ id: contentId }).filter((variant) => variant.id !== winningVariantId).map((value) => {
|
|
70
|
+
return `.variant-${value.id} { display: none; }
|
|
71
|
+
`;
|
|
72
|
+
}).join("");
|
|
73
|
+
styleEl.innerHTML = newStyleStr;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function bldrCntntScrpt(variantContentId, defaultContentId, isHydrationTarget2) {
|
|
77
|
+
if (!navigator.cookieEnabled) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
function getCookie(name) {
|
|
81
|
+
const nameEQ = name + "=";
|
|
82
|
+
const ca = document.cookie.split(";");
|
|
83
|
+
for (let i = 0; i < ca.length; i++) {
|
|
84
|
+
let c = ca[i];
|
|
85
|
+
while (c.charAt(0) === " ")
|
|
86
|
+
c = c.substring(1, c.length);
|
|
87
|
+
if (c.indexOf(nameEQ) === 0)
|
|
88
|
+
return c.substring(nameEQ.length, c.length);
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const cookieName = `builder.tests.${defaultContentId}`;
|
|
93
|
+
const variantId = getCookie(cookieName);
|
|
94
|
+
const parentDiv = document.querySelector(`[builder-content-id="${variantContentId}"]`);
|
|
95
|
+
const variantIsDefaultContent = variantContentId === defaultContentId;
|
|
96
|
+
if (variantId === variantContentId) {
|
|
97
|
+
if (variantIsDefaultContent) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
parentDiv == null ? void 0 : parentDiv.removeAttribute("hidden");
|
|
101
|
+
parentDiv == null ? void 0 : parentDiv.removeAttribute("aria-hidden");
|
|
102
|
+
} else {
|
|
103
|
+
if (variantIsDefaultContent) {
|
|
104
|
+
if (isHydrationTarget2) {
|
|
105
|
+
parentDiv == null ? void 0 : parentDiv.remove();
|
|
106
|
+
} else {
|
|
107
|
+
parentDiv == null ? void 0 : parentDiv.setAttribute("hidden", "true");
|
|
108
|
+
parentDiv == null ? void 0 : parentDiv.setAttribute("aria-hidden", "true");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const isHydrationTarget = (target) => target === "react" || target === "reactNative" || target === "vue3" || target === "vue2";
|
|
116
|
+
const AB_TEST_FN_NAME = "bldrAbTest";
|
|
117
|
+
const CONTENT_FN_NAME = "bldrCntntScrpt";
|
|
118
|
+
const getVariantsScriptString = (variants, contentId) => {
|
|
119
|
+
const fnStr = bldrAbTest.toString().replace(/\s+/g, " ");
|
|
120
|
+
const fnStr2 = bldrCntntScrpt.toString().replace(/\s+/g, " ");
|
|
121
|
+
return `
|
|
122
|
+
const ${AB_TEST_FN_NAME} = ${fnStr}
|
|
123
|
+
const ${CONTENT_FN_NAME} = ${fnStr2}
|
|
124
|
+
${AB_TEST_FN_NAME}("${contentId}", ${JSON.stringify(variants)}, ${isHydrationTarget})
|
|
125
|
+
`;
|
|
126
|
+
};
|
|
127
|
+
const getRenderContentScriptString = ({
|
|
128
|
+
parentContentId,
|
|
129
|
+
contentId
|
|
130
|
+
}) => {
|
|
131
|
+
return `
|
|
132
|
+
${CONTENT_FN_NAME}("${contentId}", "${parentContentId}", ${isHydrationTarget})`;
|
|
133
|
+
};
|
|
134
|
+
export {
|
|
135
|
+
checkShouldRunVariants,
|
|
136
|
+
getRenderContentScriptString,
|
|
137
|
+
getVariants,
|
|
138
|
+
getVariantsScriptString
|
|
139
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Show, For, createSignal } from "solid-js";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
checkShouldRunVariants,
|
|
5
|
+
getVariants,
|
|
6
|
+
getVariantsScriptString,
|
|
7
|
+
} from "./helpers";
|
|
8
|
+
import RenderContent from "../render-content/render-content.jsx";
|
|
9
|
+
import { getDefaultCanTrack } from "../../helpers/canTrack";
|
|
10
|
+
import RenderInlinedStyles from "../render-inlined-styles.jsx";
|
|
11
|
+
import { handleABTestingSync } from "../../helpers/ab-tests";
|
|
12
|
+
|
|
13
|
+
function RenderContentVariants(props) {
|
|
14
|
+
const [variantScriptStr, setVariantScriptStr] = createSignal(
|
|
15
|
+
getVariantsScriptString(
|
|
16
|
+
getVariants(props.content).map((value) => ({
|
|
17
|
+
id: value.id,
|
|
18
|
+
testRatio: value.testRatio,
|
|
19
|
+
})),
|
|
20
|
+
props.content?.id || ""
|
|
21
|
+
)
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const [shouldRenderVariants, setShouldRenderVariants] = createSignal(
|
|
25
|
+
checkShouldRunVariants({
|
|
26
|
+
canTrack: getDefaultCanTrack(props.canTrack),
|
|
27
|
+
content: props.content,
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const [hideVariantsStyleString, setHideVariantsStyleString] = createSignal(
|
|
32
|
+
getVariants(props.content)
|
|
33
|
+
.map((value) => `.variant-${value.id} { display: none; } `)
|
|
34
|
+
.join("")
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const [contentToRender, setContentToRender] = createSignal(
|
|
38
|
+
checkShouldRunVariants({
|
|
39
|
+
canTrack: getDefaultCanTrack(props.canTrack),
|
|
40
|
+
content: props.content,
|
|
41
|
+
})
|
|
42
|
+
? props.content
|
|
43
|
+
: handleABTestingSync({
|
|
44
|
+
item: props.content,
|
|
45
|
+
canTrack: getDefaultCanTrack(props.canTrack),
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<>
|
|
51
|
+
<Show when={shouldRenderVariants()}>
|
|
52
|
+
<RenderInlinedStyles
|
|
53
|
+
id={`variants-styles-${props.content?.id}`}
|
|
54
|
+
styles={hideVariantsStyleString()}
|
|
55
|
+
></RenderInlinedStyles>
|
|
56
|
+
<script
|
|
57
|
+
id={`variants-script-${props.content?.id}`}
|
|
58
|
+
innerHTML={variantScriptStr()}
|
|
59
|
+
></script>
|
|
60
|
+
<For each={getVariants(props.content)}>
|
|
61
|
+
{(variant, _index) => {
|
|
62
|
+
const index = _index();
|
|
63
|
+
return (
|
|
64
|
+
<RenderContent
|
|
65
|
+
key={variant.id}
|
|
66
|
+
content={variant}
|
|
67
|
+
apiKey={props.apiKey}
|
|
68
|
+
apiVersion={props.apiVersion}
|
|
69
|
+
canTrack={props.canTrack}
|
|
70
|
+
customComponents={props.customComponents}
|
|
71
|
+
hideContent={true}
|
|
72
|
+
parentContentId={props.content?.id}
|
|
73
|
+
isSsrAbTest={shouldRenderVariants()}
|
|
74
|
+
></RenderContent>
|
|
75
|
+
);
|
|
76
|
+
}}
|
|
77
|
+
</For>
|
|
78
|
+
</Show>
|
|
79
|
+
<RenderContent
|
|
80
|
+
model={props.model}
|
|
81
|
+
content={contentToRender()}
|
|
82
|
+
apiKey={props.apiKey}
|
|
83
|
+
apiVersion={props.apiVersion}
|
|
84
|
+
canTrack={props.canTrack}
|
|
85
|
+
customComponents={props.customComponents}
|
|
86
|
+
classNameProp={`variant-${props.content?.id}`}
|
|
87
|
+
parentContentId={props.content?.id}
|
|
88
|
+
isSsrAbTest={shouldRenderVariants()}
|
|
89
|
+
></RenderContent>
|
|
90
|
+
</>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default RenderContentVariants;
|
|
@@ -1,27 +1,5 @@
|
|
|
1
|
-
import { Show, createSignal } from "solid-js";
|
|
2
|
-
import { Dynamic } from "solid-js/web";
|
|
3
|
-
|
|
4
|
-
import { TARGET } from "../constants/target.js";
|
|
5
|
-
|
|
6
1
|
function RenderInlinedStyles(props) {
|
|
7
|
-
|
|
8
|
-
// NOTE: we have to obfusctate the name of the tag due to a limitation in the svelte-preprocessor plugin.
|
|
9
|
-
// https://github.com/sveltejs/vite-plugin-svelte/issues/315#issuecomment-1109000027
|
|
10
|
-
return "sty" + "le";
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function injectedStyleScript() {
|
|
14
|
-
return `<${tag()}>${props.styles}</${tag()}>`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<Show
|
|
19
|
-
fallback={<Dynamic component={tag()}>{props.styles}</Dynamic>}
|
|
20
|
-
when={TARGET === "svelte" || TARGET === "qwik"}
|
|
21
|
-
>
|
|
22
|
-
<Dynamic innerHTML={props.styles} component={tag()}></Dynamic>
|
|
23
|
-
</Show>
|
|
24
|
-
);
|
|
2
|
+
return <style innerHTML={props.styles} id={props.id}></style>;
|
|
25
3
|
}
|
|
26
4
|
|
|
27
5
|
export default RenderInlinedStyles;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SDK_VERSION = "0.4.0"
|
|
@@ -2,9 +2,10 @@ import { createContext } from "solid-js";
|
|
|
2
2
|
var stdin_default = createContext({
|
|
3
3
|
content: null,
|
|
4
4
|
context: {},
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
localState: void 0,
|
|
6
|
+
rootSetState() {
|
|
7
7
|
},
|
|
8
|
+
rootState: {},
|
|
8
9
|
apiKey: null,
|
|
9
10
|
apiVersion: void 0,
|
|
10
11
|
registeredComponents: {},
|