@builder.io/sdk-svelte 0.1.11 → 0.1.13
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 +2 -1
- package/package/CHANGELOG.md +9 -0
- package/package/blocks/columns/columns.svelte +72 -75
- package/package/blocks/columns/columns.svelte.d.ts +1 -1
- package/package/blocks/symbol/symbol.svelte +38 -32
- package/package/components/render-content/render-content.svelte +16 -5
- package/package/components/render-content/render-content.svelte.d.ts +1 -0
- package/package/components/render-content/render-content.types.d.ts +2 -0
- package/package/context/builder.context.d.ts +1 -0
- package/package/context/builder.context.js +1 -0
- package/package/context/types.d.ts +2 -0
- package/package/functions/get-content/generate-content-url.js +5 -2
- package/package/functions/get-content/generate-content-url.test.js +42 -0
- package/package/functions/get-content/types.d.ts +6 -0
- package/package/types/api-version.d.ts +1 -0
- package/package/types/api-version.js +1 -0
- package/package.json +1 -1
- package/src/blocks/columns/columns.svelte +75 -78
- package/src/blocks/symbol/symbol.svelte +42 -38
- package/src/components/render-content/render-content.svelte +16 -5
- package/src/components/render-content/render-content.types.ts +2 -0
- package/src/context/builder.context.ts +1 -0
- package/src/context/types.ts +2 -0
- package/src/functions/get-content/generate-content-url.test.ts +50 -0
- package/src/functions/get-content/generate-content-url.ts +8 -1
- package/src/functions/get-content/types.ts +7 -0
- package/src/types/api-version.ts +1 -0
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.1.
|
|
4
|
+
"version": "0.1.12",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"package",
|
|
@@ -139,6 +139,7 @@
|
|
|
139
139
|
"./index-helpers/blocks-exports": "./index-helpers/blocks-exports.js",
|
|
140
140
|
"./index-helpers/top-of-file": "./index-helpers/top-of-file.js",
|
|
141
141
|
"./scripts/init-editing": "./scripts/init-editing.js",
|
|
142
|
+
"./types/api-version": "./types/api-version.js",
|
|
142
143
|
"./types/builder-block": "./types/builder-block.js",
|
|
143
144
|
"./types/builder-content": "./types/builder-content.js",
|
|
144
145
|
"./types/can-track": "./types/can-track.js",
|
package/package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
### 0.1.12
|
|
2
|
+
|
|
3
|
+
- Fix: `RenderContent` not re-rendering when its `content` updates. This fixes Symbol rendering in the visual editor.
|
|
4
|
+
- Feature: `RenderContent` and `getContent` now have an `apiVersion` field that can be set to `v2` or `v3` to target specific Builder API versions. Current default is `v2`
|
|
5
|
+
|
|
6
|
+
### 0.1.11
|
|
7
|
+
|
|
8
|
+
- No changes.
|
|
9
|
+
|
|
1
10
|
### 0.1.10
|
|
2
11
|
|
|
3
12
|
- Fix: Text blocks styles (remove initial top padding)
|
|
@@ -5,7 +5,6 @@ import RenderBlocks from "../../components/render-blocks.svelte";
|
|
|
5
5
|
import { getSizesForBreakpoints } from "../../constants/device-sizes";
|
|
6
6
|
import RenderInlinedStyles from "../../components/render-inlined-styles.svelte";
|
|
7
7
|
import { TARGET } from "../../constants/target.js";
|
|
8
|
-
import { convertStyleMapToCSS } from "../../helpers/css";
|
|
9
8
|
import BuilderContext from "../../context/builder.context.js";
|
|
10
9
|
export let space;
|
|
11
10
|
export let columns;
|
|
@@ -23,112 +22,112 @@ function mitosis_styling(node, vars) {
|
|
|
23
22
|
});
|
|
24
23
|
}
|
|
25
24
|
let builderContext = getContext(BuilderContext.key);
|
|
26
|
-
function getGutterSize() {
|
|
27
|
-
return typeof space === "number" ? space || 0 : 20;
|
|
28
|
-
}
|
|
29
|
-
function getColumns() {
|
|
30
|
-
return columns || [];
|
|
31
|
-
}
|
|
32
25
|
function getWidth(index) {
|
|
33
|
-
|
|
34
|
-
return columns[index]?.width || 100 / columns.length;
|
|
26
|
+
return cols[index]?.width || 100 / cols.length;
|
|
35
27
|
}
|
|
36
28
|
function getColumnCssWidth(index) {
|
|
37
|
-
const
|
|
38
|
-
const gutterSize = getGutterSize();
|
|
39
|
-
const subtractWidth = (gutterSize * (columns.length - 1)) / columns.length;
|
|
29
|
+
const subtractWidth = (gutterSize * (cols.length - 1)) / cols.length;
|
|
40
30
|
return `calc(${getWidth(index)}% - ${subtractWidth}px)`;
|
|
41
31
|
}
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
function getTabletStyle({ stackedStyle, desktopStyle }) {
|
|
33
|
+
return stackAt === "tablet" ? stackedStyle : desktopStyle;
|
|
34
|
+
}
|
|
35
|
+
function getMobileStyle({ stackedStyle, desktopStyle }) {
|
|
36
|
+
return stackAt === "never" ? desktopStyle : stackedStyle;
|
|
37
|
+
}
|
|
38
|
+
function columnCssVars(index) {
|
|
39
|
+
const width = getColumnCssWidth(index);
|
|
40
|
+
const gutter = `${index === 0 ? 0 : gutterSize}px`;
|
|
41
|
+
if (TARGET === "reactNative") {
|
|
42
|
+
return {
|
|
43
|
+
width,
|
|
44
|
+
marginLeft: stackColumnsAt === "never" ? gutter : "0",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const mobileWidth = "100%";
|
|
48
|
+
const mobileMarginLeft = "0";
|
|
49
|
+
return {
|
|
50
|
+
width,
|
|
51
|
+
"margin-left": gutter,
|
|
52
|
+
"--column-width-mobile": getMobileStyle({
|
|
53
|
+
stackedStyle: mobileWidth,
|
|
54
|
+
desktopStyle: width,
|
|
55
|
+
}),
|
|
56
|
+
"--column-margin-left-mobile": getMobileStyle({
|
|
57
|
+
stackedStyle: mobileMarginLeft,
|
|
58
|
+
desktopStyle: gutter,
|
|
59
|
+
}),
|
|
60
|
+
"--column-width-tablet": getTabletStyle({
|
|
61
|
+
stackedStyle: mobileWidth,
|
|
62
|
+
desktopStyle: width,
|
|
63
|
+
}),
|
|
64
|
+
"--column-margin-left-tablet": getTabletStyle({
|
|
65
|
+
stackedStyle: mobileMarginLeft,
|
|
66
|
+
desktopStyle: gutter,
|
|
67
|
+
}),
|
|
68
|
+
};
|
|
45
69
|
}
|
|
46
70
|
function getWidthForBreakpointSize(size) {
|
|
47
71
|
const breakpointSizes = getSizesForBreakpoints(builderContext.content?.meta?.breakpoints || {});
|
|
48
72
|
return breakpointSizes[size].max;
|
|
49
73
|
}
|
|
50
74
|
$: columnsCssVars = () => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
75
|
+
if (TARGET === "reactNative") {
|
|
76
|
+
return {
|
|
77
|
+
flexDirection: flexDir,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
56
80
|
return {
|
|
57
81
|
"--flex-dir": flexDir,
|
|
58
|
-
"--flex-dir-tablet":
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const width = "100%";
|
|
63
|
-
const marginLeft = "0";
|
|
64
|
-
return {
|
|
65
|
-
"--column-width": width,
|
|
66
|
-
"--column-margin-left": marginLeft,
|
|
67
|
-
"--column-width-tablet": maybeApplyForTablet(width),
|
|
68
|
-
"--column-margin-left-tablet": maybeApplyForTablet(marginLeft),
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
$: columnStyleObjects = () => {
|
|
72
|
-
return {
|
|
73
|
-
columns: {
|
|
74
|
-
small: {
|
|
75
|
-
flexDirection: "var(--flex-dir)",
|
|
76
|
-
alignItems: "stretch",
|
|
77
|
-
},
|
|
78
|
-
medium: {
|
|
79
|
-
flexDirection: "var(--flex-dir-tablet)",
|
|
80
|
-
alignItems: "stretch",
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
column: {
|
|
84
|
-
small: {
|
|
85
|
-
width: "var(--column-width) !important",
|
|
86
|
-
marginLeft: "var(--column-margin-left) !important",
|
|
87
|
-
},
|
|
88
|
-
medium: {
|
|
89
|
-
width: "var(--column-width-tablet) !important",
|
|
90
|
-
marginLeft: "var(--column-margin-left-tablet) !important",
|
|
91
|
-
},
|
|
92
|
-
},
|
|
82
|
+
"--flex-dir-tablet": getTabletStyle({
|
|
83
|
+
stackedStyle: flexDir,
|
|
84
|
+
desktopStyle: "row",
|
|
85
|
+
}),
|
|
93
86
|
};
|
|
94
87
|
};
|
|
95
88
|
$: columnsStyles = () => {
|
|
96
89
|
return `
|
|
97
90
|
@media (max-width: ${getWidthForBreakpointSize("medium")}px) {
|
|
98
91
|
.${builderBlock.id}-breakpoints {
|
|
99
|
-
|
|
92
|
+
flex-direction: var(--flex-dir-tablet);
|
|
93
|
+
align-items: stretch;
|
|
100
94
|
}
|
|
101
95
|
|
|
102
96
|
.${builderBlock.id}-breakpoints > .builder-column {
|
|
103
|
-
|
|
97
|
+
width: var(--column-width-tablet) !important;
|
|
98
|
+
margin-left: var(--column-margin-left-tablet) !important;
|
|
104
99
|
}
|
|
105
100
|
}
|
|
106
101
|
|
|
107
102
|
@media (max-width: ${getWidthForBreakpointSize("small")}px) {
|
|
108
103
|
.${builderBlock.id}-breakpoints {
|
|
109
|
-
|
|
104
|
+
flex-direction: var(--flex-dir);
|
|
105
|
+
align-items: stretch;
|
|
110
106
|
}
|
|
111
107
|
|
|
112
108
|
.${builderBlock.id}-breakpoints > .builder-column {
|
|
113
|
-
|
|
109
|
+
width: var(--column-width-mobile) !important;
|
|
110
|
+
margin-left: var(--column-margin-left-mobile) !important;
|
|
114
111
|
}
|
|
115
112
|
},
|
|
116
113
|
`;
|
|
117
114
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
115
|
+
let gutterSize = typeof space === "number" ? space || 0 : 20;
|
|
116
|
+
let cols = columns || [];
|
|
117
|
+
let stackAt = stackColumnsAt || "tablet";
|
|
118
|
+
let flexDir = stackColumnsAt === "never"
|
|
119
|
+
? "row"
|
|
120
|
+
: reverseColumnsWhenStacked
|
|
121
|
+
? "column-reverse"
|
|
122
|
+
: "column";
|
|
124
123
|
</script>
|
|
125
124
|
|
|
126
125
|
<div
|
|
127
|
-
use:mitosis_styling={
|
|
128
|
-
...(TARGET === "reactNative" ? reactNativeColumnsStyles() : {}),
|
|
129
|
-
...columnsCssVars(),
|
|
130
|
-
}}
|
|
126
|
+
use:mitosis_styling={columnsCssVars()}
|
|
131
127
|
class={`builder-columns ${builderBlock.id}-breakpoints` + " div"}
|
|
128
|
+
dataSet={{
|
|
129
|
+
"builder-block-name": "builder-columns",
|
|
130
|
+
}}
|
|
132
131
|
>
|
|
133
132
|
{#if TARGET !== "reactNative"}
|
|
134
133
|
<RenderInlinedStyles styles={columnsStyles()} />
|
|
@@ -136,13 +135,11 @@ $: reactNativeColumnStyles = () => {
|
|
|
136
135
|
|
|
137
136
|
{#each columns as column, index (index)}
|
|
138
137
|
<div
|
|
139
|
-
use:mitosis_styling={
|
|
140
|
-
width: getColumnCssWidth(index),
|
|
141
|
-
marginLeft: `${index === 0 ? 0 : getGutterSize()}px`,
|
|
142
|
-
...(TARGET === "reactNative" ? reactNativeColumnStyles() : {}),
|
|
143
|
-
...columnCssVars(),
|
|
144
|
-
}}
|
|
138
|
+
use:mitosis_styling={columnCssVars(index)}
|
|
145
139
|
class="builder-column div-2"
|
|
140
|
+
dataSet={{
|
|
141
|
+
"builder-block-name": "builder-column",
|
|
142
|
+
}}
|
|
146
143
|
>
|
|
147
144
|
<RenderBlocks
|
|
148
145
|
blocks={column.blocks}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script context="module"></script>
|
|
2
2
|
|
|
3
|
-
<script>import { getContext } from "svelte";
|
|
3
|
+
<script>import { getContext, onMount } from "svelte";
|
|
4
4
|
import RenderContent from "../../components/render-content/render-content.svelte";
|
|
5
5
|
import BuilderContext from "../../context/builder.context.js";
|
|
6
6
|
import { getContent } from "../../functions/get-content/index.js";
|
|
@@ -36,24 +36,7 @@ export let attributes;
|
|
|
36
36
|
export let symbol;
|
|
37
37
|
export let dynamic;
|
|
38
38
|
let builderContext = getContext(BuilderContext.key);
|
|
39
|
-
|
|
40
|
-
return [
|
|
41
|
-
...(TARGET === "vue2" || TARGET === "vue3"
|
|
42
|
-
? Object.keys(attributes.class)
|
|
43
|
-
: [attributes.class]),
|
|
44
|
-
"builder-symbol",
|
|
45
|
-
symbol?.inline ? "builder-inline-symbol" : undefined,
|
|
46
|
-
symbol?.dynamic || dynamic ? "builder-dynamic-symbol" : undefined,
|
|
47
|
-
]
|
|
48
|
-
.filter(Boolean)
|
|
49
|
-
.join(" ");
|
|
50
|
-
};
|
|
51
|
-
$: contentToUse = () => {
|
|
52
|
-
return symbol?.content || fetchedContent;
|
|
53
|
-
};
|
|
54
|
-
let fetchedContent = null;
|
|
55
|
-
function onUpdateFn_0() {
|
|
56
|
-
const symbolToUse = symbol;
|
|
39
|
+
function fetchContent() {
|
|
57
40
|
/**
|
|
58
41
|
* If:
|
|
59
42
|
* - we have a symbol prop
|
|
@@ -63,44 +46,67 @@ function onUpdateFn_0() {
|
|
|
63
46
|
*
|
|
64
47
|
* then we want to re-fetch the symbol content.
|
|
65
48
|
*/
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
!fetchedContent &&
|
|
69
|
-
symbolToUse.model &&
|
|
49
|
+
if (!contentToUse &&
|
|
50
|
+
symbol?.model &&
|
|
70
51
|
// This is a hack, we should not need to check for this, but it is needed for Svelte.
|
|
71
52
|
builderContext?.apiKey) {
|
|
72
53
|
getContent({
|
|
73
|
-
model:
|
|
54
|
+
model: symbol.model,
|
|
74
55
|
apiKey: builderContext.apiKey,
|
|
56
|
+
apiVersion: builderContext.apiVersion,
|
|
75
57
|
query: {
|
|
76
|
-
id:
|
|
58
|
+
id: symbol.entry,
|
|
77
59
|
},
|
|
78
|
-
})
|
|
79
|
-
|
|
60
|
+
})
|
|
61
|
+
.then((response) => {
|
|
62
|
+
if (response) {
|
|
63
|
+
contentToUse = response;
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
.catch((err) => {
|
|
67
|
+
console.error("[Builder.io]: Could not fetch symbol content: ", err);
|
|
80
68
|
});
|
|
81
69
|
}
|
|
82
70
|
}
|
|
83
|
-
|
|
71
|
+
let className = [
|
|
72
|
+
...(TARGET === "vue2" || TARGET === "vue3"
|
|
73
|
+
? Object.keys(attributes.class)
|
|
74
|
+
: [attributes.class]),
|
|
75
|
+
"builder-symbol",
|
|
76
|
+
symbol?.inline ? "builder-inline-symbol" : undefined,
|
|
77
|
+
symbol?.dynamic || dynamic ? "builder-dynamic-symbol" : undefined,
|
|
78
|
+
]
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.join(" ");
|
|
81
|
+
let contentToUse = symbol?.content;
|
|
82
|
+
onMount(() => {
|
|
83
|
+
fetchContent();
|
|
84
|
+
});
|
|
85
|
+
function onUpdateFn_0() {
|
|
86
|
+
fetchContent();
|
|
87
|
+
}
|
|
88
|
+
$: onUpdateFn_0(...[symbol]);
|
|
84
89
|
</script>
|
|
85
90
|
|
|
86
91
|
<div
|
|
87
92
|
{...filterAttrs(attributes, isNonEvent)}
|
|
88
93
|
dataSet={{
|
|
89
|
-
class: className
|
|
94
|
+
class: className,
|
|
90
95
|
}}
|
|
91
|
-
class={className
|
|
96
|
+
class={className}
|
|
92
97
|
use:setAttrs={filterAttrs(attributes, isEvent)}
|
|
93
98
|
>
|
|
94
99
|
<RenderContent
|
|
100
|
+
apiVersion={builderContext.apiVersion}
|
|
95
101
|
apiKey={builderContext.apiKey}
|
|
96
102
|
context={builderContext.context}
|
|
97
103
|
customComponents={Object.values(builderContext.registeredComponents)}
|
|
98
104
|
data={{
|
|
99
105
|
...symbol?.data,
|
|
100
106
|
...builderContext.state,
|
|
101
|
-
...
|
|
107
|
+
...contentToUse?.data?.state,
|
|
102
108
|
}}
|
|
103
109
|
model={symbol?.model}
|
|
104
|
-
content={contentToUse
|
|
110
|
+
content={contentToUse}
|
|
105
111
|
/>
|
|
106
112
|
</div>
|
|
@@ -27,6 +27,7 @@ export let model;
|
|
|
27
27
|
export let context;
|
|
28
28
|
export let apiKey;
|
|
29
29
|
export let includeRefs;
|
|
30
|
+
export let apiVersion;
|
|
30
31
|
function mergeNewContent(newContent) {
|
|
31
32
|
useContent = {
|
|
32
33
|
...useContent,
|
|
@@ -251,6 +252,7 @@ onMount(() => {
|
|
|
251
252
|
getContent({
|
|
252
253
|
model: model,
|
|
253
254
|
apiKey: apiKey,
|
|
255
|
+
apiVersion: apiVersion,
|
|
254
256
|
}).then((content) => {
|
|
255
257
|
if (content) {
|
|
256
258
|
mergeNewContent(content);
|
|
@@ -264,17 +266,23 @@ onMount(() => {
|
|
|
264
266
|
}
|
|
265
267
|
});
|
|
266
268
|
function onUpdateFn_0() {
|
|
267
|
-
|
|
269
|
+
if (content) {
|
|
270
|
+
mergeNewContent(content);
|
|
271
|
+
}
|
|
268
272
|
}
|
|
269
|
-
$: onUpdateFn_0(...[
|
|
273
|
+
$: onUpdateFn_0(...[content]);
|
|
270
274
|
function onUpdateFn_1() {
|
|
271
|
-
|
|
275
|
+
evaluateJsCode();
|
|
272
276
|
}
|
|
273
|
-
$: onUpdateFn_1(...[useContent?.data?.
|
|
277
|
+
$: onUpdateFn_1(...[useContent?.data?.jsCode, contentState]);
|
|
274
278
|
function onUpdateFn_2() {
|
|
279
|
+
runHttpRequests();
|
|
280
|
+
}
|
|
281
|
+
$: onUpdateFn_2(...[useContent?.data?.httpRequests]);
|
|
282
|
+
function onUpdateFn_3() {
|
|
275
283
|
emitStateUpdate();
|
|
276
284
|
}
|
|
277
|
-
$:
|
|
285
|
+
$: onUpdateFn_3(...[contentState]);
|
|
278
286
|
setContext(builderContext.key, {
|
|
279
287
|
get content() {
|
|
280
288
|
return useContent;
|
|
@@ -291,6 +299,9 @@ setContext(builderContext.key, {
|
|
|
291
299
|
get apiKey() {
|
|
292
300
|
return apiKey;
|
|
293
301
|
},
|
|
302
|
+
get apiVersion() {
|
|
303
|
+
return apiVersion;
|
|
304
|
+
},
|
|
294
305
|
get registeredComponents() {
|
|
295
306
|
return allRegisteredComponents;
|
|
296
307
|
},
|
|
@@ -11,6 +11,7 @@ declare const __propDef: {
|
|
|
11
11
|
context: RenderContentProps["context"];
|
|
12
12
|
apiKey: RenderContentProps["apiKey"];
|
|
13
13
|
includeRefs: RenderContentProps["includeRefs"];
|
|
14
|
+
apiVersion: RenderContentProps["apiVersion"];
|
|
14
15
|
};
|
|
15
16
|
events: {
|
|
16
17
|
[evt: string]: CustomEvent<any>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BuilderRenderContext, RegisteredComponent, BuilderRenderState } from '../../context/types';
|
|
2
2
|
import type { BuilderContent } from '../../types/builder-content';
|
|
3
3
|
import type { Nullable } from '../../types/typescript';
|
|
4
|
+
import type { ApiVersion } from '../../types/api-version';
|
|
4
5
|
export type RenderContentProps = {
|
|
5
6
|
content?: Nullable<BuilderContent>;
|
|
6
7
|
model?: string;
|
|
@@ -9,6 +10,7 @@ export type RenderContentProps = {
|
|
|
9
10
|
};
|
|
10
11
|
context?: BuilderRenderContext;
|
|
11
12
|
apiKey: string;
|
|
13
|
+
apiVersion?: ApiVersion;
|
|
12
14
|
customComponents?: RegisteredComponent[];
|
|
13
15
|
canTrack?: boolean;
|
|
14
16
|
locale?: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BuilderContent } from '../types/builder-content.js';
|
|
2
2
|
import type { ComponentInfo } from '../types/components.js';
|
|
3
3
|
import type { Dictionary, Nullable } from '../types/typescript.js';
|
|
4
|
+
import type { ApiVersion } from '../types/api-version.js';
|
|
4
5
|
export type RegisteredComponent = ComponentInfo & {
|
|
5
6
|
component: any;
|
|
6
7
|
};
|
|
@@ -13,6 +14,7 @@ export interface BuilderContextInterface {
|
|
|
13
14
|
state: BuilderRenderState;
|
|
14
15
|
setState?: (state: BuilderRenderState) => void;
|
|
15
16
|
apiKey: string | null;
|
|
17
|
+
apiVersion?: ApiVersion;
|
|
16
18
|
registeredComponents: RegisteredComponents;
|
|
17
19
|
inheritedStyles: Record<string, unknown>;
|
|
18
20
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { flatten } from '../../helpers/flatten.js';
|
|
2
2
|
import { getBuilderSearchParamsFromWindow, normalizeSearchParams, } from '../get-builder-search-params/index.js';
|
|
3
3
|
export const generateContentUrl = (options) => {
|
|
4
|
-
const { limit = 30, userAttributes, query, noTraverse = false, model, apiKey, includeRefs = true, locale, } = options;
|
|
4
|
+
const { limit = 30, userAttributes, query, noTraverse = false, model, apiKey, includeRefs = true, locale, apiVersion = 'v2', } = options;
|
|
5
5
|
if (!apiKey) {
|
|
6
6
|
throw new Error('Missing API key');
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
if (!['v2', 'v3'].includes(apiVersion)) {
|
|
9
|
+
throw new Error(`Invalid apiVersion: expected 'v2' or 'v3', received '${apiVersion}'`);
|
|
10
|
+
}
|
|
11
|
+
const url = new URL(`https://cdn.builder.io/api/${apiVersion}/content/${model}?apiKey=${apiKey}&limit=${limit}&noTraverse=${noTraverse}&includeRefs=${includeRefs}${locale ? `&locale=${locale}` : ''}`);
|
|
9
12
|
const queryOptions = {
|
|
10
13
|
...getBuilderSearchParamsFromWindow(),
|
|
11
14
|
...normalizeSearchParams(options.options || {}),
|
|
@@ -28,4 +28,46 @@ describe('Generate Content URL', () => {
|
|
|
28
28
|
});
|
|
29
29
|
expect(output).toMatchSnapshot();
|
|
30
30
|
});
|
|
31
|
+
test('generate content url with apiVersion as v2', () => {
|
|
32
|
+
const output = generateContentUrl({
|
|
33
|
+
apiKey: testKey,
|
|
34
|
+
model: testModel,
|
|
35
|
+
query: { id: testId },
|
|
36
|
+
options,
|
|
37
|
+
apiVersion: 'v2',
|
|
38
|
+
});
|
|
39
|
+
expect(output).toMatchSnapshot();
|
|
40
|
+
});
|
|
41
|
+
test('generate content url with apiVersion as v3', () => {
|
|
42
|
+
const output = generateContentUrl({
|
|
43
|
+
apiKey: testKey,
|
|
44
|
+
model: testModel,
|
|
45
|
+
query: { id: testId },
|
|
46
|
+
options,
|
|
47
|
+
apiVersion: 'v3',
|
|
48
|
+
});
|
|
49
|
+
expect(output).toMatchSnapshot();
|
|
50
|
+
});
|
|
51
|
+
test('throw error when trying to generate content url with apiVersion as v1', () => {
|
|
52
|
+
expect(() => {
|
|
53
|
+
generateContentUrl({
|
|
54
|
+
apiKey: testKey,
|
|
55
|
+
model: testModel,
|
|
56
|
+
query: { id: testId },
|
|
57
|
+
options,
|
|
58
|
+
apiVersion: 'v1',
|
|
59
|
+
});
|
|
60
|
+
}).toThrow(`Invalid apiVersion: expected 'v2' or 'v3', received 'v1'`);
|
|
61
|
+
});
|
|
62
|
+
test('throw error when trying to generate content url with an invalid apiVersion value', () => {
|
|
63
|
+
expect(() => {
|
|
64
|
+
generateContentUrl({
|
|
65
|
+
apiKey: testKey,
|
|
66
|
+
model: testModel,
|
|
67
|
+
query: { id: testId },
|
|
68
|
+
options,
|
|
69
|
+
apiVersion: 'INVALID_API_VERSION',
|
|
70
|
+
});
|
|
71
|
+
}).toThrow(`Invalid apiVersion: expected 'v2' or 'v3', received 'INVALID_API_VERSION'`);
|
|
72
|
+
});
|
|
31
73
|
});
|
|
@@ -36,4 +36,10 @@ export interface GetContentOptions {
|
|
|
36
36
|
* If provided, the API will auto-resolve localized objects to the value of this `locale` key.
|
|
37
37
|
*/
|
|
38
38
|
locale?: string;
|
|
39
|
+
/**
|
|
40
|
+
* If provided, sets the Builder API version used to fetch content.
|
|
41
|
+
*
|
|
42
|
+
* Defaults to `v2`.
|
|
43
|
+
*/
|
|
44
|
+
apiVersion?: 'v2' | 'v3';
|
|
39
45
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type ApiVersion = 'v2' | 'v3';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
import type { SizeName } from "../../constants/device-sizes";
|
|
5
5
|
|
|
6
6
|
type Column = {
|
|
7
|
-
blocks:
|
|
8
|
-
// TODO: Implement this when support for dynamic CSS lands
|
|
7
|
+
blocks: BuilderBlock[];
|
|
9
8
|
width?: number;
|
|
10
9
|
};
|
|
11
10
|
|
|
@@ -31,7 +30,6 @@
|
|
|
31
30
|
import { getSizesForBreakpoints } from "../../constants/device-sizes";
|
|
32
31
|
import RenderInlinedStyles from "../../components/render-inlined-styles.svelte";
|
|
33
32
|
import { TARGET } from "../../constants/target.js";
|
|
34
|
-
import { convertStyleMapToCSS } from "../../helpers/css";
|
|
35
33
|
import BuilderContext from "../../context/builder.context.js";
|
|
36
34
|
|
|
37
35
|
export let space: ColumnProps["space"];
|
|
@@ -52,25 +50,50 @@
|
|
|
52
50
|
|
|
53
51
|
let builderContext = getContext(BuilderContext.key);
|
|
54
52
|
|
|
55
|
-
function getGutterSize() {
|
|
56
|
-
return typeof space === "number" ? space || 0 : 20;
|
|
57
|
-
}
|
|
58
|
-
function getColumns() {
|
|
59
|
-
return columns || [];
|
|
60
|
-
}
|
|
61
53
|
function getWidth(index) {
|
|
62
|
-
|
|
63
|
-
return columns[index]?.width || 100 / columns.length;
|
|
54
|
+
return cols[index]?.width || 100 / cols.length;
|
|
64
55
|
}
|
|
65
56
|
function getColumnCssWidth(index) {
|
|
66
|
-
const
|
|
67
|
-
const gutterSize = getGutterSize();
|
|
68
|
-
const subtractWidth = (gutterSize * (columns.length - 1)) / columns.length;
|
|
57
|
+
const subtractWidth = (gutterSize * (cols.length - 1)) / cols.length;
|
|
69
58
|
return `calc(${getWidth(index)}% - ${subtractWidth}px)`;
|
|
70
59
|
}
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
|
|
60
|
+
function getTabletStyle({ stackedStyle, desktopStyle }) {
|
|
61
|
+
return stackAt === "tablet" ? stackedStyle : desktopStyle;
|
|
62
|
+
}
|
|
63
|
+
function getMobileStyle({ stackedStyle, desktopStyle }) {
|
|
64
|
+
return stackAt === "never" ? desktopStyle : stackedStyle;
|
|
65
|
+
}
|
|
66
|
+
function columnCssVars(index) {
|
|
67
|
+
const width = getColumnCssWidth(index);
|
|
68
|
+
const gutter = `${index === 0 ? 0 : gutterSize}px`;
|
|
69
|
+
if (TARGET === "reactNative") {
|
|
70
|
+
return {
|
|
71
|
+
width,
|
|
72
|
+
marginLeft: stackColumnsAt === "never" ? gutter : "0",
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const mobileWidth = "100%";
|
|
76
|
+
const mobileMarginLeft = "0";
|
|
77
|
+
return {
|
|
78
|
+
width,
|
|
79
|
+
"margin-left": gutter,
|
|
80
|
+
"--column-width-mobile": getMobileStyle({
|
|
81
|
+
stackedStyle: mobileWidth,
|
|
82
|
+
desktopStyle: width,
|
|
83
|
+
}),
|
|
84
|
+
"--column-margin-left-mobile": getMobileStyle({
|
|
85
|
+
stackedStyle: mobileMarginLeft,
|
|
86
|
+
desktopStyle: gutter,
|
|
87
|
+
}),
|
|
88
|
+
"--column-width-tablet": getTabletStyle({
|
|
89
|
+
stackedStyle: mobileWidth,
|
|
90
|
+
desktopStyle: width,
|
|
91
|
+
}),
|
|
92
|
+
"--column-margin-left-tablet": getTabletStyle({
|
|
93
|
+
stackedStyle: mobileMarginLeft,
|
|
94
|
+
desktopStyle: gutter,
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
74
97
|
}
|
|
75
98
|
function getWidthForBreakpointSize(size) {
|
|
76
99
|
const breakpointSizes = getSizesForBreakpoints(
|
|
@@ -79,88 +102,64 @@
|
|
|
79
102
|
return breakpointSizes[size].max;
|
|
80
103
|
}
|
|
81
104
|
$: columnsCssVars = () => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
: "column";
|
|
105
|
+
if (TARGET === "reactNative") {
|
|
106
|
+
return {
|
|
107
|
+
flexDirection: flexDir,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
88
110
|
return {
|
|
89
111
|
"--flex-dir": flexDir,
|
|
90
|
-
"--flex-dir-tablet":
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const width = "100%";
|
|
95
|
-
const marginLeft = "0";
|
|
96
|
-
return {
|
|
97
|
-
"--column-width": width,
|
|
98
|
-
"--column-margin-left": marginLeft,
|
|
99
|
-
"--column-width-tablet": maybeApplyForTablet(width),
|
|
100
|
-
"--column-margin-left-tablet": maybeApplyForTablet(marginLeft),
|
|
101
|
-
};
|
|
102
|
-
};
|
|
103
|
-
$: columnStyleObjects = () => {
|
|
104
|
-
return {
|
|
105
|
-
columns: {
|
|
106
|
-
small: {
|
|
107
|
-
flexDirection: "var(--flex-dir)",
|
|
108
|
-
alignItems: "stretch",
|
|
109
|
-
},
|
|
110
|
-
medium: {
|
|
111
|
-
flexDirection: "var(--flex-dir-tablet)",
|
|
112
|
-
alignItems: "stretch",
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
column: {
|
|
116
|
-
small: {
|
|
117
|
-
width: "var(--column-width) !important",
|
|
118
|
-
marginLeft: "var(--column-margin-left) !important",
|
|
119
|
-
},
|
|
120
|
-
medium: {
|
|
121
|
-
width: "var(--column-width-tablet) !important",
|
|
122
|
-
marginLeft: "var(--column-margin-left-tablet) !important",
|
|
123
|
-
},
|
|
124
|
-
},
|
|
112
|
+
"--flex-dir-tablet": getTabletStyle({
|
|
113
|
+
stackedStyle: flexDir,
|
|
114
|
+
desktopStyle: "row",
|
|
115
|
+
}),
|
|
125
116
|
};
|
|
126
117
|
};
|
|
127
118
|
$: columnsStyles = () => {
|
|
128
119
|
return `
|
|
129
120
|
@media (max-width: ${getWidthForBreakpointSize("medium")}px) {
|
|
130
121
|
.${builderBlock.id}-breakpoints {
|
|
131
|
-
|
|
122
|
+
flex-direction: var(--flex-dir-tablet);
|
|
123
|
+
align-items: stretch;
|
|
132
124
|
}
|
|
133
125
|
|
|
134
126
|
.${builderBlock.id}-breakpoints > .builder-column {
|
|
135
|
-
|
|
127
|
+
width: var(--column-width-tablet) !important;
|
|
128
|
+
margin-left: var(--column-margin-left-tablet) !important;
|
|
136
129
|
}
|
|
137
130
|
}
|
|
138
131
|
|
|
139
132
|
@media (max-width: ${getWidthForBreakpointSize("small")}px) {
|
|
140
133
|
.${builderBlock.id}-breakpoints {
|
|
141
|
-
|
|
134
|
+
flex-direction: var(--flex-dir);
|
|
135
|
+
align-items: stretch;
|
|
142
136
|
}
|
|
143
137
|
|
|
144
138
|
.${builderBlock.id}-breakpoints > .builder-column {
|
|
145
|
-
|
|
139
|
+
width: var(--column-width-mobile) !important;
|
|
140
|
+
margin-left: var(--column-margin-left-mobile) !important;
|
|
146
141
|
}
|
|
147
142
|
},
|
|
148
143
|
`;
|
|
149
144
|
};
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
145
|
+
|
|
146
|
+
let gutterSize = typeof space === "number" ? space || 0 : 20;
|
|
147
|
+
let cols = columns || [];
|
|
148
|
+
let stackAt = stackColumnsAt || "tablet";
|
|
149
|
+
let flexDir =
|
|
150
|
+
stackColumnsAt === "never"
|
|
151
|
+
? "row"
|
|
152
|
+
: reverseColumnsWhenStacked
|
|
153
|
+
? "column-reverse"
|
|
154
|
+
: "column";
|
|
156
155
|
</script>
|
|
157
156
|
|
|
158
157
|
<div
|
|
159
|
-
use:mitosis_styling={
|
|
160
|
-
...(TARGET === "reactNative" ? reactNativeColumnsStyles() : {}),
|
|
161
|
-
...columnsCssVars(),
|
|
162
|
-
}}
|
|
158
|
+
use:mitosis_styling={columnsCssVars()}
|
|
163
159
|
class={`builder-columns ${builderBlock.id}-breakpoints` + " div"}
|
|
160
|
+
dataSet={{
|
|
161
|
+
"builder-block-name": "builder-columns",
|
|
162
|
+
}}
|
|
164
163
|
>
|
|
165
164
|
{#if TARGET !== "reactNative"}
|
|
166
165
|
<RenderInlinedStyles styles={columnsStyles()} />
|
|
@@ -168,13 +167,11 @@
|
|
|
168
167
|
|
|
169
168
|
{#each columns as column, index (index)}
|
|
170
169
|
<div
|
|
171
|
-
use:mitosis_styling={
|
|
172
|
-
width: getColumnCssWidth(index),
|
|
173
|
-
marginLeft: `${index === 0 ? 0 : getGutterSize()}px`,
|
|
174
|
-
...(TARGET === "reactNative" ? reactNativeColumnStyles() : {}),
|
|
175
|
-
...columnCssVars(),
|
|
176
|
-
}}
|
|
170
|
+
use:mitosis_styling={columnCssVars(index)}
|
|
177
171
|
class="builder-column div-2"
|
|
172
|
+
dataSet={{
|
|
173
|
+
"builder-block-name": "builder-column",
|
|
174
|
+
}}
|
|
178
175
|
>
|
|
179
176
|
<RenderBlocks
|
|
180
177
|
blocks={column.blocks}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
<script context="module" lang="ts">
|
|
2
2
|
import type { BuilderContent } from "../../types/builder-content.js";
|
|
3
3
|
|
|
4
|
-
import type { Nullable } from "../../types/typescript.js";
|
|
5
|
-
|
|
6
4
|
import type { BuilderBlock } from "../../types/builder-block.js";
|
|
7
5
|
|
|
8
6
|
export interface SymbolInfo {
|
|
@@ -25,7 +23,7 @@
|
|
|
25
23
|
</script>
|
|
26
24
|
|
|
27
25
|
<script lang="ts">
|
|
28
|
-
import { getContext } from "svelte";
|
|
26
|
+
import { getContext, onMount } from "svelte";
|
|
29
27
|
|
|
30
28
|
import RenderContent from "../../components/render-content/render-content.svelte";
|
|
31
29
|
import BuilderContext from "../../context/builder.context.js";
|
|
@@ -67,27 +65,7 @@
|
|
|
67
65
|
|
|
68
66
|
let builderContext = getContext(BuilderContext.key);
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
return [
|
|
72
|
-
...(TARGET === "vue2" || TARGET === "vue3"
|
|
73
|
-
? Object.keys(attributes.class)
|
|
74
|
-
: [attributes.class]),
|
|
75
|
-
"builder-symbol",
|
|
76
|
-
symbol?.inline ? "builder-inline-symbol" : undefined,
|
|
77
|
-
symbol?.dynamic || dynamic ? "builder-dynamic-symbol" : undefined,
|
|
78
|
-
]
|
|
79
|
-
.filter(Boolean)
|
|
80
|
-
.join(" ");
|
|
81
|
-
};
|
|
82
|
-
$: contentToUse = () => {
|
|
83
|
-
return symbol?.content || fetchedContent;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
let fetchedContent = null;
|
|
87
|
-
|
|
88
|
-
function onUpdateFn_0() {
|
|
89
|
-
const symbolToUse = symbol;
|
|
90
|
-
|
|
68
|
+
function fetchContent() {
|
|
91
69
|
/**
|
|
92
70
|
* If:
|
|
93
71
|
* - we have a symbol prop
|
|
@@ -98,45 +76,71 @@
|
|
|
98
76
|
* then we want to re-fetch the symbol content.
|
|
99
77
|
*/
|
|
100
78
|
if (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
!fetchedContent &&
|
|
104
|
-
symbolToUse.model &&
|
|
79
|
+
!contentToUse &&
|
|
80
|
+
symbol?.model &&
|
|
105
81
|
// This is a hack, we should not need to check for this, but it is needed for Svelte.
|
|
106
82
|
builderContext?.apiKey
|
|
107
83
|
) {
|
|
108
84
|
getContent({
|
|
109
|
-
model:
|
|
85
|
+
model: symbol.model,
|
|
110
86
|
apiKey: builderContext.apiKey,
|
|
87
|
+
apiVersion: builderContext.apiVersion,
|
|
111
88
|
query: {
|
|
112
|
-
id:
|
|
89
|
+
id: symbol.entry,
|
|
113
90
|
},
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
|
|
91
|
+
})
|
|
92
|
+
.then((response) => {
|
|
93
|
+
if (response) {
|
|
94
|
+
contentToUse = response;
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
.catch((err) => {
|
|
98
|
+
console.error("[Builder.io]: Could not fetch symbol content: ", err);
|
|
99
|
+
});
|
|
117
100
|
}
|
|
118
101
|
}
|
|
119
|
-
|
|
102
|
+
|
|
103
|
+
let className = [
|
|
104
|
+
...(TARGET === "vue2" || TARGET === "vue3"
|
|
105
|
+
? Object.keys(attributes.class)
|
|
106
|
+
: [attributes.class]),
|
|
107
|
+
"builder-symbol",
|
|
108
|
+
symbol?.inline ? "builder-inline-symbol" : undefined,
|
|
109
|
+
symbol?.dynamic || dynamic ? "builder-dynamic-symbol" : undefined,
|
|
110
|
+
]
|
|
111
|
+
.filter(Boolean)
|
|
112
|
+
.join(" ");
|
|
113
|
+
let contentToUse = symbol?.content;
|
|
114
|
+
|
|
115
|
+
onMount(() => {
|
|
116
|
+
fetchContent();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
function onUpdateFn_0() {
|
|
120
|
+
fetchContent();
|
|
121
|
+
}
|
|
122
|
+
$: onUpdateFn_0(...[symbol]);
|
|
120
123
|
</script>
|
|
121
124
|
|
|
122
125
|
<div
|
|
123
126
|
{...filterAttrs(attributes, isNonEvent)}
|
|
124
127
|
dataSet={{
|
|
125
|
-
class: className
|
|
128
|
+
class: className,
|
|
126
129
|
}}
|
|
127
|
-
class={className
|
|
130
|
+
class={className}
|
|
128
131
|
use:setAttrs={filterAttrs(attributes, isEvent)}
|
|
129
132
|
>
|
|
130
133
|
<RenderContent
|
|
134
|
+
apiVersion={builderContext.apiVersion}
|
|
131
135
|
apiKey={builderContext.apiKey}
|
|
132
136
|
context={builderContext.context}
|
|
133
137
|
customComponents={Object.values(builderContext.registeredComponents)}
|
|
134
138
|
data={{
|
|
135
139
|
...symbol?.data,
|
|
136
140
|
...builderContext.state,
|
|
137
|
-
...
|
|
141
|
+
...contentToUse?.data?.state,
|
|
138
142
|
}}
|
|
139
143
|
model={symbol?.model}
|
|
140
|
-
content={contentToUse
|
|
144
|
+
content={contentToUse}
|
|
141
145
|
/>
|
|
142
146
|
</div>
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
export let context: RenderContentProps["context"];
|
|
58
58
|
export let apiKey: RenderContentProps["apiKey"];
|
|
59
59
|
export let includeRefs: RenderContentProps["includeRefs"];
|
|
60
|
+
export let apiVersion: RenderContentProps["apiVersion"];
|
|
60
61
|
|
|
61
62
|
function mergeNewContent(newContent) {
|
|
62
63
|
useContent = {
|
|
@@ -305,6 +306,7 @@
|
|
|
305
306
|
getContent({
|
|
306
307
|
model: model,
|
|
307
308
|
apiKey: apiKey,
|
|
309
|
+
apiVersion: apiVersion,
|
|
308
310
|
}).then((content) => {
|
|
309
311
|
if (content) {
|
|
310
312
|
mergeNewContent(content);
|
|
@@ -319,17 +321,23 @@
|
|
|
319
321
|
});
|
|
320
322
|
|
|
321
323
|
function onUpdateFn_0() {
|
|
322
|
-
|
|
324
|
+
if (content) {
|
|
325
|
+
mergeNewContent(content);
|
|
326
|
+
}
|
|
323
327
|
}
|
|
324
|
-
$: onUpdateFn_0(...[
|
|
328
|
+
$: onUpdateFn_0(...[content]);
|
|
325
329
|
function onUpdateFn_1() {
|
|
326
|
-
|
|
330
|
+
evaluateJsCode();
|
|
327
331
|
}
|
|
328
|
-
$: onUpdateFn_1(...[useContent?.data?.
|
|
332
|
+
$: onUpdateFn_1(...[useContent?.data?.jsCode, contentState]);
|
|
329
333
|
function onUpdateFn_2() {
|
|
334
|
+
runHttpRequests();
|
|
335
|
+
}
|
|
336
|
+
$: onUpdateFn_2(...[useContent?.data?.httpRequests]);
|
|
337
|
+
function onUpdateFn_3() {
|
|
330
338
|
emitStateUpdate();
|
|
331
339
|
}
|
|
332
|
-
$:
|
|
340
|
+
$: onUpdateFn_3(...[contentState]);
|
|
333
341
|
|
|
334
342
|
setContext(builderContext.key, {
|
|
335
343
|
get content() {
|
|
@@ -347,6 +355,9 @@
|
|
|
347
355
|
get apiKey() {
|
|
348
356
|
return apiKey;
|
|
349
357
|
},
|
|
358
|
+
get apiVersion() {
|
|
359
|
+
return apiVersion;
|
|
360
|
+
},
|
|
350
361
|
get registeredComponents() {
|
|
351
362
|
return allRegisteredComponents;
|
|
352
363
|
},
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
} from '../../context/types';
|
|
6
6
|
import type { BuilderContent } from '../../types/builder-content';
|
|
7
7
|
import type { Nullable } from '../../types/typescript';
|
|
8
|
+
import type { ApiVersion } from '../../types/api-version';
|
|
8
9
|
|
|
9
10
|
export type RenderContentProps = {
|
|
10
11
|
content?: Nullable<BuilderContent>;
|
|
@@ -12,6 +13,7 @@ export type RenderContentProps = {
|
|
|
12
13
|
data?: { [key: string]: any };
|
|
13
14
|
context?: BuilderRenderContext;
|
|
14
15
|
apiKey: string;
|
|
16
|
+
apiVersion?: ApiVersion;
|
|
15
17
|
customComponents?: RegisteredComponent[];
|
|
16
18
|
canTrack?: boolean;
|
|
17
19
|
locale?: string;
|
package/src/context/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BuilderContent } from '../types/builder-content.js';
|
|
2
2
|
import type { ComponentInfo } from '../types/components.js';
|
|
3
3
|
import type { Dictionary, Nullable } from '../types/typescript.js';
|
|
4
|
+
import type { ApiVersion } from '../types/api-version.js';
|
|
4
5
|
|
|
5
6
|
export type RegisteredComponent = ComponentInfo & {
|
|
6
7
|
component: any;
|
|
@@ -18,6 +19,7 @@ export interface BuilderContextInterface {
|
|
|
18
19
|
state: BuilderRenderState;
|
|
19
20
|
setState?: (state: BuilderRenderState) => void;
|
|
20
21
|
apiKey: string | null;
|
|
22
|
+
apiVersion?: ApiVersion;
|
|
21
23
|
registeredComponents: RegisteredComponents;
|
|
22
24
|
// Used to recursively store all CSS coming from a parent that would apply to a Text block
|
|
23
25
|
inheritedStyles: Record<string, unknown>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { generateContentUrl } from './generate-content-url';
|
|
2
|
+
import type { GetContentOptions } from './types';
|
|
2
3
|
|
|
3
4
|
const testKey = 'YJIGb4i01jvw0SRdL5Bt';
|
|
4
5
|
const testModel = 'page';
|
|
@@ -23,6 +24,7 @@ describe('Generate Content URL', () => {
|
|
|
23
24
|
});
|
|
24
25
|
expect(output).toMatchSnapshot();
|
|
25
26
|
});
|
|
27
|
+
|
|
26
28
|
test('Handles overrides correctly', () => {
|
|
27
29
|
const output = generateContentUrl({
|
|
28
30
|
apiKey: testKey,
|
|
@@ -32,4 +34,52 @@ describe('Generate Content URL', () => {
|
|
|
32
34
|
});
|
|
33
35
|
expect(output).toMatchSnapshot();
|
|
34
36
|
});
|
|
37
|
+
|
|
38
|
+
test('generate content url with apiVersion as v2', () => {
|
|
39
|
+
const output = generateContentUrl({
|
|
40
|
+
apiKey: testKey,
|
|
41
|
+
model: testModel,
|
|
42
|
+
query: { id: testId },
|
|
43
|
+
options,
|
|
44
|
+
apiVersion: 'v2',
|
|
45
|
+
});
|
|
46
|
+
expect(output).toMatchSnapshot();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('generate content url with apiVersion as v3', () => {
|
|
50
|
+
const output = generateContentUrl({
|
|
51
|
+
apiKey: testKey,
|
|
52
|
+
model: testModel,
|
|
53
|
+
query: { id: testId },
|
|
54
|
+
options,
|
|
55
|
+
apiVersion: 'v3',
|
|
56
|
+
});
|
|
57
|
+
expect(output).toMatchSnapshot();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('throw error when trying to generate content url with apiVersion as v1', () => {
|
|
61
|
+
expect(() => {
|
|
62
|
+
generateContentUrl({
|
|
63
|
+
apiKey: testKey,
|
|
64
|
+
model: testModel,
|
|
65
|
+
query: { id: testId },
|
|
66
|
+
options,
|
|
67
|
+
apiVersion: 'v1' as GetContentOptions['apiVersion'],
|
|
68
|
+
});
|
|
69
|
+
}).toThrow(`Invalid apiVersion: expected 'v2' or 'v3', received 'v1'`);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('throw error when trying to generate content url with an invalid apiVersion value', () => {
|
|
73
|
+
expect(() => {
|
|
74
|
+
generateContentUrl({
|
|
75
|
+
apiKey: testKey,
|
|
76
|
+
model: testModel,
|
|
77
|
+
query: { id: testId },
|
|
78
|
+
options,
|
|
79
|
+
apiVersion: 'INVALID_API_VERSION' as GetContentOptions['apiVersion'],
|
|
80
|
+
});
|
|
81
|
+
}).toThrow(
|
|
82
|
+
`Invalid apiVersion: expected 'v2' or 'v3', received 'INVALID_API_VERSION'`
|
|
83
|
+
);
|
|
84
|
+
});
|
|
35
85
|
});
|
|
@@ -15,14 +15,21 @@ export const generateContentUrl = (options: GetContentOptions): URL => {
|
|
|
15
15
|
apiKey,
|
|
16
16
|
includeRefs = true,
|
|
17
17
|
locale,
|
|
18
|
+
apiVersion = 'v2',
|
|
18
19
|
} = options;
|
|
19
20
|
|
|
20
21
|
if (!apiKey) {
|
|
21
22
|
throw new Error('Missing API key');
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
if (!['v2', 'v3'].includes(apiVersion)) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Invalid apiVersion: expected 'v2' or 'v3', received '${apiVersion}'`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
24
31
|
const url = new URL(
|
|
25
|
-
`https://cdn.builder.io/api/
|
|
32
|
+
`https://cdn.builder.io/api/${apiVersion}/content/${model}?apiKey=${apiKey}&limit=${limit}&noTraverse=${noTraverse}&includeRefs=${includeRefs}${
|
|
26
33
|
locale ? `&locale=${locale}` : ''
|
|
27
34
|
}`
|
|
28
35
|
);
|
|
@@ -43,4 +43,11 @@ export interface GetContentOptions {
|
|
|
43
43
|
* If provided, the API will auto-resolve localized objects to the value of this `locale` key.
|
|
44
44
|
*/
|
|
45
45
|
locale?: string;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* If provided, sets the Builder API version used to fetch content.
|
|
49
|
+
*
|
|
50
|
+
* Defaults to `v2`.
|
|
51
|
+
*/
|
|
52
|
+
apiVersion?: 'v2' | 'v3';
|
|
46
53
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type ApiVersion = 'v2' | 'v3';
|