@shoppexio/builder-runtime 0.1.1 → 0.1.3
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/dist/YouTubeEmbed.d.ts +13 -0
- package/dist/YouTubeEmbed.d.ts.map +1 -0
- package/dist/YouTubeEmbed.js +49 -0
- package/dist/YouTubeEmbedBuilderBlock.d.ts +7 -0
- package/dist/YouTubeEmbedBuilderBlock.d.ts.map +1 -0
- package/dist/YouTubeEmbedBuilderBlock.js +16 -0
- package/dist/block-style-settings.d.ts +5 -0
- package/dist/block-style-settings.d.ts.map +1 -0
- package/dist/block-style-settings.js +16 -0
- package/dist/builder-runtime.test.d.ts +2 -0
- package/dist/builder-runtime.test.d.ts.map +1 -0
- package/dist/builder-runtime.test.js +115 -0
- package/dist/content.d.ts +6 -0
- package/dist/content.d.ts.map +1 -1
- package/dist/content.js +31 -7
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/layout.d.ts +3 -8
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +2 -10
- package/dist/manifest-setting-paths.d.ts +5 -0
- package/dist/manifest-setting-paths.d.ts.map +1 -0
- package/dist/manifest-setting-paths.js +40 -0
- package/dist/merchant-custom-page.d.ts +57 -0
- package/dist/merchant-custom-page.d.ts.map +1 -0
- package/dist/merchant-custom-page.js +63 -0
- package/dist/preview-fixtures.d.ts +16 -0
- package/dist/preview-fixtures.d.ts.map +1 -0
- package/dist/preview-fixtures.js +40 -0
- package/dist/preview-mode.d.ts +2 -0
- package/dist/preview-mode.d.ts.map +1 -0
- package/dist/preview-mode.js +7 -0
- package/dist/product-page.d.ts +13 -0
- package/dist/product-page.d.ts.map +1 -0
- package/dist/product-page.js +18 -0
- package/dist/react-runtime.test.d.ts +2 -0
- package/dist/react-runtime.test.d.ts.map +1 -0
- package/dist/react-runtime.test.js +332 -0
- package/dist/react.d.ts +37 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +138 -46
- package/dist/search-bar-settings.d.ts +33 -0
- package/dist/search-bar-settings.d.ts.map +1 -0
- package/dist/search-bar-settings.js +99 -0
- package/dist/standard-product-blocks.d.ts +48 -0
- package/dist/standard-product-blocks.d.ts.map +1 -0
- package/dist/standard-product-blocks.js +45 -0
- package/dist/standard-product-page.d.ts +69 -0
- package/dist/standard-product-page.d.ts.map +1 -0
- package/dist/standard-product-page.js +89 -0
- package/dist/storefront-google-fonts.d.ts +2 -0
- package/dist/storefront-google-fonts.d.ts.map +1 -0
- package/dist/storefront-google-fonts.js +28 -0
- package/dist/youtube-embed-block.d.ts +10 -0
- package/dist/youtube-embed-block.d.ts.map +1 -0
- package/dist/youtube-embed-block.js +19 -0
- package/dist/youtube.d.ts +5 -0
- package/dist/youtube.d.ts.map +1 -0
- package/dist/youtube.js +52 -0
- package/package.json +3 -3
- package/src/YouTubeEmbed.tsx +105 -0
- package/src/YouTubeEmbedBuilderBlock.tsx +49 -0
- package/src/block-style-settings.ts +24 -0
- package/src/builder-runtime.test.ts +69 -0
- package/src/content.ts +44 -9
- package/src/index.ts +8 -0
- package/src/layout.ts +11 -21
- package/src/manifest-setting-paths.test.ts +23 -0
- package/src/manifest-setting-paths.ts +55 -0
- package/src/merchant-custom-page.tsx +161 -0
- package/src/preview-fixtures.ts +56 -0
- package/src/preview-mode.ts +8 -0
- package/src/product-page.test.ts +37 -0
- package/src/product-page.ts +32 -0
- package/src/react-runtime.test.tsx +42 -0
- package/src/react.tsx +243 -49
- package/src/search-bar-settings.test.ts +72 -0
- package/src/search-bar-settings.ts +176 -0
- package/src/standard-product-blocks.test.tsx +93 -0
- package/src/standard-product-blocks.tsx +121 -0
- package/src/standard-product-page.test.ts +171 -0
- package/src/standard-product-page.ts +169 -0
- package/src/storefront-google-fonts.test.ts +31 -0
- package/src/storefront-google-fonts.ts +43 -0
- package/src/youtube-embed-block.test.ts +76 -0
- package/src/youtube-embed-block.ts +28 -0
- package/src/youtube-embed-builder-block.test.tsx +166 -0
- package/src/youtube.test.ts +48 -0
- package/src/youtube.ts +66 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const BUILDER_PREVIEW_REVIEWS = [
|
|
2
|
+
{
|
|
3
|
+
id: 'preview-review-1',
|
|
4
|
+
author: 'Alex M.',
|
|
5
|
+
comment: 'Instant delivery and clear instructions. Would buy again.',
|
|
6
|
+
rating: 5,
|
|
7
|
+
created_at: '2026-04-12T10:00:00.000Z',
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
id: 'preview-review-2',
|
|
11
|
+
author: 'Jamie R.',
|
|
12
|
+
comment: 'Support answered quickly when I had a setup question.',
|
|
13
|
+
rating: 5,
|
|
14
|
+
created_at: '2026-04-03T14:30:00.000Z',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'preview-review-3',
|
|
18
|
+
author: 'Taylor S.',
|
|
19
|
+
comment: 'Smooth checkout experience and exactly what was advertised.',
|
|
20
|
+
rating: 4,
|
|
21
|
+
created_at: '2026-03-22T09:15:00.000Z',
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
export function getBuilderPreviewReviewFixtures() {
|
|
25
|
+
return BUILDER_PREVIEW_REVIEWS;
|
|
26
|
+
}
|
|
27
|
+
export const BUILDER_PREVIEW_FAQ_ITEMS = [
|
|
28
|
+
{
|
|
29
|
+
question: 'How fast is delivery?',
|
|
30
|
+
answer: 'Most digital products are delivered instantly after payment confirmation.',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
question: 'Which payment methods do you accept?',
|
|
34
|
+
answer: 'Available payment methods depend on your shop configuration and region.',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
question: 'How do I get support?',
|
|
38
|
+
answer: 'Use the contact page or your customer portal for order-related help.',
|
|
39
|
+
},
|
|
40
|
+
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-mode.d.ts","sourceRoot":"","sources":["../src/preview-mode.ts"],"names":[],"mappings":"AAAA,wBAAgB,oBAAoB,CAAC,QAAQ,GAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAmB,GAAG,OAAO,CAOlG"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BlockInstance } from '@shoppex/builder-contracts';
|
|
2
|
+
export declare function getBuilderBlockSettingText(block: Pick<BlockInstance, 'settings'>, key: string): string | null;
|
|
3
|
+
export declare function getLayoutPageBlockAttributes(pageId: string, block: Pick<BlockInstance, 'id' | 'type'>): {
|
|
4
|
+
'data-page-id': string;
|
|
5
|
+
};
|
|
6
|
+
export declare function getProductPageBlockAttributes(block: Pick<BlockInstance, 'id' | 'type'>): {
|
|
7
|
+
'data-page-id': string;
|
|
8
|
+
};
|
|
9
|
+
/** @deprecated Use getBuilderBlockSettingText */
|
|
10
|
+
export declare const getProductBlockText: typeof getBuilderBlockSettingText;
|
|
11
|
+
/** @deprecated Use getProductPageBlockAttributes */
|
|
12
|
+
export declare const getBuilderProductBlockAttributes: typeof getProductPageBlockAttributes;
|
|
13
|
+
//# sourceMappingURL=product-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product-page.d.ts","sourceRoot":"","sources":["../src/product-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAGhE,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,EACtC,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,IAAI,CAGf;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,MAAM,CAAC;;EAM1C;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,MAAM,CAAC;;EAG1C;AAED,iDAAiD;AACjD,eAAO,MAAM,mBAAmB,mCAA6B,CAAC;AAE9D,oDAAoD;AACpD,eAAO,MAAM,gCAAgC,sCAAgC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { builderBlock } from './attributes.js';
|
|
2
|
+
export function getBuilderBlockSettingText(block, key) {
|
|
3
|
+
const value = block.settings[key];
|
|
4
|
+
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
|
|
5
|
+
}
|
|
6
|
+
export function getLayoutPageBlockAttributes(pageId, block) {
|
|
7
|
+
return {
|
|
8
|
+
'data-page-id': pageId,
|
|
9
|
+
...builderBlock(block.id, block.type),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function getProductPageBlockAttributes(block) {
|
|
13
|
+
return getLayoutPageBlockAttributes('product', block);
|
|
14
|
+
}
|
|
15
|
+
/** @deprecated Use getBuilderBlockSettingText */
|
|
16
|
+
export const getProductBlockText = getBuilderBlockSettingText;
|
|
17
|
+
/** @deprecated Use getProductPageBlockAttributes */
|
|
18
|
+
export const getBuilderProductBlockAttributes = getProductPageBlockAttributes;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-runtime.test.d.ts","sourceRoot":"","sources":["../src/react-runtime.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
|
3
|
+
import { createEmptyBuilderSettings } from '@shoppex/builder-contracts';
|
|
4
|
+
import { JSDOM } from 'jsdom';
|
|
5
|
+
import { act } from 'react';
|
|
6
|
+
import { createRoot } from 'react-dom/client';
|
|
7
|
+
import { BuilderBlockFrame, BuilderBlockProvider, BuilderPage, BuilderRuntimePreviewProvider, useBuilderContent, useBuilderContentRecord, } from './react.js';
|
|
8
|
+
function createSettings(revision, title) {
|
|
9
|
+
return {
|
|
10
|
+
...createEmptyBuilderSettings(revision),
|
|
11
|
+
theme: {
|
|
12
|
+
content: {
|
|
13
|
+
'hero.title': title,
|
|
14
|
+
},
|
|
15
|
+
layout: {
|
|
16
|
+
home: {
|
|
17
|
+
blocks: [
|
|
18
|
+
{
|
|
19
|
+
id: 'hero-1',
|
|
20
|
+
type: 'hero',
|
|
21
|
+
visible: true,
|
|
22
|
+
settings: {},
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
style_slots: {},
|
|
28
|
+
pages: [],
|
|
29
|
+
terms: {},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function Probe() {
|
|
34
|
+
const title = useBuilderContent('hero.title', '');
|
|
35
|
+
return (_jsx("section", { "data-builder-block": "hero-1", "data-builder-block-type": "hero", "data-page-id": "home", children: _jsx("h1", { "data-builder-content": "hero.title", children: title }) }));
|
|
36
|
+
}
|
|
37
|
+
function FallbackProbe() {
|
|
38
|
+
const title = useBuilderContent('hero.title', 'Default title');
|
|
39
|
+
return _jsx("h1", { "data-builder-content": "hero.title", children: title });
|
|
40
|
+
}
|
|
41
|
+
function SlotButtonProbe() {
|
|
42
|
+
return (_jsx("section", { "data-builder-block": "hero-1", "data-builder-block-type": "hero", "data-page-id": "home", children: _jsx("button", { type: "button", "data-builder-slot": "button.background", children: "Buy now" }) }));
|
|
43
|
+
}
|
|
44
|
+
function ScopedProbe() {
|
|
45
|
+
return (_jsx(BuilderBlockProvider, { block: {
|
|
46
|
+
id: 'hero-1',
|
|
47
|
+
type: 'hero',
|
|
48
|
+
visible: true,
|
|
49
|
+
settings: {
|
|
50
|
+
title: 'Scoped title',
|
|
51
|
+
subtitle: 'Scoped subtitle',
|
|
52
|
+
},
|
|
53
|
+
}, children: _jsx(ScopedProbeContent, {}) }));
|
|
54
|
+
}
|
|
55
|
+
function ScopedProbeContent() {
|
|
56
|
+
const title = useBuilderContent('hero.title', '');
|
|
57
|
+
const subtitle = useBuilderContent('hero.subtitle', '');
|
|
58
|
+
const contentRecord = useBuilderContentRecord();
|
|
59
|
+
return (_jsxs(_Fragment, { children: [_jsx("h1", { "data-testid": "scoped-title", children: title }), _jsx("p", { "data-testid": "scoped-subtitle", children: subtitle }), _jsx("span", { "data-testid": "scoped-record", children: String(contentRecord.hero.title) })] }));
|
|
60
|
+
}
|
|
61
|
+
function HeroBlock({ block }) {
|
|
62
|
+
const title = useBuilderContent('hero.title', '');
|
|
63
|
+
return (_jsx(BuilderBlockFrame, { as: "section", pageId: "home", block: block, children: _jsx("h1", { "data-builder-content": "hero.title", children: title }) }));
|
|
64
|
+
}
|
|
65
|
+
describe('BuilderRuntimePreviewProvider', () => {
|
|
66
|
+
let dom;
|
|
67
|
+
let root;
|
|
68
|
+
let postedMessages;
|
|
69
|
+
let parentWindow;
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
postedMessages = [];
|
|
72
|
+
parentWindow = {
|
|
73
|
+
postMessage: (message) => {
|
|
74
|
+
postedMessages.push(message);
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
dom = new JSDOM('<!doctype html><html><body><div id="root"></div></body></html>', {
|
|
78
|
+
url: 'https://preview.shoppex.test/?shoppex-preview-mode=theme',
|
|
79
|
+
referrer: 'https://dashboard.shoppex.test/theme/editor',
|
|
80
|
+
});
|
|
81
|
+
Object.defineProperty(dom.window, 'parent', {
|
|
82
|
+
configurable: true,
|
|
83
|
+
value: parentWindow,
|
|
84
|
+
});
|
|
85
|
+
Object.assign(globalThis, {
|
|
86
|
+
window: dom.window,
|
|
87
|
+
document: dom.window.document,
|
|
88
|
+
navigator: dom.window.navigator,
|
|
89
|
+
Element: dom.window.Element,
|
|
90
|
+
HTMLElement: dom.window.HTMLElement,
|
|
91
|
+
HTMLImageElement: dom.window.HTMLImageElement,
|
|
92
|
+
Node: dom.window.Node,
|
|
93
|
+
MouseEvent: dom.window.MouseEvent,
|
|
94
|
+
MessageEvent: dom.window.MessageEvent,
|
|
95
|
+
CustomEvent: dom.window.CustomEvent,
|
|
96
|
+
CSS: {
|
|
97
|
+
escape: (value) => value.replaceAll('"', '\\"'),
|
|
98
|
+
},
|
|
99
|
+
IS_REACT_ACT_ENVIRONMENT: true,
|
|
100
|
+
});
|
|
101
|
+
root = createRoot(dom.window.document.getElementById('root'));
|
|
102
|
+
});
|
|
103
|
+
afterEach(async () => {
|
|
104
|
+
await act(async () => {
|
|
105
|
+
root.unmount();
|
|
106
|
+
});
|
|
107
|
+
dom.window.close();
|
|
108
|
+
const globals = globalThis;
|
|
109
|
+
for (const key of [
|
|
110
|
+
'window',
|
|
111
|
+
'document',
|
|
112
|
+
'navigator',
|
|
113
|
+
'Element',
|
|
114
|
+
'HTMLElement',
|
|
115
|
+
'HTMLImageElement',
|
|
116
|
+
'Node',
|
|
117
|
+
'MouseEvent',
|
|
118
|
+
'MessageEvent',
|
|
119
|
+
'CustomEvent',
|
|
120
|
+
'CSS',
|
|
121
|
+
'IS_REACT_ACT_ENVIRONMENT',
|
|
122
|
+
]) {
|
|
123
|
+
delete globals[key];
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
test('sends READY with the initial revision', async () => {
|
|
127
|
+
await act(async () => {
|
|
128
|
+
root.render(_jsx(BuilderRuntimePreviewProvider, { initialSettings: createSettings(3, 'Initial title'), children: _jsx(Probe, {}) }));
|
|
129
|
+
});
|
|
130
|
+
expect(postedMessages).toContainEqual({
|
|
131
|
+
type: 'READY',
|
|
132
|
+
revision: 3,
|
|
133
|
+
health: {
|
|
134
|
+
reactMounted: true,
|
|
135
|
+
builderRuntimeProvider: true,
|
|
136
|
+
protocolVersion: 2,
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
expect(dom.window.document.body.textContent).toContain('Initial title');
|
|
140
|
+
});
|
|
141
|
+
test('normalizes mixed initial builder settings before strict validation', async () => {
|
|
142
|
+
await act(async () => {
|
|
143
|
+
root.render(_jsx(BuilderRuntimePreviewProvider, { initialSettings: {
|
|
144
|
+
version: 2,
|
|
145
|
+
revision: 9,
|
|
146
|
+
theme: {
|
|
147
|
+
content: {
|
|
148
|
+
'hero.title': 'Mixed title',
|
|
149
|
+
},
|
|
150
|
+
layout: {
|
|
151
|
+
home: {
|
|
152
|
+
sections: [
|
|
153
|
+
{
|
|
154
|
+
id: 'hero',
|
|
155
|
+
visible: true,
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
tokens_override: {
|
|
161
|
+
colors: {
|
|
162
|
+
primary: '#111827',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
}, children: _jsx(Probe, {}) }));
|
|
167
|
+
});
|
|
168
|
+
expect(postedMessages).toContainEqual({
|
|
169
|
+
type: 'READY',
|
|
170
|
+
revision: 9,
|
|
171
|
+
health: {
|
|
172
|
+
reactMounted: true,
|
|
173
|
+
builderRuntimeProvider: true,
|
|
174
|
+
protocolVersion: 2,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
expect(dom.window.document.body.textContent).toContain('Mixed title');
|
|
178
|
+
});
|
|
179
|
+
test('preserves intentionally empty content strings in hooks', async () => {
|
|
180
|
+
await act(async () => {
|
|
181
|
+
root.render(_jsx(BuilderRuntimePreviewProvider, { initialSettings: createSettings(4, ''), children: _jsx(FallbackProbe, {}) }));
|
|
182
|
+
});
|
|
183
|
+
expect(dom.window.document.body.textContent).toBe('');
|
|
184
|
+
});
|
|
185
|
+
test('responds to explicit READY requests when the parent missed the initial message', async () => {
|
|
186
|
+
await act(async () => {
|
|
187
|
+
root.render(_jsx(BuilderRuntimePreviewProvider, { initialSettings: createSettings(3, 'Initial title'), children: _jsx(Probe, {}) }));
|
|
188
|
+
});
|
|
189
|
+
postedMessages.length = 0;
|
|
190
|
+
await act(async () => {
|
|
191
|
+
dom.window.dispatchEvent(new dom.window.MessageEvent('message', {
|
|
192
|
+
origin: 'https://dashboard.shoppex.test',
|
|
193
|
+
source: parentWindow,
|
|
194
|
+
data: {
|
|
195
|
+
type: 'REQUEST_READY',
|
|
196
|
+
},
|
|
197
|
+
}));
|
|
198
|
+
});
|
|
199
|
+
expect(postedMessages).toEqual([{
|
|
200
|
+
type: 'READY',
|
|
201
|
+
revision: 3,
|
|
202
|
+
health: {
|
|
203
|
+
reactMounted: true,
|
|
204
|
+
builderRuntimeProvider: true,
|
|
205
|
+
protocolVersion: 2,
|
|
206
|
+
},
|
|
207
|
+
}]);
|
|
208
|
+
});
|
|
209
|
+
test('reports iframe runtime errors to the builder parent', async () => {
|
|
210
|
+
await act(async () => {
|
|
211
|
+
root.render(_jsx(BuilderRuntimePreviewProvider, { initialSettings: createSettings(6, 'Initial title'), children: _jsx(Probe, {}) }));
|
|
212
|
+
});
|
|
213
|
+
postedMessages.length = 0;
|
|
214
|
+
dom.window.dispatchEvent(new dom.window.ErrorEvent('error', {
|
|
215
|
+
error: new Error('Preview crashed'),
|
|
216
|
+
message: 'Preview crashed',
|
|
217
|
+
}));
|
|
218
|
+
expect(postedMessages).toEqual([expect.objectContaining({
|
|
219
|
+
type: 'PREVIEW_ERROR',
|
|
220
|
+
revision: 6,
|
|
221
|
+
message: 'Preview crashed',
|
|
222
|
+
source: 'error',
|
|
223
|
+
})]);
|
|
224
|
+
});
|
|
225
|
+
test('applies preview state and acknowledges the exact revision', async () => {
|
|
226
|
+
await act(async () => {
|
|
227
|
+
root.render(_jsx(BuilderRuntimePreviewProvider, { initialSettings: createSettings(3, 'Initial title'), children: _jsx(Probe, {}) }));
|
|
228
|
+
});
|
|
229
|
+
await act(async () => {
|
|
230
|
+
dom.window.dispatchEvent(new dom.window.MessageEvent('message', {
|
|
231
|
+
origin: 'https://dashboard.shoppex.test',
|
|
232
|
+
source: parentWindow,
|
|
233
|
+
data: {
|
|
234
|
+
type: 'APPLY_STATE',
|
|
235
|
+
revision: 4,
|
|
236
|
+
state: createSettings(4, 'Updated title'),
|
|
237
|
+
},
|
|
238
|
+
}));
|
|
239
|
+
});
|
|
240
|
+
expect(postedMessages).toContainEqual({ type: 'APPLIED', revision: 4 });
|
|
241
|
+
expect(dom.window.document.body.textContent).toContain('Updated title');
|
|
242
|
+
});
|
|
243
|
+
test('renders page blocks through a typed registry and shared block frame', async () => {
|
|
244
|
+
await act(async () => {
|
|
245
|
+
root.render(_jsx(BuilderRuntimePreviewProvider, { initialSettings: createSettings(3, 'Registry title'), children: _jsx(BuilderPage, { pageId: "home", registry: {
|
|
246
|
+
hero: HeroBlock,
|
|
247
|
+
}, context: {} }) }));
|
|
248
|
+
});
|
|
249
|
+
const renderedBlock = dom.window.document.querySelector('[data-builder-block="hero-1"]');
|
|
250
|
+
expect(renderedBlock?.getAttribute('data-page-id')).toBe('home');
|
|
251
|
+
expect(renderedBlock?.getAttribute('data-builder-block-type')).toBe('hero');
|
|
252
|
+
expect(dom.window.document.body.textContent).toContain('Registry title');
|
|
253
|
+
});
|
|
254
|
+
test('prefers scoped block settings over global builder content inside a block provider', async () => {
|
|
255
|
+
await act(async () => {
|
|
256
|
+
root.render(_jsx(BuilderRuntimePreviewProvider, { initialSettings: createSettings(3, 'Global title'), children: _jsx(ScopedProbe, {}) }));
|
|
257
|
+
});
|
|
258
|
+
expect(dom.window.document.querySelector('[data-testid="scoped-title"]')?.textContent).toBe('Scoped title');
|
|
259
|
+
expect(dom.window.document.querySelector('[data-testid="scoped-subtitle"]')?.textContent).toBe('Scoped subtitle');
|
|
260
|
+
expect(dom.window.document.querySelector('[data-testid="scoped-record"]')?.textContent).toBe('Scoped title');
|
|
261
|
+
});
|
|
262
|
+
test('rejects stale preview revisions without changing rendered content', async () => {
|
|
263
|
+
await act(async () => {
|
|
264
|
+
root.render(_jsx(BuilderRuntimePreviewProvider, { initialSettings: createSettings(5, 'Current title'), children: _jsx(Probe, {}) }));
|
|
265
|
+
});
|
|
266
|
+
await act(async () => {
|
|
267
|
+
dom.window.dispatchEvent(new dom.window.MessageEvent('message', {
|
|
268
|
+
origin: 'https://dashboard.shoppex.test',
|
|
269
|
+
source: parentWindow,
|
|
270
|
+
data: {
|
|
271
|
+
type: 'APPLY_STATE',
|
|
272
|
+
revision: 4,
|
|
273
|
+
state: createSettings(4, 'Stale title'),
|
|
274
|
+
},
|
|
275
|
+
}));
|
|
276
|
+
});
|
|
277
|
+
expect(postedMessages).toContainEqual({
|
|
278
|
+
type: 'APPLY_FAILED',
|
|
279
|
+
revision: 4,
|
|
280
|
+
error: 'Stale builder revision',
|
|
281
|
+
});
|
|
282
|
+
expect(dom.window.document.body.textContent).toContain('Current title');
|
|
283
|
+
});
|
|
284
|
+
test('emits block selection details from iframe clicks', async () => {
|
|
285
|
+
await act(async () => {
|
|
286
|
+
root.render(_jsx(BuilderRuntimePreviewProvider, { initialSettings: createSettings(7, 'Clickable title'), children: _jsx(Probe, {}) }));
|
|
287
|
+
});
|
|
288
|
+
const heading = dom.window.document.querySelector('h1');
|
|
289
|
+
heading?.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
290
|
+
expect(postedMessages).toContainEqual({
|
|
291
|
+
type: 'ELEMENT_CLICKED',
|
|
292
|
+
revision: 7,
|
|
293
|
+
selection: {
|
|
294
|
+
pageId: 'home',
|
|
295
|
+
blockId: 'hero-1',
|
|
296
|
+
blockType: 'hero',
|
|
297
|
+
contentPath: 'hero.title',
|
|
298
|
+
elementType: 'text',
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
test('classifies buttons before style slots for inspector clicks', async () => {
|
|
303
|
+
await act(async () => {
|
|
304
|
+
root.render(_jsx(BuilderRuntimePreviewProvider, { initialSettings: createSettings(7, 'Clickable title'), children: _jsx(SlotButtonProbe, {}) }));
|
|
305
|
+
});
|
|
306
|
+
const button = dom.window.document.querySelector('button');
|
|
307
|
+
button?.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
308
|
+
expect(postedMessages).toContainEqual({
|
|
309
|
+
type: 'ELEMENT_CLICKED',
|
|
310
|
+
revision: 7,
|
|
311
|
+
selection: {
|
|
312
|
+
pageId: 'home',
|
|
313
|
+
blockId: 'hero-1',
|
|
314
|
+
blockType: 'hero',
|
|
315
|
+
slotId: 'button.background',
|
|
316
|
+
elementType: 'button',
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
test('does not intercept clicks outside trusted builder preview embeds', async () => {
|
|
321
|
+
dom.reconfigure({
|
|
322
|
+
url: 'https://preview.shoppex.test/',
|
|
323
|
+
});
|
|
324
|
+
await act(async () => {
|
|
325
|
+
root.render(_jsx(BuilderRuntimePreviewProvider, { initialSettings: createSettings(7, 'Clickable title'), children: _jsx(Probe, {}) }));
|
|
326
|
+
});
|
|
327
|
+
postedMessages.length = 0;
|
|
328
|
+
const heading = dom.window.document.querySelector('h1');
|
|
329
|
+
heading?.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
330
|
+
expect(postedMessages).toEqual([]);
|
|
331
|
+
});
|
|
332
|
+
});
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BlockInstance, BuilderSettings, Breakpoint, StyleSlotId } from '@shoppex/builder-contracts';
|
|
2
|
-
import { type ComponentType, type ElementType, type ReactNode } from 'react';
|
|
2
|
+
import { type ComponentPropsWithoutRef, type ComponentType, type ElementType, type ReactNode } from 'react';
|
|
3
|
+
import { type BuilderAttributeMap } from './attributes.js';
|
|
3
4
|
type BuilderRuntimeContextValue = {
|
|
4
5
|
settings: BuilderSettings;
|
|
5
6
|
};
|
|
@@ -75,11 +76,45 @@ export declare function useVisibleBuilderPageBlocks(pageId: string): {
|
|
|
75
76
|
style_overrides?: Partial<Record<StyleSlotId, import("@shoppex/builder-contracts").StyleSlotValue>> | undefined;
|
|
76
77
|
}[];
|
|
77
78
|
export declare function useThemePageBlocks(pageId: string, defaultOrder: string[]): BlockInstance[];
|
|
79
|
+
export declare function useThemePageBlockAttributes(pageId: string, defaultOrder: string[]): BuilderAttributeMap;
|
|
80
|
+
type DedicatedBuilderPageProps<T extends ElementType> = {
|
|
81
|
+
pageId: string;
|
|
82
|
+
defaultBlockOrder: string[];
|
|
83
|
+
as?: T;
|
|
84
|
+
children: ReactNode;
|
|
85
|
+
} & Omit<ComponentPropsWithoutRef<T>, 'as' | 'children'>;
|
|
86
|
+
export declare function DedicatedBuilderPage<T extends ElementType = 'section'>({ pageId, defaultBlockOrder, as, children, ...rest }: DedicatedBuilderPageProps<T>): import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
|
|
87
|
+
export declare function useBuilderPreviewReviews<T>(input: {
|
|
88
|
+
reviews: T[];
|
|
89
|
+
isLoading: boolean;
|
|
90
|
+
error: unknown;
|
|
91
|
+
fixtures?: T[];
|
|
92
|
+
}): {
|
|
93
|
+
reviews: T[];
|
|
94
|
+
isLoading: boolean;
|
|
95
|
+
error: unknown;
|
|
96
|
+
isUsingFixtures: boolean;
|
|
97
|
+
};
|
|
78
98
|
export declare function useBuilderStyleSlot(slotId: StyleSlotId, input?: {
|
|
79
99
|
breakpoint?: Breakpoint;
|
|
80
100
|
fallback?: unknown;
|
|
81
101
|
}): unknown;
|
|
82
102
|
export declare function useBuilderCss(selector?: string): string;
|
|
103
|
+
export declare function useSearchBarSettings(input: {
|
|
104
|
+
variant: 'hero' | 'navigation';
|
|
105
|
+
headerSettings?: Record<string, unknown>;
|
|
106
|
+
}): import("./search-bar-settings.js").ResolvedSearchBarSettings;
|
|
107
|
+
export { buildSearchShellStyle, getNavigationHeaderSettings, resolveSearchBarSettings, } from './search-bar-settings.js';
|
|
108
|
+
export declare function isBuilderPreviewRuntime(): boolean;
|
|
83
109
|
export declare function resolvePreviewReloadTarget(location: Pick<Location, 'search' | 'hash'>, sessionPath: unknown): string | null;
|
|
84
|
-
export {};
|
|
110
|
+
export { getBuilderBlockSettingText, getBuilderProductBlockAttributes, getLayoutPageBlockAttributes, getProductBlockText, getProductPageBlockAttributes, } from './product-page.js';
|
|
111
|
+
export { createStandardProductBlockRegistry, splitStandardProductPageBlocks, buildStandardProductInfoTabs, resolveStandardBuyBoxLabels, resolveStandardDetailsLabels, resolveStandardRelatedProductsTitle, resolveScopedBlockSettingText, STANDARD_PRODUCT_BLOCK_TYPES, STANDARD_PRODUCT_PRIMARY_BLOCK_TYPES, STANDARD_PRODUCT_SECONDARY_BLOCK_TYPES, } from './standard-product-blocks.js';
|
|
112
|
+
export type { StandardBuyBoxLabels, StandardDetailsLabels, StandardProductBlockRegistryData, StandardProductBlockRegistryOptions, StandardProductBlockRegistrySlots, StandardProductBlockType, StandardProductSettingScope, StandardProductTabSpec, } from './standard-product-blocks.js';
|
|
113
|
+
export { YouTubeEmbed, YouTubeEmbedPreviewPlaceholder, type YouTubeEmbedProps } from './YouTubeEmbed.js';
|
|
114
|
+
export { YouTubeEmbedBuilderBlock, type YouTubeEmbedBuilderBlockProps, } from './YouTubeEmbedBuilderBlock.js';
|
|
115
|
+
export { createMerchantCustomPageRegistry, MerchantCustomPageBuilderView, useMerchantCustomPageRegistry, useMerchantCustomPageView, type MerchantCustomPageRegistryOptions, } from './merchant-custom-page.js';
|
|
116
|
+
export { isBuilderPreviewMode } from './preview-mode.js';
|
|
117
|
+
export { getYouTubeEmbedBlockStyleProps, readYouTubeEmbedBlockSettings, type YouTubeEmbedBlockInstance, } from './youtube-embed-block.js';
|
|
118
|
+
export { readManifestStyleBlockProps } from './block-style-settings.js';
|
|
119
|
+
export { getBuilderPreviewReviewFixtures } from './preview-fixtures.js';
|
|
85
120
|
//# sourceMappingURL=react.d.ts.map
|
package/dist/react.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAoB,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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"}
|