@designcrowd/library.brand-page 5.19.1-id-based-items-1 → 5.19.1
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/lib/src/{CreatedOnBrandCrowd-de-de-0PcGqy0N.js → CreatedOnBrandCrowd-de-de-BmeMc9oz.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-DB8wcFMI.js → CreatedOnBrandCrowd-en-us-CQNdm_6P.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-DVWQ243C.js → CreatedOnBrandCrowd-es-es-BrmHWeAM.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-D-M_L0Du.js → CreatedOnBrandCrowd-fr-ca-CaIyjTXl.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-D-M_L0Du.js → CreatedOnBrandCrowd-fr-fr-CaIyjTXl.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-CNpzknxU.js → CreatedOnBrandCrowd-pt-br-BClE0Nd9.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-CNpzknxU.js → CreatedOnBrandCrowd-pt-pt-BClE0Nd9.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-DMJoD5wE.js → CreatedOnDcom-de-de-CkrdlPqK.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-B-F3Pmjv.js → CreatedOnDcom-en-us-D33J9qky.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-DmLk-cA2.js → CreatedOnDcom-es-es-BsmInngw.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-ntnsCzvA.js → CreatedOnDcom-fr-ca-DOXlsRea.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-DFcO0gHL.js → CreatedOnDcom-fr-fr-SfFYLtyn.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-CtuJoVXW.js → CreatedOnDcom-pt-br-DcWeuZUy.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-B7shZVVn.js → CreatedOnDcom-pt-pt-DZsZZ4iX.js} +1 -1
- package/lib/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.d.ts +1 -1
- package/lib/src/brand-page/models/index.d.ts +0 -12
- package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +0 -18
- package/lib/src/index.mjs +1 -1
- package/lib/src/{lib-BQmJBVbS.js → lib-C5vGkjfs.js} +17561 -17370
- package/lib/src/metadata-builder/builders/footer-builder.js +44 -34
- package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.js +68 -57
- package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +93 -58
- package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +99 -66
- package/lib/src/metadata-builder/builders/section-builders/social-links-builder.js +17 -57
- package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +85 -51
- package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +74 -49
- package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +4 -13
- package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +48 -66
- package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +0 -2
- package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +0 -2
- package/package.json +1 -1
- package/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.ts +1 -2
- package/src/brand-page/models/index.ts +0 -12
- package/src/metadata-builder/builders/footer-builder.test.ts +0 -298
- package/src/metadata-builder/builders/footer-builder.ts +65 -56
- package/src/metadata-builder/builders/section-builders/image-gallery-builder.ts +88 -62
- package/src/metadata-builder/builders/section-builders/images-section-builder.ts +129 -66
- package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +160 -105
- package/src/metadata-builder/builders/section-builders/social-links-builder.ts +20 -65
- package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +146 -83
- package/src/metadata-builder/builders/section-builders/text-section-builder.ts +122 -70
- package/src/metadata-builder/parsers/ai-toolcall-parser.ts +4 -13
- package/src/metadata-builder/parsers/section-tool-arguments.ts +48 -66
- package/lib/src/metadata-builder/builders/item-id-utils.d.ts +0 -41
- package/lib/src/metadata-builder/builders/item-id-utils.js +0 -68
- package/src/metadata-builder/builders/item-id-utils.test.ts +0 -118
- package/src/metadata-builder/builders/item-id-utils.ts +0 -85
|
@@ -92,15 +92,10 @@ export interface ProfileSectionArguments {
|
|
|
92
92
|
background_color?: string | null;
|
|
93
93
|
}
|
|
94
94
|
export interface TextSectionItem {
|
|
95
|
-
/**
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
|
|
99
|
-
* move for existing ones). Use "__end__" for the end of the list. Omit to
|
|
100
|
-
* leave an existing item in place / append a new item. Mirrors the
|
|
101
|
-
* section-level move_before_content_id.
|
|
102
|
-
*/
|
|
103
|
-
move_before_item_id?: string;
|
|
95
|
+
/** 0-based index of existing item to update in-place */
|
|
96
|
+
item_index?: number;
|
|
97
|
+
/** 0-based position to insert a new item at */
|
|
98
|
+
insert_at?: number;
|
|
104
99
|
text?: RichTextField;
|
|
105
100
|
text_style?: TextStyleArguments;
|
|
106
101
|
}
|
|
@@ -113,8 +108,10 @@ export interface TextSectionArguments {
|
|
|
113
108
|
/** Multiple text items (BrandSite) */
|
|
114
109
|
items?: TextSectionItem[];
|
|
115
110
|
_replace_items?: boolean;
|
|
116
|
-
/**
|
|
117
|
-
|
|
111
|
+
/** 0-based indices of items to remove */
|
|
112
|
+
_remove_item_indices?: number[];
|
|
113
|
+
/** New order as array of current 0-based indices */
|
|
114
|
+
_reorder_items?: number[];
|
|
118
115
|
background_color?: string | null;
|
|
119
116
|
}
|
|
120
117
|
export interface ImageSectionArguments {
|
|
@@ -122,15 +119,10 @@ export interface ImageSectionArguments {
|
|
|
122
119
|
image_title?: string;
|
|
123
120
|
}
|
|
124
121
|
export interface ImageGalleryItem {
|
|
125
|
-
/**
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
|
|
129
|
-
* move for existing ones). Use "__end__" for the end of the list. Omit to
|
|
130
|
-
* leave an existing item in place / append a new item. Mirrors the
|
|
131
|
-
* section-level move_before_content_id.
|
|
132
|
-
*/
|
|
133
|
-
move_before_item_id?: string;
|
|
122
|
+
/** 0-based index of existing item to update in-place */
|
|
123
|
+
item_index?: number;
|
|
124
|
+
/** 0-based position to insert a new item at */
|
|
125
|
+
insert_at?: number;
|
|
134
126
|
image_url?: string;
|
|
135
127
|
caption?: string;
|
|
136
128
|
}
|
|
@@ -139,8 +131,10 @@ export interface ImageGallerySectionArguments {
|
|
|
139
131
|
gallery_type?: 'grid' | 'carousel' | 'masonry';
|
|
140
132
|
is_full_width?: boolean;
|
|
141
133
|
_replace_items?: boolean;
|
|
142
|
-
/**
|
|
143
|
-
|
|
134
|
+
/** 0-based indices of items to remove */
|
|
135
|
+
_remove_item_indices?: number[];
|
|
136
|
+
/** New order as array of current 0-based indices */
|
|
137
|
+
_reorder_items?: number[];
|
|
144
138
|
}
|
|
145
139
|
export interface GeneralLinkSectionArguments {
|
|
146
140
|
title?: string;
|
|
@@ -195,15 +189,10 @@ export interface TiktokSectionArguments {
|
|
|
195
189
|
url?: string;
|
|
196
190
|
}
|
|
197
191
|
export interface SocialLink {
|
|
198
|
-
/**
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
|
|
202
|
-
* move for existing ones). Use "__end__" for the end of the list. Omit to
|
|
203
|
-
* leave an existing item in place / append a new item. Mirrors the
|
|
204
|
-
* section-level move_before_content_id.
|
|
205
|
-
*/
|
|
206
|
-
move_before_item_id?: string;
|
|
192
|
+
/** 0-based index of existing item to update in-place */
|
|
193
|
+
item_index?: number;
|
|
194
|
+
/** 0-based position to insert a new item at */
|
|
195
|
+
insert_at?: number;
|
|
207
196
|
platform?: 'facebook' | 'x' | 'instagram' | 'youtube' | 'linkedin' | 'pinterest' | 'twitch' | 'tiktok';
|
|
208
197
|
url?: string;
|
|
209
198
|
}
|
|
@@ -215,8 +204,10 @@ export interface SocialLinksSectionArguments {
|
|
|
215
204
|
/** Style for the icon container background (used when icon_style is 'square' or 'circle'). */
|
|
216
205
|
icon_background_style?: TextStyleArguments;
|
|
217
206
|
_replace_items?: boolean;
|
|
218
|
-
/**
|
|
219
|
-
|
|
207
|
+
/** 0-based indices of items to remove */
|
|
208
|
+
_remove_item_indices?: number[];
|
|
209
|
+
/** New order as array of current 0-based indices */
|
|
210
|
+
_reorder_items?: number[];
|
|
220
211
|
}
|
|
221
212
|
export interface MailingListSectionArguments {
|
|
222
213
|
title?: RichTextField;
|
|
@@ -277,15 +268,10 @@ export interface AboutSectionArguments {
|
|
|
277
268
|
background_color?: string | null;
|
|
278
269
|
}
|
|
279
270
|
export interface ImagesSectionItem {
|
|
280
|
-
/**
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
|
|
284
|
-
* move for existing ones). Use "__end__" for the end of the list. Omit to
|
|
285
|
-
* leave an existing item in place / append a new item. Mirrors the
|
|
286
|
-
* section-level move_before_content_id.
|
|
287
|
-
*/
|
|
288
|
-
move_before_item_id?: string;
|
|
271
|
+
/** 0-based index of existing item to update in-place */
|
|
272
|
+
item_index?: number;
|
|
273
|
+
/** 0-based position to insert a new item at */
|
|
274
|
+
insert_at?: number;
|
|
289
275
|
image_url?: string;
|
|
290
276
|
heading?: string;
|
|
291
277
|
text?: string;
|
|
@@ -297,8 +283,10 @@ export interface ImagesSectionArguments {
|
|
|
297
283
|
items?: ImagesSectionItem[];
|
|
298
284
|
display_style?: 'left-aligned' | 'right-aligned' | 'centered' | 'media-only' | 'center-card-aligned-image' | 'alternate-image';
|
|
299
285
|
_replace_items?: boolean;
|
|
300
|
-
/**
|
|
301
|
-
|
|
286
|
+
/** 0-based indices of items to remove */
|
|
287
|
+
_remove_item_indices?: number[];
|
|
288
|
+
/** New order as array of current 0-based indices */
|
|
289
|
+
_reorder_items?: number[];
|
|
302
290
|
title_style?: TextStyleArguments;
|
|
303
291
|
description_style?: TextStyleArguments;
|
|
304
292
|
heading_style?: TextStyleArguments;
|
|
@@ -310,15 +298,10 @@ export interface ImagesSectionArguments {
|
|
|
310
298
|
card_style?: CardStyleArguments;
|
|
311
299
|
}
|
|
312
300
|
export interface TestimonialItem {
|
|
313
|
-
/**
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
|
|
317
|
-
* move for existing ones). Use "__end__" for the end of the list. Omit to
|
|
318
|
-
* leave an existing item in place / append a new item. Mirrors the
|
|
319
|
-
* section-level move_before_content_id.
|
|
320
|
-
*/
|
|
321
|
-
move_before_item_id?: string;
|
|
301
|
+
/** 0-based index of existing item to update in-place */
|
|
302
|
+
item_index?: number;
|
|
303
|
+
/** 0-based position to insert a new item at */
|
|
304
|
+
insert_at?: number;
|
|
322
305
|
testimonial?: string;
|
|
323
306
|
name?: string;
|
|
324
307
|
position?: string;
|
|
@@ -331,8 +314,10 @@ export interface TestimonialSectionArguments {
|
|
|
331
314
|
items?: TestimonialItem[];
|
|
332
315
|
display_style?: 'centered' | 'big-image' | 'columns';
|
|
333
316
|
_replace_items?: boolean;
|
|
334
|
-
/**
|
|
335
|
-
|
|
317
|
+
/** 0-based indices of items to remove */
|
|
318
|
+
_remove_item_indices?: number[];
|
|
319
|
+
/** New order as array of current 0-based indices */
|
|
320
|
+
_reorder_items?: number[];
|
|
336
321
|
title_style?: TextStyleArguments;
|
|
337
322
|
testimonial_style?: TextStyleArguments;
|
|
338
323
|
name_style?: TextStyleArguments;
|
|
@@ -341,15 +326,10 @@ export interface TestimonialSectionArguments {
|
|
|
341
326
|
card_style?: CardStyleArguments;
|
|
342
327
|
}
|
|
343
328
|
export interface ShapesSectionItem {
|
|
344
|
-
/**
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
|
|
348
|
-
* move for existing ones). Use "__end__" for the end of the list. Omit to
|
|
349
|
-
* leave an existing item in place / append a new item. Mirrors the
|
|
350
|
-
* section-level move_before_content_id.
|
|
351
|
-
*/
|
|
352
|
-
move_before_item_id?: string;
|
|
329
|
+
/** 0-based index of existing item to update in-place */
|
|
330
|
+
item_index?: number;
|
|
331
|
+
/** 0-based position to insert a new item at */
|
|
332
|
+
insert_at?: number;
|
|
353
333
|
icon_image_url?: string;
|
|
354
334
|
shape_url?: string;
|
|
355
335
|
color_theme?: string;
|
|
@@ -362,8 +342,10 @@ export interface ShapesSectionArguments {
|
|
|
362
342
|
items?: ShapesSectionItem[];
|
|
363
343
|
display_style?: 'left-aligned' | 'right-aligned' | 'centered' | 'media-only' | 'center-card-aligned-image' | 'alternate-image';
|
|
364
344
|
_replace_items?: boolean;
|
|
365
|
-
/**
|
|
366
|
-
|
|
345
|
+
/** 0-based indices of items to remove */
|
|
346
|
+
_remove_item_indices?: number[];
|
|
347
|
+
/** New order as array of current 0-based indices */
|
|
348
|
+
_reorder_items?: number[];
|
|
367
349
|
title_style?: TextStyleArguments;
|
|
368
350
|
description_style?: TextStyleArguments;
|
|
369
351
|
heading_style?: TextStyleArguments;
|
|
@@ -2866,7 +2866,6 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
2866
2866
|
logoImageBase64Url?: string | undefined;
|
|
2867
2867
|
socialLinks: {
|
|
2868
2868
|
url: string;
|
|
2869
|
-
id?: string | undefined;
|
|
2870
2869
|
imageUrl: string;
|
|
2871
2870
|
size?: string | undefined;
|
|
2872
2871
|
imageTitle: string;
|
|
@@ -3091,7 +3090,6 @@ declare const _default: import("vue").DefineComponent<{}, {
|
|
|
3091
3090
|
logoImageBase64Url?: string | undefined;
|
|
3092
3091
|
socialLinks: {
|
|
3093
3092
|
url: string;
|
|
3094
|
-
id?: string | undefined;
|
|
3095
3093
|
imageUrl: string;
|
|
3096
3094
|
size?: string | undefined;
|
|
3097
3095
|
imageTitle: string;
|
|
@@ -2824,7 +2824,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
2824
2824
|
logoImageBase64Url?: string | undefined;
|
|
2825
2825
|
socialLinks: {
|
|
2826
2826
|
url: string;
|
|
2827
|
-
id?: string | undefined;
|
|
2828
2827
|
imageUrl: string;
|
|
2829
2828
|
size?: string | undefined;
|
|
2830
2829
|
imageTitle: string;
|
|
@@ -3049,7 +3048,6 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
3049
3048
|
logoImageBase64Url?: string | undefined;
|
|
3050
3049
|
socialLinks: {
|
|
3051
3050
|
url: string;
|
|
3052
|
-
id?: string | undefined;
|
|
3053
3051
|
imageUrl: string;
|
|
3054
3052
|
size?: string | undefined;
|
|
3055
3053
|
imageTitle: string;
|
package/package.json
CHANGED
|
@@ -7,8 +7,7 @@ import type { ImageGalleryImageClickPayload } from '../models';
|
|
|
7
7
|
import type { BrandPageContext, ImageGalleryItem } from '../../../models';
|
|
8
8
|
import { getImageAltText } from '../../../utils';
|
|
9
9
|
|
|
10
|
-
interface IndexedImageGalleryItem extends
|
|
11
|
-
// Local rendering index, distinct from the stable string `id` on ImageItem.
|
|
10
|
+
interface IndexedImageGalleryItem extends ImageGalleryItem {
|
|
12
11
|
id: number;
|
|
13
12
|
}
|
|
14
13
|
|
|
@@ -234,13 +234,6 @@ export interface ImageContent extends Content {
|
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
export interface ImageItem {
|
|
237
|
-
/**
|
|
238
|
-
* Stable per-item identifier. Mirrors the section-level `Content.id` one
|
|
239
|
-
* level deeper so AI tool calls can reference items by id rather than by
|
|
240
|
-
* positional index. Optional for backward compatibility with persisted data
|
|
241
|
-
* that predates item ids — backfilled on load (see ensureItemIds).
|
|
242
|
-
*/
|
|
243
|
-
id?: string;
|
|
244
237
|
imageUrl: string;
|
|
245
238
|
imageTitle: string;
|
|
246
239
|
width: number;
|
|
@@ -473,8 +466,6 @@ export interface ShapeItem extends ImageItem {
|
|
|
473
466
|
}
|
|
474
467
|
|
|
475
468
|
export interface TestimonialItems {
|
|
476
|
-
/** Stable per-item identifier. See ImageItem.id. */
|
|
477
|
-
id?: string;
|
|
478
469
|
imageUrl: string;
|
|
479
470
|
name: string;
|
|
480
471
|
position: string;
|
|
@@ -492,8 +483,6 @@ export interface TestimonialItems {
|
|
|
492
483
|
}
|
|
493
484
|
|
|
494
485
|
export interface TextItem {
|
|
495
|
-
/** Stable per-item identifier. See ImageItem.id. */
|
|
496
|
-
id?: string;
|
|
497
486
|
text: string;
|
|
498
487
|
textStyle: TextStyleDeclaration;
|
|
499
488
|
|
|
@@ -563,7 +552,6 @@ export interface AboutSectionContent extends Content {
|
|
|
563
552
|
}
|
|
564
553
|
|
|
565
554
|
export interface LinksItem {
|
|
566
|
-
id?: string;
|
|
567
555
|
imageUrl: string;
|
|
568
556
|
url: string;
|
|
569
557
|
size?: string;
|
|
@@ -14,10 +14,6 @@ import { FooterBuilder } from './footer-builder';
|
|
|
14
14
|
* The fix routes copyright style writes through `applyDetachedStyleOverride`,
|
|
15
15
|
* which resolves the preset in and clears `textStyleType` so the per-element
|
|
16
16
|
* override sticks.
|
|
17
|
-
*
|
|
18
|
-
* BP-5603: social link mutations converted from index-based to id-based
|
|
19
|
-
* (item_id / move_before_item_id / _remove_social_link_ids), mirroring the
|
|
20
|
-
* section-level move_before_content_id contract one level deeper.
|
|
21
17
|
*/
|
|
22
18
|
|
|
23
19
|
const textStyleOptions = {
|
|
@@ -46,37 +42,6 @@ const makeModel = (copyrightStyle: Record<string, unknown>): BrandPageModel =>
|
|
|
46
42
|
pageFooterMobile: { copyrightStyle: { ...copyrightStyle } },
|
|
47
43
|
}) as unknown as BrandPageModel;
|
|
48
44
|
|
|
49
|
-
const makeModelWithLinks = (
|
|
50
|
-
links: { id?: string; imageUrl: string; url: string }[],
|
|
51
|
-
): BrandPageModel =>
|
|
52
|
-
({
|
|
53
|
-
styles: { textStyles: { textStyleOptions } },
|
|
54
|
-
pageFooterDesktop: {
|
|
55
|
-
copyrightStyle: {},
|
|
56
|
-
socialLinks: links.map((l) => ({
|
|
57
|
-
id: l.id,
|
|
58
|
-
imageUrl: l.imageUrl,
|
|
59
|
-
url: l.url,
|
|
60
|
-
imageTitle: '',
|
|
61
|
-
width: 0,
|
|
62
|
-
height: 0,
|
|
63
|
-
imageLink: '',
|
|
64
|
-
})),
|
|
65
|
-
},
|
|
66
|
-
pageFooterMobile: {
|
|
67
|
-
copyrightStyle: {},
|
|
68
|
-
socialLinks: links.map((l) => ({
|
|
69
|
-
id: l.id,
|
|
70
|
-
imageUrl: l.imageUrl,
|
|
71
|
-
url: l.url,
|
|
72
|
-
imageTitle: '',
|
|
73
|
-
width: 0,
|
|
74
|
-
height: 0,
|
|
75
|
-
imageLink: '',
|
|
76
|
-
})),
|
|
77
|
-
},
|
|
78
|
-
}) as unknown as BrandPageModel;
|
|
79
|
-
|
|
80
45
|
const run = (args: UpdateFooterArguments, model: BrandPageModel) =>
|
|
81
46
|
new FooterBuilder({
|
|
82
47
|
id: 'f1',
|
|
@@ -84,10 +49,6 @@ const run = (args: UpdateFooterArguments, model: BrandPageModel) =>
|
|
|
84
49
|
payload: args,
|
|
85
50
|
}).buildAsync(model);
|
|
86
51
|
|
|
87
|
-
// ---------------------------------------------------------------------------
|
|
88
|
-
// Copyright font (BP-5420)
|
|
89
|
-
// ---------------------------------------------------------------------------
|
|
90
|
-
|
|
91
52
|
describe('FooterBuilder — copyright font vs global style', () => {
|
|
92
53
|
it('detaches from the global preset: applies the font, clears textStyleType, retains preset values', async () => {
|
|
93
54
|
const model = makeModel({
|
|
@@ -113,262 +74,3 @@ describe('FooterBuilder — copyright font vs global style', () => {
|
|
|
113
74
|
expect(style.textAlign).toBe('center');
|
|
114
75
|
});
|
|
115
76
|
});
|
|
116
|
-
|
|
117
|
-
// ---------------------------------------------------------------------------
|
|
118
|
-
// Social links — id-based mutations (BP-5603)
|
|
119
|
-
// ---------------------------------------------------------------------------
|
|
120
|
-
|
|
121
|
-
describe('FooterBuilder — social links id-based mutations', () => {
|
|
122
|
-
it('backfills ids on legacy links that have none', async () => {
|
|
123
|
-
// Links without ids (legacy persisted data)
|
|
124
|
-
const model = makeModelWithLinks([
|
|
125
|
-
{ imageUrl: 'facebook', url: 'https://fb.com' },
|
|
126
|
-
{ imageUrl: 'instagram', url: 'https://ig.com' },
|
|
127
|
-
]);
|
|
128
|
-
|
|
129
|
-
const { model: out } = await run({}, model);
|
|
130
|
-
|
|
131
|
-
const links = out.pageFooterDesktop!.socialLinks;
|
|
132
|
-
|
|
133
|
-
expect(links[0].id).toBeTruthy();
|
|
134
|
-
expect(links[1].id).toBeTruthy();
|
|
135
|
-
expect(links[0].id).not.toBe(links[1].id);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('updates an existing link in place by item_id', async () => {
|
|
139
|
-
const model = makeModelWithLinks([
|
|
140
|
-
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
141
|
-
{ id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
|
|
142
|
-
]);
|
|
143
|
-
|
|
144
|
-
const { model: out } = await run(
|
|
145
|
-
{
|
|
146
|
-
social_links: [
|
|
147
|
-
{ item_id: 'link-1', url: 'https://facebook.com/mybrand' },
|
|
148
|
-
],
|
|
149
|
-
},
|
|
150
|
-
model,
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
const links = out.pageFooterDesktop!.socialLinks;
|
|
154
|
-
|
|
155
|
-
expect(links).toHaveLength(2);
|
|
156
|
-
expect(links[0].url).toBe('https://facebook.com/mybrand');
|
|
157
|
-
expect(links[0].id).toBe('link-1');
|
|
158
|
-
// untouched link unchanged
|
|
159
|
-
expect(links[1].id).toBe('link-2');
|
|
160
|
-
expect(links[1].url).toBe('https://ig.com');
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('updates social_link_icon on an existing link', async () => {
|
|
164
|
-
const model = makeModelWithLinks([
|
|
165
|
-
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
166
|
-
]);
|
|
167
|
-
|
|
168
|
-
const { model: out } = await run(
|
|
169
|
-
{
|
|
170
|
-
social_links: [{ item_id: 'link-1', social_link_icon: 'facebook-v2' }],
|
|
171
|
-
},
|
|
172
|
-
model,
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
expect(out.pageFooterDesktop!.socialLinks[0].imageUrl).toBe('facebook-v2');
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('skips unknown item_id silently', async () => {
|
|
179
|
-
const model = makeModelWithLinks([
|
|
180
|
-
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
181
|
-
]);
|
|
182
|
-
|
|
183
|
-
const { model: out } = await run(
|
|
184
|
-
{
|
|
185
|
-
social_links: [{ item_id: 'no-such-id', url: 'https://nowhere.com' }],
|
|
186
|
-
},
|
|
187
|
-
model,
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
const links = out.pageFooterDesktop!.socialLinks;
|
|
191
|
-
|
|
192
|
-
expect(links).toHaveLength(1);
|
|
193
|
-
expect(links[0].url).toBe('https://fb.com');
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('inserts a new link (no item_id) appended by default', async () => {
|
|
197
|
-
const model = makeModelWithLinks([
|
|
198
|
-
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
199
|
-
]);
|
|
200
|
-
|
|
201
|
-
const { model: out } = await run(
|
|
202
|
-
{
|
|
203
|
-
social_links: [
|
|
204
|
-
{ social_link_icon: 'instagram', url: 'https://ig.com' },
|
|
205
|
-
],
|
|
206
|
-
},
|
|
207
|
-
model,
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
const links = out.pageFooterDesktop!.socialLinks;
|
|
211
|
-
|
|
212
|
-
expect(links).toHaveLength(2);
|
|
213
|
-
expect(links[1].imageUrl).toBe('instagram');
|
|
214
|
-
expect(links[1].id).toBeTruthy();
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('inserts a new link before an anchor via move_before_item_id', async () => {
|
|
218
|
-
const model = makeModelWithLinks([
|
|
219
|
-
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
220
|
-
{ id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
|
|
221
|
-
]);
|
|
222
|
-
|
|
223
|
-
const { model: out } = await run(
|
|
224
|
-
{
|
|
225
|
-
social_links: [
|
|
226
|
-
{
|
|
227
|
-
social_link_icon: 'x',
|
|
228
|
-
url: 'https://x.com',
|
|
229
|
-
move_before_item_id: 'link-2',
|
|
230
|
-
},
|
|
231
|
-
],
|
|
232
|
-
},
|
|
233
|
-
model,
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
const links = out.pageFooterDesktop!.socialLinks;
|
|
237
|
-
|
|
238
|
-
expect(links).toHaveLength(3);
|
|
239
|
-
expect(links[0].id).toBe('link-1');
|
|
240
|
-
expect(links[1].imageUrl).toBe('x');
|
|
241
|
-
expect(links[2].id).toBe('link-2');
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it('inserts a new link at end via move_before_item_id "__end__"', async () => {
|
|
245
|
-
const model = makeModelWithLinks([
|
|
246
|
-
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
247
|
-
{ id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
|
|
248
|
-
]);
|
|
249
|
-
|
|
250
|
-
const { model: out } = await run(
|
|
251
|
-
{
|
|
252
|
-
social_links: [
|
|
253
|
-
{
|
|
254
|
-
social_link_icon: 'x',
|
|
255
|
-
url: 'https://x.com',
|
|
256
|
-
move_before_item_id: '__end__',
|
|
257
|
-
},
|
|
258
|
-
],
|
|
259
|
-
},
|
|
260
|
-
model,
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
const links = out.pageFooterDesktop!.socialLinks;
|
|
264
|
-
|
|
265
|
-
expect(links).toHaveLength(3);
|
|
266
|
-
expect(links[2].imageUrl).toBe('x');
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
it('moves an existing link before an anchor via move_before_item_id', async () => {
|
|
270
|
-
const model = makeModelWithLinks([
|
|
271
|
-
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
272
|
-
{ id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
|
|
273
|
-
{ id: 'link-3', imageUrl: 'x', url: 'https://x.com' },
|
|
274
|
-
]);
|
|
275
|
-
|
|
276
|
-
const { model: out } = await run(
|
|
277
|
-
{
|
|
278
|
-
social_links: [{ item_id: 'link-3', move_before_item_id: 'link-1' }],
|
|
279
|
-
},
|
|
280
|
-
model,
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
const links = out.pageFooterDesktop!.socialLinks;
|
|
284
|
-
|
|
285
|
-
expect(links.map((l) => l.id)).toEqual(['link-3', 'link-1', 'link-2']);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it('removes links by id', async () => {
|
|
289
|
-
const model = makeModelWithLinks([
|
|
290
|
-
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
291
|
-
{ id: 'link-2', imageUrl: 'instagram', url: 'https://ig.com' },
|
|
292
|
-
{ id: 'link-3', imageUrl: 'x', url: 'https://x.com' },
|
|
293
|
-
]);
|
|
294
|
-
|
|
295
|
-
const { model: out } = await run(
|
|
296
|
-
{ _remove_social_link_ids: ['link-1', 'link-3'] },
|
|
297
|
-
model,
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
const links = out.pageFooterDesktop!.socialLinks;
|
|
301
|
-
|
|
302
|
-
expect(links).toHaveLength(1);
|
|
303
|
-
expect(links[0].id).toBe('link-2');
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
it('_replace_social_links rebuilds the entire list and assigns fresh ids', async () => {
|
|
307
|
-
const model = makeModelWithLinks([
|
|
308
|
-
{ id: 'old-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
309
|
-
]);
|
|
310
|
-
|
|
311
|
-
const { model: out } = await run(
|
|
312
|
-
{
|
|
313
|
-
_replace_social_links: true,
|
|
314
|
-
social_links: [
|
|
315
|
-
{ social_link_icon: 'instagram', url: 'https://ig.com' },
|
|
316
|
-
{ social_link_icon: 'x', url: 'https://x.com' },
|
|
317
|
-
],
|
|
318
|
-
},
|
|
319
|
-
model,
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
const links = out.pageFooterDesktop!.socialLinks;
|
|
323
|
-
|
|
324
|
-
expect(links).toHaveLength(2);
|
|
325
|
-
expect(links[0].imageUrl).toBe('instagram');
|
|
326
|
-
expect(links[1].imageUrl).toBe('x');
|
|
327
|
-
// fresh ids assigned, not the old one
|
|
328
|
-
expect(links[0].id).toBeTruthy();
|
|
329
|
-
expect(links[0].id).not.toBe('old-1');
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
it('_replace_social_links with empty social_links clears all', async () => {
|
|
333
|
-
const model = makeModelWithLinks([
|
|
334
|
-
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
335
|
-
]);
|
|
336
|
-
|
|
337
|
-
const { model: out } = await run({ _replace_social_links: true }, model);
|
|
338
|
-
|
|
339
|
-
expect(out.pageFooterDesktop!.socialLinks).toHaveLength(0);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it('normalises mailto: and tel: prefixes on new links', async () => {
|
|
343
|
-
const model = makeModelWithLinks([]);
|
|
344
|
-
|
|
345
|
-
const { model: out } = await run(
|
|
346
|
-
{
|
|
347
|
-
social_links: [
|
|
348
|
-
{ social_link_icon: 'email', url: 'mailto:me@example.com' },
|
|
349
|
-
{ social_link_icon: 'phone', url: 'tel:+1234567890' },
|
|
350
|
-
],
|
|
351
|
-
},
|
|
352
|
-
model,
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
const links = out.pageFooterDesktop!.socialLinks;
|
|
356
|
-
|
|
357
|
-
expect(links[0].url).toBe('me@example.com');
|
|
358
|
-
expect(links[1].url).toBe('+1234567890');
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
it('applies mutations to both desktop and mobile footers', async () => {
|
|
362
|
-
const model = makeModelWithLinks([
|
|
363
|
-
{ id: 'link-1', imageUrl: 'facebook', url: 'https://fb.com' },
|
|
364
|
-
]);
|
|
365
|
-
|
|
366
|
-
const { model: out } = await run(
|
|
367
|
-
{ _remove_social_link_ids: ['link-1'] },
|
|
368
|
-
model,
|
|
369
|
-
);
|
|
370
|
-
|
|
371
|
-
expect(out.pageFooterDesktop!.socialLinks).toHaveLength(0);
|
|
372
|
-
expect(out.pageFooterMobile!.socialLinks).toHaveLength(0);
|
|
373
|
-
});
|
|
374
|
-
});
|