@shoppexio/builder-runtime 0.1.0 → 0.1.2
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/css-vars.d.ts.map +1 -1
- package/dist/css-vars.js +24 -6
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/layout.d.ts +5 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +2 -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/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.d.ts +36 -224
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +606 -47
- 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/package.json +3 -3
- package/src/builder-runtime.test.ts +57 -0
- package/src/css-vars.ts +29 -8
- package/src/index.ts +4 -0
- package/src/layout.ts +14 -1
- package/src/preview-fixtures.ts +56 -0
- package/src/product-page.test.ts +37 -0
- package/src/product-page.ts +32 -0
- package/src/react-runtime.test.tsx +215 -3
- package/src/react.tsx +769 -45
- 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/dist/builder-runtime.test.d.ts +0 -2
- package/dist/builder-runtime.test.d.ts.map +0 -1
- package/dist/builder-runtime.test.js +0 -115
- package/dist/react-runtime.test.d.ts +0 -2
- package/dist/react-runtime.test.d.ts.map +0 -1
- package/dist/react-runtime.test.js +0 -292
package/src/layout.ts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
getThemePageBlockOrderFromManifest as resolveThemePageBlockOrder,
|
|
3
|
+
type BlockInstance,
|
|
4
|
+
type BuilderSettings,
|
|
5
|
+
type PageLayout,
|
|
6
|
+
type ThemeManifest,
|
|
7
|
+
type ThemePageBlockOrderPage,
|
|
8
|
+
} from '@shoppex/builder-contracts';
|
|
9
|
+
|
|
10
|
+
export type ThemePageBlockOrderManifest = {
|
|
11
|
+
pages?: Record<string, ThemePageBlockOrderPage>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export { resolveThemePageBlockOrder as getThemePageBlockOrderFromManifest };
|
|
2
15
|
|
|
3
16
|
export function getPageLayout(settings: BuilderSettings, pageId: string): PageLayout {
|
|
4
17
|
return settings.theme.layout[pageId] ?? { blocks: [] };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type BuilderPreviewReview = {
|
|
2
|
+
id: string;
|
|
3
|
+
author: string | null;
|
|
4
|
+
comment: string | null;
|
|
5
|
+
rating: number | null;
|
|
6
|
+
created_at: string;
|
|
7
|
+
is_automated?: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type BuilderPreviewFaqItem = {
|
|
11
|
+
question: string;
|
|
12
|
+
answer: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const BUILDER_PREVIEW_REVIEWS: BuilderPreviewReview[] = [
|
|
16
|
+
{
|
|
17
|
+
id: 'preview-review-1',
|
|
18
|
+
author: 'Alex M.',
|
|
19
|
+
comment: 'Instant delivery and clear instructions. Would buy again.',
|
|
20
|
+
rating: 5,
|
|
21
|
+
created_at: '2026-04-12T10:00:00.000Z',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'preview-review-2',
|
|
25
|
+
author: 'Jamie R.',
|
|
26
|
+
comment: 'Support answered quickly when I had a setup question.',
|
|
27
|
+
rating: 5,
|
|
28
|
+
created_at: '2026-04-03T14:30:00.000Z',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'preview-review-3',
|
|
32
|
+
author: 'Taylor S.',
|
|
33
|
+
comment: 'Smooth checkout experience and exactly what was advertised.',
|
|
34
|
+
rating: 4,
|
|
35
|
+
created_at: '2026-03-22T09:15:00.000Z',
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export function getBuilderPreviewReviewFixtures<T = BuilderPreviewReview>(): T[] {
|
|
40
|
+
return BUILDER_PREVIEW_REVIEWS as T[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const BUILDER_PREVIEW_FAQ_ITEMS: BuilderPreviewFaqItem[] = [
|
|
44
|
+
{
|
|
45
|
+
question: 'How fast is delivery?',
|
|
46
|
+
answer: 'Most digital products are delivered instantly after payment confirmation.',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
question: 'Which payment methods do you accept?',
|
|
50
|
+
answer: 'Available payment methods depend on your shop configuration and region.',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
question: 'How do I get support?',
|
|
54
|
+
answer: 'Use the contact page or your customer portal for order-related help.',
|
|
55
|
+
},
|
|
56
|
+
];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { createBlockInstance } from '@shoppex/builder-contracts';
|
|
3
|
+
import {
|
|
4
|
+
getBuilderBlockSettingText,
|
|
5
|
+
getLayoutPageBlockAttributes,
|
|
6
|
+
getProductPageBlockAttributes,
|
|
7
|
+
} from './product-page.js';
|
|
8
|
+
|
|
9
|
+
describe('product-page helpers', () => {
|
|
10
|
+
test('getBuilderBlockSettingText trims non-empty strings', () => {
|
|
11
|
+
const block = createBlockInstance({
|
|
12
|
+
type: 'buy-box',
|
|
13
|
+
settings: {
|
|
14
|
+
variantLabel: ' License Type ',
|
|
15
|
+
addonsLabel: ' ',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
expect(getBuilderBlockSettingText(block, 'variantLabel')).toBe('License Type');
|
|
20
|
+
expect(getBuilderBlockSettingText(block, 'addonsLabel')).toBeNull();
|
|
21
|
+
expect(getBuilderBlockSettingText(block, 'missing')).toBeNull();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('getLayoutPageBlockAttributes includes page id and builder block attrs', () => {
|
|
25
|
+
const block = createBlockInstance({ id: 'gallery-1', type: 'gallery' });
|
|
26
|
+
expect(getProductPageBlockAttributes(block)).toEqual({
|
|
27
|
+
'data-page-id': 'product',
|
|
28
|
+
'data-builder-block': 'gallery-1',
|
|
29
|
+
'data-builder-block-type': 'gallery',
|
|
30
|
+
});
|
|
31
|
+
expect(getLayoutPageBlockAttributes('reviews-page', block)).toEqual({
|
|
32
|
+
'data-page-id': 'reviews-page',
|
|
33
|
+
'data-builder-block': 'gallery-1',
|
|
34
|
+
'data-builder-block-type': 'gallery',
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { BlockInstance } from '@shoppex/builder-contracts';
|
|
2
|
+
import { builderBlock } from './attributes.js';
|
|
3
|
+
|
|
4
|
+
export function getBuilderBlockSettingText(
|
|
5
|
+
block: Pick<BlockInstance, 'settings'>,
|
|
6
|
+
key: string,
|
|
7
|
+
): string | null {
|
|
8
|
+
const value = block.settings[key];
|
|
9
|
+
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getLayoutPageBlockAttributes(
|
|
13
|
+
pageId: string,
|
|
14
|
+
block: Pick<BlockInstance, 'id' | 'type'>,
|
|
15
|
+
) {
|
|
16
|
+
return {
|
|
17
|
+
'data-page-id': pageId,
|
|
18
|
+
...builderBlock(block.id, block.type),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getProductPageBlockAttributes(
|
|
23
|
+
block: Pick<BlockInstance, 'id' | 'type'>,
|
|
24
|
+
) {
|
|
25
|
+
return getLayoutPageBlockAttributes('product', block);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @deprecated Use getBuilderBlockSettingText */
|
|
29
|
+
export const getProductBlockText = getBuilderBlockSettingText;
|
|
30
|
+
|
|
31
|
+
/** @deprecated Use getProductPageBlockAttributes */
|
|
32
|
+
export const getBuilderProductBlockAttributes = getProductPageBlockAttributes;
|
|
@@ -8,8 +8,11 @@ import {
|
|
|
8
8
|
BuilderBlockProvider,
|
|
9
9
|
BuilderPage,
|
|
10
10
|
BuilderRuntimePreviewProvider,
|
|
11
|
+
resolvePreviewReloadTarget,
|
|
11
12
|
useBuilderContent,
|
|
12
13
|
useBuilderContentRecord,
|
|
14
|
+
useThemePageBlocks,
|
|
15
|
+
useThemePageBlockAttributes,
|
|
13
16
|
} from './react.js';
|
|
14
17
|
|
|
15
18
|
function createSettings(revision: number, title: string): BuilderSettings {
|
|
@@ -96,6 +99,33 @@ function ScopedProbeContent() {
|
|
|
96
99
|
);
|
|
97
100
|
}
|
|
98
101
|
|
|
102
|
+
function PageBlocksProbe({ pageId = 'home', defaultOrder = ['hero', 'products'] }: {
|
|
103
|
+
pageId?: string;
|
|
104
|
+
defaultOrder?: string[];
|
|
105
|
+
}) {
|
|
106
|
+
const blocks = useThemePageBlocks(pageId, defaultOrder);
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<ol data-testid="page-blocks">
|
|
110
|
+
{blocks.map((block) => (
|
|
111
|
+
<li key={block.id}>{block.id}:{block.type}</li>
|
|
112
|
+
))}
|
|
113
|
+
</ol>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function PageBlockAttributesProbe({
|
|
118
|
+
pageId = 'contact-page',
|
|
119
|
+
defaultOrder = ['page-contact-page'],
|
|
120
|
+
}: {
|
|
121
|
+
pageId?: string;
|
|
122
|
+
defaultOrder?: string[];
|
|
123
|
+
}) {
|
|
124
|
+
const attrs = useThemePageBlockAttributes(pageId, defaultOrder);
|
|
125
|
+
|
|
126
|
+
return <section data-testid="page-block-attrs" {...attrs} />;
|
|
127
|
+
}
|
|
128
|
+
|
|
99
129
|
function HeroBlock({ block }: { block: BlockInstance }) {
|
|
100
130
|
const title = useBuilderContent('hero.title', '');
|
|
101
131
|
|
|
@@ -106,6 +136,26 @@ function HeroBlock({ block }: { block: BlockInstance }) {
|
|
|
106
136
|
);
|
|
107
137
|
}
|
|
108
138
|
|
|
139
|
+
describe('resolvePreviewReloadTarget', () => {
|
|
140
|
+
test('reloads through the worker session route when one was injected', () => {
|
|
141
|
+
expect(
|
|
142
|
+
resolvePreviewReloadTarget(
|
|
143
|
+
{ search: '?shoppex-preview-mode=theme', hash: '#hero' },
|
|
144
|
+
'/s/session-1/draft-1/products/example',
|
|
145
|
+
),
|
|
146
|
+
).toBe('/s/session-1/draft-1/products/example?shoppex-preview-mode=theme#hero');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('keeps normal browser reload behavior outside preview sessions', () => {
|
|
150
|
+
expect(
|
|
151
|
+
resolvePreviewReloadTarget(
|
|
152
|
+
{ search: '?shoppex-preview-mode=theme', hash: '' },
|
|
153
|
+
'/products/example',
|
|
154
|
+
),
|
|
155
|
+
).toBeNull();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
109
159
|
describe('BuilderRuntimePreviewProvider', () => {
|
|
110
160
|
let dom: JSDOM;
|
|
111
161
|
let root: Root;
|
|
@@ -184,10 +234,47 @@ describe('BuilderRuntimePreviewProvider', () => {
|
|
|
184
234
|
);
|
|
185
235
|
});
|
|
186
236
|
|
|
187
|
-
expect(postedMessages).toContainEqual({
|
|
237
|
+
expect(postedMessages).toContainEqual({
|
|
238
|
+
type: 'READY',
|
|
239
|
+
revision: 3,
|
|
240
|
+
health: {
|
|
241
|
+
reactMounted: true,
|
|
242
|
+
builderRuntimeProvider: true,
|
|
243
|
+
protocolVersion: 2,
|
|
244
|
+
},
|
|
245
|
+
});
|
|
188
246
|
expect(dom.window.document.body.textContent).toContain('Initial title');
|
|
189
247
|
});
|
|
190
248
|
|
|
249
|
+
test('sends READY when the trusted parent origin is provided explicitly without a referrer', async () => {
|
|
250
|
+
dom.reconfigure({
|
|
251
|
+
url: 'https://preview.shoppex.test/?shoppex-preview-mode=theme&shoppex-preview-parent-origin=https%3A%2F%2Fdashboard.shoppex.test',
|
|
252
|
+
});
|
|
253
|
+
Object.defineProperty(dom.window.document, 'referrer', {
|
|
254
|
+
configurable: true,
|
|
255
|
+
value: '',
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
await act(async () => {
|
|
259
|
+
root.render(
|
|
260
|
+
<BuilderRuntimePreviewProvider initialSettings={createSettings(4, 'Explicit origin title')}>
|
|
261
|
+
<Probe />
|
|
262
|
+
</BuilderRuntimePreviewProvider>,
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
expect(postedMessages).toContainEqual({
|
|
267
|
+
type: 'READY',
|
|
268
|
+
revision: 4,
|
|
269
|
+
health: {
|
|
270
|
+
reactMounted: true,
|
|
271
|
+
builderRuntimeProvider: true,
|
|
272
|
+
protocolVersion: 2,
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
expect(dom.window.document.body.textContent).toContain('Explicit origin title');
|
|
276
|
+
});
|
|
277
|
+
|
|
191
278
|
test('normalizes mixed initial builder settings before strict validation', async () => {
|
|
192
279
|
await act(async () => {
|
|
193
280
|
root.render(
|
|
@@ -222,7 +309,15 @@ describe('BuilderRuntimePreviewProvider', () => {
|
|
|
222
309
|
);
|
|
223
310
|
});
|
|
224
311
|
|
|
225
|
-
expect(postedMessages).toContainEqual({
|
|
312
|
+
expect(postedMessages).toContainEqual({
|
|
313
|
+
type: 'READY',
|
|
314
|
+
revision: 9,
|
|
315
|
+
health: {
|
|
316
|
+
reactMounted: true,
|
|
317
|
+
builderRuntimeProvider: true,
|
|
318
|
+
protocolVersion: 2,
|
|
319
|
+
},
|
|
320
|
+
});
|
|
226
321
|
expect(dom.window.document.body.textContent).toContain('Mixed title');
|
|
227
322
|
});
|
|
228
323
|
|
|
@@ -261,7 +356,39 @@ describe('BuilderRuntimePreviewProvider', () => {
|
|
|
261
356
|
);
|
|
262
357
|
});
|
|
263
358
|
|
|
264
|
-
expect(postedMessages).toEqual([{
|
|
359
|
+
expect(postedMessages).toEqual([{
|
|
360
|
+
type: 'READY',
|
|
361
|
+
revision: 3,
|
|
362
|
+
health: {
|
|
363
|
+
reactMounted: true,
|
|
364
|
+
builderRuntimeProvider: true,
|
|
365
|
+
protocolVersion: 2,
|
|
366
|
+
},
|
|
367
|
+
}]);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test('reports iframe runtime errors to the builder parent', async () => {
|
|
371
|
+
await act(async () => {
|
|
372
|
+
root.render(
|
|
373
|
+
<BuilderRuntimePreviewProvider initialSettings={createSettings(6, 'Initial title')}>
|
|
374
|
+
<Probe />
|
|
375
|
+
</BuilderRuntimePreviewProvider>,
|
|
376
|
+
);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
postedMessages.length = 0;
|
|
380
|
+
|
|
381
|
+
dom.window.dispatchEvent(new dom.window.ErrorEvent('error', {
|
|
382
|
+
error: new Error('Preview crashed'),
|
|
383
|
+
message: 'Preview crashed',
|
|
384
|
+
}));
|
|
385
|
+
|
|
386
|
+
expect(postedMessages).toEqual([expect.objectContaining({
|
|
387
|
+
type: 'PREVIEW_ERROR',
|
|
388
|
+
revision: 6,
|
|
389
|
+
message: 'Preview crashed',
|
|
390
|
+
source: 'error',
|
|
391
|
+
})]);
|
|
265
392
|
});
|
|
266
393
|
|
|
267
394
|
test('applies preview state and acknowledges the exact revision', async () => {
|
|
@@ -313,6 +440,91 @@ describe('BuilderRuntimePreviewProvider', () => {
|
|
|
313
440
|
expect(dom.window.document.body.textContent).toContain('Registry title');
|
|
314
441
|
});
|
|
315
442
|
|
|
443
|
+
test('returns visible page blocks or manifest default order from the runtime helper', async () => {
|
|
444
|
+
await act(async () => {
|
|
445
|
+
root.render(
|
|
446
|
+
<BuilderRuntimePreviewProvider initialSettings={createEmptyBuilderSettings(1)}>
|
|
447
|
+
<PageBlocksProbe />
|
|
448
|
+
</BuilderRuntimePreviewProvider>,
|
|
449
|
+
);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
expect(dom.window.document.querySelector('[data-testid="page-blocks"]')?.textContent).toBe('hero:heroproducts:products');
|
|
453
|
+
|
|
454
|
+
await act(async () => {
|
|
455
|
+
dom.window.dispatchEvent(
|
|
456
|
+
new dom.window.MessageEvent('message', {
|
|
457
|
+
origin: 'https://dashboard.shoppex.test',
|
|
458
|
+
source: parentWindow as Window,
|
|
459
|
+
data: {
|
|
460
|
+
type: 'APPLY_STATE',
|
|
461
|
+
revision: 2,
|
|
462
|
+
state: {
|
|
463
|
+
...createEmptyBuilderSettings(2),
|
|
464
|
+
theme: {
|
|
465
|
+
...createEmptyBuilderSettings(2).theme,
|
|
466
|
+
layout: {
|
|
467
|
+
home: {
|
|
468
|
+
blocks: [
|
|
469
|
+
{ id: 'hero-1', type: 'hero', visible: true, settings: {} },
|
|
470
|
+
{ id: 'faq-1', type: 'faq', visible: false, settings: {} },
|
|
471
|
+
],
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
}),
|
|
478
|
+
);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
expect(dom.window.document.querySelector('[data-testid="page-blocks"]')?.textContent).toBe('hero-1:hero');
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
test('useThemePageBlockAttributes exposes page id and first block attrs', async () => {
|
|
485
|
+
await act(async () => {
|
|
486
|
+
root.render(
|
|
487
|
+
<BuilderRuntimePreviewProvider
|
|
488
|
+
initialSettings={{
|
|
489
|
+
...createEmptyBuilderSettings(1),
|
|
490
|
+
theme: {
|
|
491
|
+
...createEmptyBuilderSettings(1).theme,
|
|
492
|
+
layout: {
|
|
493
|
+
'contact-page': {
|
|
494
|
+
blocks: [
|
|
495
|
+
{ id: 'contact-1', type: 'page-contact-page', visible: true, settings: {} },
|
|
496
|
+
],
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
}}
|
|
501
|
+
>
|
|
502
|
+
<PageBlockAttributesProbe />
|
|
503
|
+
</BuilderRuntimePreviewProvider>,
|
|
504
|
+
);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
const section = dom.window.document.querySelector('[data-testid="page-block-attrs"]');
|
|
508
|
+
expect(section?.getAttribute('data-page-id')).toBe('contact-page');
|
|
509
|
+
expect(section?.getAttribute('data-builder-block')).toBe('contact-1');
|
|
510
|
+
expect(section?.getAttribute('data-builder-block-type')).toBe('page-contact-page');
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
test('shows missing registry blocks inside trusted builder preview', async () => {
|
|
514
|
+
await act(async () => {
|
|
515
|
+
root.render(
|
|
516
|
+
<BuilderRuntimePreviewProvider initialSettings={createSettings(3, 'Registry title')}>
|
|
517
|
+
<BuilderPage pageId="home" registry={{}} context={{}} />
|
|
518
|
+
</BuilderRuntimePreviewProvider>,
|
|
519
|
+
);
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
const missingBlock = dom.window.document.querySelector('[data-builder-runtime-error="missing-block-component"]');
|
|
523
|
+
|
|
524
|
+
expect(missingBlock?.getAttribute('data-builder-block')).toBe('hero-1');
|
|
525
|
+
expect(missingBlock?.textContent).toContain('Missing Builder component for block "hero".');
|
|
526
|
+
});
|
|
527
|
+
|
|
316
528
|
test('prefers scoped block settings over global builder content inside a block provider', async () => {
|
|
317
529
|
await act(async () => {
|
|
318
530
|
root.render(
|