@builder.io/sdk-svelte 0.3.1 → 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/package/--package.json +8 -5
- package/package/CHANGELOG.md +13 -0
- package/package/components/render-block/render-block.svelte +6 -6
- package/package/components/render-content/components/render-styles.svelte +1 -1
- package/package/components/render-content/render-content.svelte +84 -2
- package/package/components/render-content/render-content.svelte.d.ts +5 -0
- package/package/components/render-content/render-content.types.d.ts +11 -2
- package/package/components/render-content/wrap-component-ref.d.ts +6 -0
- package/package/components/render-content/wrap-component-ref.js +6 -0
- package/package/components/render-content-variants/helpers.d.ts +17 -0
- package/package/components/render-content-variants/helpers.js +182 -0
- package/package/components/render-content-variants/render-content-variants.svelte +70 -0
- package/package/components/render-content-variants/render-content-variants.svelte.d.ts +23 -0
- package/package/components/render-inlined-styles.svelte +3 -15
- package/package/components/render-inlined-styles.svelte.d.ts +2 -0
- package/package/constants/sdk-version.d.ts +1 -1
- package/package/constants/sdk-version.js +1 -1
- package/package/functions/get-content/generate-content-url.js +2 -2
- package/package/functions/get-content/generate-content-url.test.js +15 -0
- package/package/functions/get-content/index.d.ts +7 -2
- package/package/functions/get-content/index.js +43 -20
- package/package/functions/get-content/types.d.ts +6 -0
- package/package/helpers/ab-tests.d.ts +8 -7
- package/package/helpers/ab-tests.js +103 -4
- package/package/helpers/canTrack.d.ts +1 -0
- package/package/helpers/canTrack.js +2 -0
- package/package/helpers/cookie.d.ts +7 -3
- package/package/helpers/cookie.js +9 -6
- package/package/helpers/logger.d.ts +1 -0
- package/package/helpers/logger.js +1 -0
- package/package/index-helpers/blocks-exports.d.ts +1 -1
- package/package/index-helpers/blocks-exports.js +1 -1
- package/package/index.d.ts +8 -7
- package/package/index.js +6 -7
- package/package/scripts/init-editing.d.ts +1 -0
- package/package/types/builder-content.d.ts +1 -3
- package/package.json +4 -5
- package/src/components/render-block/render-block.svelte +6 -6
- package/src/components/render-content/components/render-styles.svelte +1 -1
- package/src/components/render-content/render-content.svelte +86 -2
- package/src/components/render-content/render-content.types.ts +11 -2
- package/src/components/render-content/wrap-component-ref.ts +6 -0
- package/src/components/render-content-variants/helpers.ts +245 -0
- package/src/components/render-content-variants/render-content-variants.svelte +83 -0
- package/src/components/render-inlined-styles.svelte +3 -16
- package/src/constants/sdk-version.ts +1 -1
- package/src/functions/get-content/generate-content-url.test.ts +17 -0
- package/src/functions/get-content/generate-content-url.ts +2 -1
- package/src/functions/get-content/index.ts +57 -26
- package/src/functions/get-content/types.ts +7 -0
- package/src/helpers/ab-tests.ts +164 -11
- package/src/helpers/canTrack.ts +4 -0
- package/src/helpers/cookie.ts +16 -11
- package/src/helpers/logger.ts +1 -0
- package/src/index-helpers/blocks-exports.ts +1 -1
- package/src/index.ts +17 -8
- package/src/scripts/init-editing.ts +1 -0
- package/src/types/builder-content.ts +1 -3
- package/package/functions/get-content/ab-testing.d.ts +0 -5
- package/package/functions/get-content/ab-testing.js +0 -78
- package/src/functions/get-content/ab-testing.ts +0 -132
package/package/--package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@builder.io/sdk-svelte",
|
|
3
3
|
"description": "Builder.io SDK for Svelte",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"package",
|
|
@@ -78,6 +78,9 @@
|
|
|
78
78
|
"./components/render-content/render-content.helpers": "./components/render-content/render-content.helpers.js",
|
|
79
79
|
"./components/render-content/render-content.svelte": "./components/render-content/render-content.svelte",
|
|
80
80
|
"./components/render-content/render-content.types": "./components/render-content/render-content.types.js",
|
|
81
|
+
"./components/render-content/wrap-component-ref": "./components/render-content/wrap-component-ref.js",
|
|
82
|
+
"./components/render-content-variants/helpers": "./components/render-content-variants/helpers.js",
|
|
83
|
+
"./components/render-content-variants/render-content-variants.svelte": "./components/render-content-variants/render-content-variants.svelte",
|
|
81
84
|
"./components/render-inlined-styles.svelte": "./components/render-inlined-styles.svelte",
|
|
82
85
|
"./constants/builder-registered-components": "./constants/builder-registered-components.js",
|
|
83
86
|
"./constants/device-sizes": "./constants/device-sizes.js",
|
|
@@ -97,7 +100,6 @@
|
|
|
97
100
|
"./functions/get-block-properties": "./functions/get-block-properties.js",
|
|
98
101
|
"./functions/get-builder-search-params/fn.test": "./functions/get-builder-search-params/fn.test.js",
|
|
99
102
|
"./functions/get-builder-search-params": "./functions/get-builder-search-params/index.js",
|
|
100
|
-
"./functions/get-content/ab-testing": "./functions/get-content/ab-testing.js",
|
|
101
103
|
"./functions/get-content/generate-content-url.test": "./functions/get-content/generate-content-url.test.js",
|
|
102
104
|
"./functions/get-content/generate-content-url": "./functions/get-content/generate-content-url.js",
|
|
103
105
|
"./functions/get-content": "./functions/get-content/index.js",
|
|
@@ -126,6 +128,7 @@
|
|
|
126
128
|
"./functions/transform-block-properties": "./functions/transform-block-properties.js",
|
|
127
129
|
"./functions/transform-block": "./functions/transform-block.js",
|
|
128
130
|
"./helpers/ab-tests": "./helpers/ab-tests.js",
|
|
131
|
+
"./helpers/canTrack": "./helpers/canTrack.js",
|
|
129
132
|
"./helpers/cookie": "./helpers/cookie.js",
|
|
130
133
|
"./helpers/css": "./helpers/css.js",
|
|
131
134
|
"./helpers/flatten": "./helpers/flatten.js",
|
|
@@ -155,10 +158,10 @@
|
|
|
155
158
|
"types": "./package/index.d.ts",
|
|
156
159
|
"devDependencies": {
|
|
157
160
|
"@sveltejs/adapter-auto": "1.0.0-next.71",
|
|
158
|
-
"@sveltejs/kit": "^1.
|
|
161
|
+
"@sveltejs/kit": "^1.16.2",
|
|
159
162
|
"@sveltejs/package": "1.0.0-next.1",
|
|
160
|
-
"svelte": "^3.
|
|
161
|
-
"svelte-check": "^
|
|
163
|
+
"svelte": "^3.59.1",
|
|
164
|
+
"svelte-check": "^3.3.2",
|
|
162
165
|
"svelte-preprocess": "^4.10.6",
|
|
163
166
|
"tslib": "^2.3.1",
|
|
164
167
|
"typescript": "^4",
|
package/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.
|
|
@@ -139,23 +139,23 @@ let component = getComponent({
|
|
|
139
139
|
block: block,
|
|
140
140
|
context: context,
|
|
141
141
|
});
|
|
142
|
-
let
|
|
142
|
+
let Tag = block.tagName || "div";
|
|
143
143
|
</script>
|
|
144
144
|
|
|
145
145
|
{#if canShowBlock()}
|
|
146
146
|
{#if !component?.noWrap}
|
|
147
|
-
{#if isEmptyHtmlElement(
|
|
148
|
-
<svelte:element this={
|
|
147
|
+
{#if isEmptyHtmlElement(Tag)}
|
|
148
|
+
<svelte:element this={Tag} {...attributes()} use:setAttrs={actions()} />
|
|
149
149
|
{/if}
|
|
150
150
|
|
|
151
|
-
{#if !isEmptyHtmlElement(
|
|
151
|
+
{#if !isEmptyHtmlElement(Tag) && repeatItem()}
|
|
152
152
|
{#each repeatItem() as data, index (index)}
|
|
153
153
|
<RenderRepeatedBlock repeatContext={data.context} block={data.block} />
|
|
154
154
|
{/each}
|
|
155
155
|
{/if}
|
|
156
156
|
|
|
157
|
-
{#if !isEmptyHtmlElement(
|
|
158
|
-
<svelte:element this={
|
|
157
|
+
{#if !isEmptyHtmlElement(Tag) && !repeatItem()}
|
|
158
|
+
<svelte:element this={Tag} {...attributes()} use:setAttrs={actions()}>
|
|
159
159
|
<RenderComponent {...renderComponentProps()} />
|
|
160
160
|
|
|
161
161
|
{#each childrenWithoutParentComponent() as child ("render-block-" + child.id)}
|
|
@@ -17,6 +17,35 @@ import { getInteractionPropertiesForEvent } from "../../functions/track/interact
|
|
|
17
17
|
import { getContentInitialValue, getContextStateInitialValue, } from "./render-content.helpers.js";
|
|
18
18
|
import { TARGET } from "../../constants/target.js";
|
|
19
19
|
import { logger } from "../../helpers/logger.js";
|
|
20
|
+
import { getRenderContentScriptString } from "../render-content-variants/helpers.js";
|
|
21
|
+
import { wrapComponentRef } from "./wrap-component-ref.js";
|
|
22
|
+
const isEvent = (attr) => attr.startsWith("on:");
|
|
23
|
+
const isNonEvent = (attr) => !attr.startsWith("on:");
|
|
24
|
+
const filterAttrs = (attrs = {}, filter) => {
|
|
25
|
+
const validAttr = {};
|
|
26
|
+
Object.keys(attrs).forEach((attr) => {
|
|
27
|
+
if (filter(attr)) {
|
|
28
|
+
validAttr[attr] = attrs[attr];
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return validAttr;
|
|
32
|
+
};
|
|
33
|
+
const setAttrs = (node, attrs = {}) => {
|
|
34
|
+
const attrKeys = Object.keys(attrs);
|
|
35
|
+
const setup = (attr) => node.addEventListener(attr.substr(3), attrs[attr]);
|
|
36
|
+
const teardown = (attr) => node.removeEventListener(attr.substr(3), attrs[attr]);
|
|
37
|
+
attrKeys.map(setup);
|
|
38
|
+
return {
|
|
39
|
+
update(attrs = {}) {
|
|
40
|
+
const attrKeys = Object.keys(attrs);
|
|
41
|
+
attrKeys.map(teardown);
|
|
42
|
+
attrKeys.map(setup);
|
|
43
|
+
},
|
|
44
|
+
destroy() {
|
|
45
|
+
attrKeys.map(teardown);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
};
|
|
20
49
|
export let content;
|
|
21
50
|
export let data;
|
|
22
51
|
export let canTrack;
|
|
@@ -25,7 +54,12 @@ export let customComponents;
|
|
|
25
54
|
export let model;
|
|
26
55
|
export let context;
|
|
27
56
|
export let apiKey;
|
|
57
|
+
export let parentContentId;
|
|
58
|
+
export let hideContent;
|
|
59
|
+
export let classNameProp;
|
|
60
|
+
export let isSsrAbTest;
|
|
28
61
|
export let includeRefs;
|
|
62
|
+
export let enrich;
|
|
29
63
|
export let apiVersion;
|
|
30
64
|
function mergeNewContent(newContent) {
|
|
31
65
|
useContent = {
|
|
@@ -191,12 +225,20 @@ let allRegisteredComponents = [
|
|
|
191
225
|
// which is the new standard way of providing custom components, and must therefore take precedence.
|
|
192
226
|
...components,
|
|
193
227
|
...(customComponents || []),
|
|
194
|
-
].reduce((acc, curr) => ({
|
|
228
|
+
].reduce((acc, { component, ...curr }) => ({
|
|
195
229
|
...acc,
|
|
196
|
-
[curr.name]:
|
|
230
|
+
[curr.name]: {
|
|
231
|
+
component: TARGET === "vue3" ? wrapComponentRef(component) : component,
|
|
232
|
+
...curr,
|
|
233
|
+
},
|
|
197
234
|
}), {});
|
|
198
235
|
let httpReqsData = {};
|
|
199
236
|
let clicked = false;
|
|
237
|
+
let scriptStr = getRenderContentScriptString({
|
|
238
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain
|
|
239
|
+
contentId: content?.id,
|
|
240
|
+
parentContentId: parentContentId,
|
|
241
|
+
});
|
|
200
242
|
onMount(() => {
|
|
201
243
|
if (!apiKey) {
|
|
202
244
|
logger.error("No API key provided to `RenderContent` component. This can cause issues. Please provide an API key using the `apiKey` prop.");
|
|
@@ -216,6 +258,11 @@ onMount(() => {
|
|
|
216
258
|
includeRefs: includeRefs,
|
|
217
259
|
}
|
|
218
260
|
: {}),
|
|
261
|
+
...(enrich
|
|
262
|
+
? {
|
|
263
|
+
enrich: enrich,
|
|
264
|
+
}
|
|
265
|
+
: {}),
|
|
219
266
|
});
|
|
220
267
|
Object.values(allRegisteredComponents).forEach((registeredComponent) => {
|
|
221
268
|
const message = createRegisterComponentMessage(registeredComponent);
|
|
@@ -332,7 +379,42 @@ onDestroy(() => {
|
|
|
332
379
|
}}
|
|
333
380
|
builder-content-id={useContent?.id}
|
|
334
381
|
builder-model={model}
|
|
382
|
+
{...filterAttrs(
|
|
383
|
+
TARGET === "reactNative"
|
|
384
|
+
? {
|
|
385
|
+
dataSet: {
|
|
386
|
+
// currently, we can't set the actual ID here.
|
|
387
|
+
// we don't need it right now, we just need to identify content divs for testing.
|
|
388
|
+
"builder-content-id": "",
|
|
389
|
+
},
|
|
390
|
+
}
|
|
391
|
+
: {},
|
|
392
|
+
isNonEvent
|
|
393
|
+
)}
|
|
394
|
+
{...hideContent
|
|
395
|
+
? {
|
|
396
|
+
hidden: true,
|
|
397
|
+
"aria-hidden": true,
|
|
398
|
+
}
|
|
399
|
+
: {}}
|
|
400
|
+
class={classNameProp}
|
|
401
|
+
use:setAttrs={filterAttrs(
|
|
402
|
+
TARGET === "reactNative"
|
|
403
|
+
? {
|
|
404
|
+
dataSet: {
|
|
405
|
+
// currently, we can't set the actual ID here.
|
|
406
|
+
// we don't need it right now, we just need to identify content divs for testing.
|
|
407
|
+
"builder-content-id": "",
|
|
408
|
+
},
|
|
409
|
+
}
|
|
410
|
+
: {},
|
|
411
|
+
isEvent
|
|
412
|
+
)}
|
|
335
413
|
>
|
|
414
|
+
{#if isSsrAbTest}
|
|
415
|
+
<svelte:element this={"script"}>{@html scriptStr}</svelte:element>
|
|
416
|
+
{/if}
|
|
417
|
+
|
|
336
418
|
{#if TARGET !== "reactNative"}
|
|
337
419
|
<RenderContentStyles
|
|
338
420
|
contentId={useContent?.id}
|
|
@@ -10,7 +10,12 @@ declare const __propDef: {
|
|
|
10
10
|
model: RenderContentProps["model"];
|
|
11
11
|
context: RenderContentProps["context"];
|
|
12
12
|
apiKey: RenderContentProps["apiKey"];
|
|
13
|
+
parentContentId: RenderContentProps["parentContentId"];
|
|
14
|
+
hideContent: RenderContentProps["hideContent"];
|
|
15
|
+
classNameProp: RenderContentProps["classNameProp"];
|
|
16
|
+
isSsrAbTest: RenderContentProps["isSsrAbTest"];
|
|
13
17
|
includeRefs: RenderContentProps["includeRefs"];
|
|
18
|
+
enrich: RenderContentProps["enrich"];
|
|
14
19
|
apiVersion: RenderContentProps["apiVersion"];
|
|
15
20
|
};
|
|
16
21
|
events: {
|
|
@@ -2,7 +2,7 @@ import type { BuilderRenderContext, RegisteredComponent, BuilderRenderState } fr
|
|
|
2
2
|
import type { BuilderContent } from '../../types/builder-content';
|
|
3
3
|
import type { Nullable } from '../../types/typescript';
|
|
4
4
|
import type { ApiVersion } from '../../types/api-version';
|
|
5
|
-
export
|
|
5
|
+
export interface RenderContentProps {
|
|
6
6
|
content?: Nullable<BuilderContent>;
|
|
7
7
|
model?: string;
|
|
8
8
|
data?: {
|
|
@@ -14,8 +14,17 @@ export type RenderContentProps = {
|
|
|
14
14
|
customComponents?: RegisteredComponent[];
|
|
15
15
|
canTrack?: boolean;
|
|
16
16
|
locale?: string;
|
|
17
|
+
/** @deprecated use `enrich` instead **/
|
|
17
18
|
includeRefs?: boolean;
|
|
18
|
-
|
|
19
|
+
enrich?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* TO-DO: improve qwik generator to not remap this name for non-HTML tags, then name it `className`
|
|
22
|
+
*/
|
|
23
|
+
classNameProp?: string;
|
|
24
|
+
hideContent?: boolean;
|
|
25
|
+
parentContentId?: string;
|
|
26
|
+
isSsrAbTest?: boolean;
|
|
27
|
+
}
|
|
19
28
|
export interface BuilderComponentStateChange {
|
|
20
29
|
state: BuilderRenderState;
|
|
21
30
|
ref: {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Nullable } from '../../helpers/nullable';
|
|
2
|
+
import type { BuilderContent } from '../../types/builder-content';
|
|
3
|
+
export declare const getVariants: (content: Nullable<BuilderContent>) => import("../../types/builder-content").BuilderContentVariation[];
|
|
4
|
+
export declare const checkShouldRunVariants: ({ canTrack, content, }: {
|
|
5
|
+
canTrack: Nullable<boolean>;
|
|
6
|
+
content: Nullable<BuilderContent>;
|
|
7
|
+
}) => boolean;
|
|
8
|
+
type VariantData = {
|
|
9
|
+
id: string;
|
|
10
|
+
testRatio?: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const getVariantsScriptString: (variants: VariantData[], contentId: string) => string;
|
|
13
|
+
export declare const getRenderContentScriptString: ({ parentContentId, contentId, }: {
|
|
14
|
+
contentId: string;
|
|
15
|
+
parentContentId: string;
|
|
16
|
+
}) => string;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { isBrowser } from '../../functions/is-browser';
|
|
2
|
+
export const getVariants = (content) => Object.values(content?.variations || {});
|
|
3
|
+
export const checkShouldRunVariants = ({ canTrack, content, }) => {
|
|
4
|
+
const hasVariants = getVariants(content).length > 0;
|
|
5
|
+
if (!hasVariants) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
if (!canTrack) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
if (isBrowser()) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* NOTE: when this function is stringified, single-line comments can cause weird issues when compiled by Sveltekit.
|
|
18
|
+
* Make sure to write multi-line comments only.
|
|
19
|
+
*/
|
|
20
|
+
function bldrAbTest(contentId, variants, isHydrationTarget) {
|
|
21
|
+
function getAndSetVariantId() {
|
|
22
|
+
function setCookie(name, value, days) {
|
|
23
|
+
let expires = '';
|
|
24
|
+
if (days) {
|
|
25
|
+
const date = new Date();
|
|
26
|
+
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
|
|
27
|
+
expires = '; expires=' + date.toUTCString();
|
|
28
|
+
}
|
|
29
|
+
document.cookie =
|
|
30
|
+
name +
|
|
31
|
+
'=' +
|
|
32
|
+
(value || '') +
|
|
33
|
+
expires +
|
|
34
|
+
'; path=/' +
|
|
35
|
+
'; 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
|
+
const cookieName = `builder.tests.${contentId}`;
|
|
50
|
+
const variantInCookie = getCookie(cookieName);
|
|
51
|
+
const availableIDs = variants.map((vr) => vr.id).concat(contentId);
|
|
52
|
+
/**
|
|
53
|
+
* cookie already exists
|
|
54
|
+
*/
|
|
55
|
+
if (variantInCookie && availableIDs.includes(variantInCookie)) {
|
|
56
|
+
return variantInCookie;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* no cookie exists, find variant
|
|
60
|
+
*/
|
|
61
|
+
let n = 0;
|
|
62
|
+
const random = Math.random();
|
|
63
|
+
for (let i = 0; i < variants.length; i++) {
|
|
64
|
+
const variant = variants[i];
|
|
65
|
+
const testRatio = variant.testRatio;
|
|
66
|
+
n += testRatio;
|
|
67
|
+
if (random < n) {
|
|
68
|
+
setCookie(cookieName, variant.id);
|
|
69
|
+
return variant.id;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* no variant found, assign default content
|
|
74
|
+
*/
|
|
75
|
+
setCookie(cookieName, contentId);
|
|
76
|
+
return contentId;
|
|
77
|
+
}
|
|
78
|
+
const winningVariantId = getAndSetVariantId();
|
|
79
|
+
const styleEl = document.getElementById(`variants-styles-${contentId}`);
|
|
80
|
+
/**
|
|
81
|
+
* For React to work, we need hydration to match SSR, so we completely remove this node and the styles tag.
|
|
82
|
+
*/
|
|
83
|
+
if (isHydrationTarget) {
|
|
84
|
+
styleEl.remove();
|
|
85
|
+
const thisScriptEl = document.getElementById(`variants-script-${contentId}`);
|
|
86
|
+
thisScriptEl?.remove();
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
/* update styles to hide all variants except the winning variant */
|
|
90
|
+
const newStyleStr = variants
|
|
91
|
+
.concat({ id: contentId })
|
|
92
|
+
.filter((variant) => variant.id !== winningVariantId)
|
|
93
|
+
.map((value) => {
|
|
94
|
+
return `.variant-${value.id} { display: none; }
|
|
95
|
+
`;
|
|
96
|
+
})
|
|
97
|
+
.join('');
|
|
98
|
+
/* TO-DO: check if this actually updates the style */
|
|
99
|
+
styleEl.innerHTML = newStyleStr;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* NOTE: when this function is stringified, single-line comments can cause weird issues when compiled by Sveltekit.
|
|
104
|
+
* Make sure to write multi-line comments only.
|
|
105
|
+
*/
|
|
106
|
+
function bldrCntntScrpt(variantContentId, defaultContentId, isHydrationTarget) {
|
|
107
|
+
if (!navigator.cookieEnabled) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
function getCookie(name) {
|
|
111
|
+
const nameEQ = name + '=';
|
|
112
|
+
const ca = document.cookie.split(';');
|
|
113
|
+
for (let i = 0; i < ca.length; i++) {
|
|
114
|
+
let c = ca[i];
|
|
115
|
+
while (c.charAt(0) === ' ')
|
|
116
|
+
c = c.substring(1, c.length);
|
|
117
|
+
if (c.indexOf(nameEQ) === 0)
|
|
118
|
+
return c.substring(nameEQ.length, c.length);
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const cookieName = `builder.tests.${defaultContentId}`;
|
|
123
|
+
const variantId = getCookie(cookieName);
|
|
124
|
+
/** get parent div by searching on `builder-content-id` attr */
|
|
125
|
+
const parentDiv = document.querySelector(`[builder-content-id="${variantContentId}"]`);
|
|
126
|
+
const variantIsDefaultContent = variantContentId === defaultContentId;
|
|
127
|
+
if (variantId === variantContentId) {
|
|
128
|
+
if (variantIsDefaultContent) {
|
|
129
|
+
/** the default content is already visible, no need to do anything */
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
/** this is the winning variant and not already visible: remove `hidden` and `aria-hidden` attr */
|
|
133
|
+
parentDiv?.removeAttribute('hidden');
|
|
134
|
+
parentDiv?.removeAttribute('aria-hidden');
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
if (variantIsDefaultContent) {
|
|
138
|
+
if (isHydrationTarget) {
|
|
139
|
+
/**
|
|
140
|
+
* For React to work, we need to support hydration, in which case the first CSR will have none of the hidden variants.
|
|
141
|
+
* So we completely remove that node.
|
|
142
|
+
*/
|
|
143
|
+
parentDiv?.remove();
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
/** this is not the winning variant, add `hidden` attr */
|
|
147
|
+
parentDiv?.setAttribute('hidden', 'true');
|
|
148
|
+
parentDiv?.setAttribute('aria-hidden', 'true');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** This is not the winning variant, and it's not the default content.
|
|
152
|
+
* There's no need to hide it, because it's already hidden.
|
|
153
|
+
*/
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const isHydrationTarget = (target) => target === 'react' ||
|
|
159
|
+
target === 'reactNative' ||
|
|
160
|
+
target === 'vue3' ||
|
|
161
|
+
target === 'vue2';
|
|
162
|
+
/**
|
|
163
|
+
* We hardcode explicit function names here, because the `.toString()` of a function can change depending on the bundler.
|
|
164
|
+
* Some bundlers will minify the fn name, etc.
|
|
165
|
+
*
|
|
166
|
+
* So we hardcode the function names here, and then use those names in the script string to make sure the function names are consistent.
|
|
167
|
+
*/
|
|
168
|
+
const AB_TEST_FN_NAME = 'bldrAbTest';
|
|
169
|
+
const CONTENT_FN_NAME = 'bldrCntntScrpt';
|
|
170
|
+
export const getVariantsScriptString = (variants, contentId) => {
|
|
171
|
+
const fnStr = bldrAbTest.toString().replace(/\s+/g, ' ');
|
|
172
|
+
const fnStr2 = bldrCntntScrpt.toString().replace(/\s+/g, ' ');
|
|
173
|
+
return `
|
|
174
|
+
const ${AB_TEST_FN_NAME} = ${fnStr}
|
|
175
|
+
const ${CONTENT_FN_NAME} = ${fnStr2}
|
|
176
|
+
${AB_TEST_FN_NAME}("${contentId}", ${JSON.stringify(variants)}, ${isHydrationTarget})
|
|
177
|
+
`;
|
|
178
|
+
};
|
|
179
|
+
export const getRenderContentScriptString = ({ parentContentId, contentId, }) => {
|
|
180
|
+
return `
|
|
181
|
+
${CONTENT_FN_NAME}("${contentId}", "${parentContentId}", ${isHydrationTarget})`;
|
|
182
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script context="module"></script>
|
|
2
|
+
|
|
3
|
+
<script>import { checkShouldRunVariants, getVariants, getVariantsScriptString, } from "./helpers";
|
|
4
|
+
import RenderContent from "../render-content/render-content.svelte";
|
|
5
|
+
import { getDefaultCanTrack } from "../../helpers/canTrack";
|
|
6
|
+
import RenderInlinedStyles from "../render-inlined-styles.svelte";
|
|
7
|
+
import { handleABTestingSync } from "../../helpers/ab-tests";
|
|
8
|
+
export let content;
|
|
9
|
+
export let canTrack;
|
|
10
|
+
export let apiKey;
|
|
11
|
+
export let apiVersion;
|
|
12
|
+
export let customComponents;
|
|
13
|
+
export let model;
|
|
14
|
+
let variantScriptStr = getVariantsScriptString(getVariants(content).map((value) => ({
|
|
15
|
+
id: value.id,
|
|
16
|
+
testRatio: value.testRatio,
|
|
17
|
+
})), content?.id || "");
|
|
18
|
+
let shouldRenderVariants = checkShouldRunVariants({
|
|
19
|
+
canTrack: getDefaultCanTrack(canTrack),
|
|
20
|
+
content: content,
|
|
21
|
+
});
|
|
22
|
+
let hideVariantsStyleString = getVariants(content)
|
|
23
|
+
.map((value) => `.variant-${value.id} { display: none; } `)
|
|
24
|
+
.join("");
|
|
25
|
+
let contentToRender = checkShouldRunVariants({
|
|
26
|
+
canTrack: getDefaultCanTrack(canTrack),
|
|
27
|
+
content: content,
|
|
28
|
+
})
|
|
29
|
+
? content
|
|
30
|
+
: handleABTestingSync({
|
|
31
|
+
item: content,
|
|
32
|
+
canTrack: getDefaultCanTrack(canTrack),
|
|
33
|
+
});
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
{#if shouldRenderVariants}
|
|
37
|
+
<RenderInlinedStyles
|
|
38
|
+
id={`variants-styles-${content?.id}`}
|
|
39
|
+
styles={hideVariantsStyleString}
|
|
40
|
+
/>
|
|
41
|
+
|
|
42
|
+
<svelte:element this={"script"} id={`variants-script-${content?.id}`}
|
|
43
|
+
>{@html variantScriptStr}</svelte:element
|
|
44
|
+
>
|
|
45
|
+
|
|
46
|
+
{#each getVariants(content) as variant (variant.id)}
|
|
47
|
+
<RenderContent
|
|
48
|
+
content={variant}
|
|
49
|
+
{apiKey}
|
|
50
|
+
{apiVersion}
|
|
51
|
+
{canTrack}
|
|
52
|
+
{customComponents}
|
|
53
|
+
hideContent={true}
|
|
54
|
+
parentContentId={content?.id}
|
|
55
|
+
isSsrAbTest={shouldRenderVariants}
|
|
56
|
+
/>
|
|
57
|
+
{/each}
|
|
58
|
+
{/if}
|
|
59
|
+
|
|
60
|
+
<RenderContent
|
|
61
|
+
{model}
|
|
62
|
+
content={contentToRender}
|
|
63
|
+
{apiKey}
|
|
64
|
+
{apiVersion}
|
|
65
|
+
{canTrack}
|
|
66
|
+
{customComponents}
|
|
67
|
+
classNameProp={`variant-${content?.id}`}
|
|
68
|
+
parentContentId={content?.id}
|
|
69
|
+
isSsrAbTest={shouldRenderVariants}
|
|
70
|
+
/>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
type VariantsProviderProps = RenderContentProps;
|
|
3
|
+
import type { RenderContentProps } from "../render-content/render-content.types";
|
|
4
|
+
declare const __propDef: {
|
|
5
|
+
props: {
|
|
6
|
+
content: VariantsProviderProps["content"];
|
|
7
|
+
canTrack: VariantsProviderProps["canTrack"];
|
|
8
|
+
apiKey: VariantsProviderProps["apiKey"];
|
|
9
|
+
apiVersion: VariantsProviderProps["apiVersion"];
|
|
10
|
+
customComponents: VariantsProviderProps["customComponents"];
|
|
11
|
+
model: VariantsProviderProps["model"];
|
|
12
|
+
};
|
|
13
|
+
events: {
|
|
14
|
+
[evt: string]: CustomEvent<any>;
|
|
15
|
+
};
|
|
16
|
+
slots: {};
|
|
17
|
+
};
|
|
18
|
+
export type RenderContentVariantsProps = typeof __propDef.props;
|
|
19
|
+
export type RenderContentVariantsEvents = typeof __propDef.events;
|
|
20
|
+
export type RenderContentVariantsSlots = typeof __propDef.slots;
|
|
21
|
+
export default class RenderContentVariants extends SvelteComponentTyped<RenderContentVariantsProps, RenderContentVariantsEvents, RenderContentVariantsSlots> {
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
<script context="module"></script>
|
|
2
2
|
|
|
3
|
-
<script>
|
|
4
|
-
export let
|
|
5
|
-
$: tag = () => {
|
|
6
|
-
// NOTE: we have to obfuscate the name of the tag due to a limitation in the svelte-preprocessor plugin.
|
|
7
|
-
// https://github.com/sveltejs/vite-plugin-svelte/issues/315#issuecomment-1109000027
|
|
8
|
-
return ("sty" + "le");
|
|
9
|
-
};
|
|
10
|
-
$: injectedStyleScript = () => {
|
|
11
|
-
return `<${tag()}>${styles}</${tag()}>`;
|
|
12
|
-
};
|
|
3
|
+
<script>export let styles;
|
|
4
|
+
export let id;
|
|
13
5
|
</script>
|
|
14
6
|
|
|
15
|
-
{
|
|
16
|
-
{@html injectedStyleScript()}
|
|
17
|
-
{:else}
|
|
18
|
-
<svelte:element this={tag()}>{styles}</svelte:element>
|
|
19
|
-
{/if}
|
|
7
|
+
<svelte:element this={"sty" + "le"} {id}>{@html styles}</svelte:element>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "0.
|
|
1
|
+
export declare const SDK_VERSION = "0.4.0";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = "0.
|
|
1
|
+
export const SDK_VERSION = "0.4.0";
|
|
@@ -2,14 +2,14 @@ import { flatten } from '../../helpers/flatten.js';
|
|
|
2
2
|
import { getBuilderSearchParamsFromWindow, normalizeSearchParams, } from '../get-builder-search-params/index.js';
|
|
3
3
|
import { DEFAULT_API_VERSION } from '../../types/api-version';
|
|
4
4
|
export const generateContentUrl = (options) => {
|
|
5
|
-
const { limit = 30, userAttributes, query, noTraverse = false, model, apiKey, includeRefs = true, locale, apiVersion = DEFAULT_API_VERSION, } = options;
|
|
5
|
+
const { limit = 30, userAttributes, query, noTraverse = false, model, apiKey, includeRefs = true, enrich, locale, apiVersion = DEFAULT_API_VERSION, } = options;
|
|
6
6
|
if (!apiKey) {
|
|
7
7
|
throw new Error('Missing API key');
|
|
8
8
|
}
|
|
9
9
|
if (!['v2', 'v3'].includes(apiVersion)) {
|
|
10
10
|
throw new Error(`Invalid apiVersion: expected 'v2' or 'v3', received '${apiVersion}'`);
|
|
11
11
|
}
|
|
12
|
-
const url = new URL(`https://cdn.builder.io/api/${apiVersion}/content/${model}?apiKey=${apiKey}&limit=${limit}&noTraverse=${noTraverse}&includeRefs=${includeRefs}${locale ? `&locale=${locale}` : ''}`);
|
|
12
|
+
const url = new URL(`https://cdn.builder.io/api/${apiVersion}/content/${model}?apiKey=${apiKey}&limit=${limit}&noTraverse=${noTraverse}&includeRefs=${includeRefs}${locale ? `&locale=${locale}` : ''}${enrich ? `&enrich=${enrich}` : ''}`);
|
|
13
13
|
const queryOptions = {
|
|
14
14
|
...getBuilderSearchParamsFromWindow(),
|
|
15
15
|
...normalizeSearchParams(options.options || {}),
|
|
@@ -79,4 +79,19 @@ describe('Generate Content URL', () => {
|
|
|
79
79
|
});
|
|
80
80
|
}).toThrow(`Invalid apiVersion: expected 'v2' or 'v3', received 'INVALID_API_VERSION'`);
|
|
81
81
|
});
|
|
82
|
+
test('generate content url with enrich option true', () => {
|
|
83
|
+
const output = generateContentUrl({
|
|
84
|
+
apiKey: testKey,
|
|
85
|
+
model: testModel,
|
|
86
|
+
enrich: true,
|
|
87
|
+
});
|
|
88
|
+
expect(output).toMatchSnapshot();
|
|
89
|
+
});
|
|
90
|
+
test('generate content url with enrich option not present', () => {
|
|
91
|
+
const output = generateContentUrl({
|
|
92
|
+
apiKey: testKey,
|
|
93
|
+
model: testModel,
|
|
94
|
+
});
|
|
95
|
+
expect(output).toMatchSnapshot();
|
|
96
|
+
});
|
|
82
97
|
});
|