@designcrowd/library.brand-page 6.1.10 → 6.1.11
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-nsMF2NQ9.js → CreatedOnBrandCrowd-de-de-DToCz9Tp.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-BQI_UCR7.js → CreatedOnBrandCrowd-en-us-CrjiEGmM.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-DgNOoSPn.js → CreatedOnBrandCrowd-es-es-DgVVM-Df.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-JFRaxjvx.js → CreatedOnBrandCrowd-fr-ca-C_oKH8iu.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-JFRaxjvx.js → CreatedOnBrandCrowd-fr-fr-C_oKH8iu.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-DP6_-90i.js → CreatedOnBrandCrowd-pt-br-DRvHblvI.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-DP6_-90i.js → CreatedOnBrandCrowd-pt-pt-DRvHblvI.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-07F2wjkk.js → CreatedOnDcom-de-de-uCIRFQYA.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-DaV9sa6e.js → CreatedOnDcom-en-us-DfbhIoId.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-73uOymvv.js → CreatedOnDcom-es-es-CipmOU1-.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-Ci9Ome30.js → CreatedOnDcom-fr-ca-B5Cq9iJK.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-DLHuMHfE.js → CreatedOnDcom-fr-fr-DIvycPcx.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-Fbt7tY-E.js → CreatedOnDcom-pt-br-BQ3EajZC.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-V2jTB6of.js → CreatedOnDcom-pt-pt-DchaL_tP.js} +1 -1
- package/lib/src/brand-page/constants/index.d.ts +1 -0
- package/lib/src/brand-page/constants/index.js +1 -0
- package/lib/src/brand-page/constants/sections/header-display.constants.d.ts +2 -1
- package/lib/src/brand-page/constants/sections/header-display.constants.js +24 -24
- package/lib/src/brand-page/constants/sections/navigation-type.constants.d.ts +9 -0
- package/lib/src/brand-page/constants/sections/navigation-type.constants.js +33 -0
- package/lib/src/brand-page/models/index.d.ts +1 -1
- package/lib/src/brand-page/models/index.js +6 -6
- package/lib/src/brand-page/utils/container-text-color.util.d.ts +7 -4
- package/lib/src/brand-page/utils/container-text-color.util.js +11 -6
- package/lib/src/brand-page/utils/migration/migration-21.js +62 -0
- package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +1 -0
- package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +1 -0
- package/lib/src/brand-page-maker/models/navigation-style-configs.d.ts +1 -0
- package/lib/src/brand-page-maker/models/navigation-style-configs.js +31 -7
- package/lib/src/{bundled-translations-C98dISr1.js → bundled-translations-BiFAlQdC.js} +21 -21
- package/lib/src/{bundled-translations.de-DE-D8ul_G4P.js → bundled-translations.de-DE-DuRWnOP-.js} +14 -14
- package/lib/src/{bundled-translations.es-ES-BVmPyfw9.js → bundled-translations.es-ES-CokHdZtW.js} +17 -17
- package/lib/src/{bundled-translations.fr-CA-DmzKPhfa.js → bundled-translations.fr-CA-CJcMmubA.js} +1 -1
- package/lib/src/{bundled-translations.fr-FR-CQRiASGY.js → bundled-translations.fr-FR-DYPup8fJ.js} +1 -1
- package/lib/src/{bundled-translations.pt-BR-guDO9Yfg.js → bundled-translations.pt-BR-GSsp1kuV.js} +1 -1
- package/lib/src/{bundled-translations.pt-PT-GFpLNo-u.js → bundled-translations.pt-PT-DbkY8UQ3.js} +9 -9
- package/lib/src/bundles/bundled-translations.de-DE.json +5 -0
- package/lib/src/bundles/bundled-translations.es-ES.json +5 -0
- package/lib/src/bundles/bundled-translations.fr-CA.json +5 -0
- package/lib/src/bundles/bundled-translations.fr-FR.json +5 -0
- package/lib/src/bundles/bundled-translations.json +5 -0
- package/lib/src/bundles/bundled-translations.pt-BR.json +5 -0
- package/lib/src/bundles/bundled-translations.pt-PT.json +5 -0
- package/lib/src/css/library.brand-page-brandCrowd.min.css +185 -63
- package/lib/src/css/library.brand-page-brandPage.min.css +190 -63
- package/lib/src/css/library.brand-page-designCom.min.css +185 -63
- package/lib/src/css/library.brand-page-preview.min.css +185 -63
- package/lib/src/index.mjs +1 -1
- package/lib/src/{lib-D2a1EmRb.js → lib-zpGnKGKw.js} +32747 -32191
- package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +1 -0
- package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +1 -0
- package/lib/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.d.ts +1 -0
- package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +1 -0
- package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +4 -1
- package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.js +15 -1
- package/lib/src/shared/page-settings/store/page-settings-store.d.ts +6 -0
- package/lib/src/shared/page-settings/store/page-settings-store.js +22 -1
- package/package.json +2 -2
- package/src/brand-page/constants/index.ts +1 -0
- package/src/brand-page/constants/sections/header-display.constants.ts +26 -25
- package/src/brand-page/constants/sections/navigation-type.constants.test.ts +53 -0
- package/src/brand-page/constants/sections/navigation-type.constants.ts +48 -0
- package/src/brand-page/models/footer-icon-color.test.ts +38 -102
- package/src/brand-page/models/index.ts +6 -9
- package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +6 -6
- package/src/brand-page/utils/__snapshots__/migration2.util.test.ts.snap +2 -6
- package/src/brand-page/utils/__snapshots__/model.util.test.ts.snap +4 -12
- package/src/brand-page/utils/container-text-color.util.test.ts +16 -0
- package/src/brand-page/utils/container-text-color.util.ts +15 -6
- package/src/brand-page/utils/migration/migration-21.test.ts +169 -0
- package/src/brand-page/utils/migration/migration-21.ts +75 -0
- package/src/brand-page-maker/models/i18n/models.de-DE.json +3 -0
- package/src/brand-page-maker/models/i18n/models.es-ES.json +3 -0
- package/src/brand-page-maker/models/i18n/models.fr-CA.json +3 -0
- package/src/brand-page-maker/models/i18n/models.fr-FR.json +3 -0
- package/src/brand-page-maker/models/i18n/models.json +3 -0
- package/src/brand-page-maker/models/i18n/models.pt-BR.json +3 -0
- package/src/brand-page-maker/models/i18n/models.pt-PT.json +3 -0
- package/src/brand-page-maker/models/navigation-style-configs.ts +32 -6
- package/src/bundles/bundled-translations.de-DE.json +5 -0
- package/src/bundles/bundled-translations.es-ES.json +5 -0
- package/src/bundles/bundled-translations.fr-CA.json +5 -0
- package/src/bundles/bundled-translations.fr-FR.json +5 -0
- package/src/bundles/bundled-translations.json +5 -0
- package/src/bundles/bundled-translations.pt-BR.json +5 -0
- package/src/bundles/bundled-translations.pt-PT.json +5 -0
- package/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.vue +0 -6
- package/src/shared/page-settings/components/header-settings-panel/HeaderSettingsPanel.vue +8 -0
- package/src/shared/page-settings/components/page-settings-section-styles/HeaderNavigationStyle.vue +95 -0
- package/src/shared/page-settings/components/page-settings-section-styles/HeaderNavigationType.vue +26 -14
- package/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.ts +33 -1
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.de-DE.json +10 -7
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.es-ES.json +10 -7
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.fr-CA.json +10 -7
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.fr-FR.json +10 -7
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.json +3 -0
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.pt-BR.json +10 -7
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.pt-PT.json +10 -7
- package/src/shared/page-settings/store/page-settings-store.test.ts +97 -0
- package/src/shared/page-settings/store/page-settings-store.ts +36 -1
|
@@ -1,142 +1,78 @@
|
|
|
1
1
|
import { describe, test, expect } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
BrandPageModel,
|
|
4
|
-
PageFooterDesktop,
|
|
5
|
-
PageFooterMobile,
|
|
6
|
-
type Theme,
|
|
7
|
-
} from './index';
|
|
2
|
+
import { BrandPageModel, PageFooterDesktop, PageFooterMobile } from './index';
|
|
8
3
|
import {
|
|
9
4
|
migrateBrandPageModel,
|
|
10
5
|
GetLatestModelVersion,
|
|
11
6
|
} from '../utils/migration.util';
|
|
12
|
-
import { STYLE_THEME_KEYS } from '../constants';
|
|
13
|
-
|
|
14
|
-
// BP-5886 (colour-v2): the footer social-icon colour must be wired to the same
|
|
15
|
-
// logo/global colour source as every other surface. It is seeded from the theme
|
|
16
|
-
// at construction (like the header's nav hamburger icon), then resolved /
|
|
17
|
-
// referencified downstream. See docs/plans/feat-bp-5886-*.
|
|
18
|
-
describe('footer icon colour is wired to the theme', () => {
|
|
19
|
-
// The footer background is transparent, so the icons sit on the page
|
|
20
|
-
// background (logo-background / secondaryColor); they must therefore take the
|
|
21
|
-
// contrasting logo-text colour (primaryColor) to stay legible.
|
|
22
|
-
const theme: Theme = {
|
|
23
|
-
primaryColor: STYLE_THEME_KEYS.LogoTextColor,
|
|
24
|
-
secondaryColor: STYLE_THEME_KEYS.LogoBackgroundColor,
|
|
25
|
-
};
|
|
26
7
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test('stays empty when no theme is passed (non-theme call sites unchanged)', () => {
|
|
8
|
+
// The footer social icons carry no fill in their SVGs, so an unstyled footer must not
|
|
9
|
+
// inherit a site text colour (which need not read on the footer band). The constructor
|
|
10
|
+
// leaves the icon unset; the fallback is decided downstream — migration-21 bakes an
|
|
11
|
+
// explicit black, and a not-yet-migrated model renders the SVG default. An explicit
|
|
12
|
+
// persisted colour always wins.
|
|
13
|
+
describe('footer icon colour fallback', () => {
|
|
14
|
+
describe('PageFooter* constructor', () => {
|
|
15
|
+
test('leaves iconStyle unset (no colour seed)', () => {
|
|
38
16
|
expect(new PageFooterDesktop().iconStyle).toEqual({});
|
|
39
17
|
expect(new PageFooterMobile().iconStyle).toEqual({});
|
|
40
18
|
});
|
|
41
19
|
|
|
42
|
-
test('repaints an empty persisted iconStyle', () => {
|
|
43
|
-
const footer = new PageFooterDesktop({ iconStyle: {} }, theme);
|
|
44
|
-
|
|
45
|
-
expect(footer.iconStyle).toEqual({
|
|
46
|
-
fill: STYLE_THEME_KEYS.LogoTextColor,
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
20
|
test('preserves an explicit persisted icon colour', () => {
|
|
51
|
-
|
|
52
|
-
{ iconStyle: { fill: '#abcdef' } }
|
|
53
|
-
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
expect(footer.iconStyle.fill).toBe('#abcdef');
|
|
21
|
+
expect(
|
|
22
|
+
new PageFooterDesktop({ iconStyle: { fill: '#abcdef' } }).iconStyle
|
|
23
|
+
.fill,
|
|
24
|
+
).toBe('#abcdef');
|
|
57
25
|
});
|
|
58
26
|
});
|
|
59
27
|
|
|
60
|
-
describe('
|
|
61
|
-
test('
|
|
62
|
-
// theme.primaryColor is derived from styles.textStyles.style.color, which
|
|
63
|
-
// defaults to LogoTextColor (TextStyles.GetDefaultTextStyle).
|
|
64
|
-
const model = new BrandPageModel({});
|
|
65
|
-
|
|
66
|
-
expect(model.pageFooterDesktop.iconStyle.fill).toBe(
|
|
67
|
-
STYLE_THEME_KEYS.LogoTextColor,
|
|
68
|
-
);
|
|
69
|
-
expect(model.pageFooterMobile.iconStyle.fill).toBe(
|
|
70
|
-
STYLE_THEME_KEYS.LogoTextColor,
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe('end-to-end through migration', () => {
|
|
76
|
-
test('the seeded footer icon token is referencified to a palette reference', async () => {
|
|
28
|
+
describe('through migration', () => {
|
|
29
|
+
test('an unset footer icon is baked to a literal black fallback', async () => {
|
|
77
30
|
// Fresh model is v=0, so it runs the full migration chain incl. migration-21.
|
|
78
31
|
const { brandPageModel } = await migrateBrandPageModel(
|
|
79
32
|
new BrandPageModel({}),
|
|
80
33
|
{ isUserDraft: false, isServerRender: false },
|
|
81
34
|
);
|
|
82
35
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// It was seeded (not empty) and got referencified off the raw token to a
|
|
86
|
-
// well-formed palette reference.
|
|
87
|
-
expect(fill).not.toBe(STYLE_THEME_KEYS.LogoBackgroundColor);
|
|
88
|
-
expect(fill).toMatch(/^var\(--palette-\d+\)$/);
|
|
36
|
+
expect(brandPageModel.pageFooterDesktop.iconStyle.fill).toBe('#000000');
|
|
37
|
+
expect(brandPageModel.pageFooterMobile.iconStyle.fill).toBe('#000000');
|
|
89
38
|
});
|
|
90
39
|
|
|
91
|
-
test('an
|
|
92
|
-
// An existing colour-v2 page is at the latest version with an already-
|
|
93
|
-
// referencified text colour. The footer icon inherits that value via
|
|
94
|
-
// theme.primaryColor, so it is render-ready without migration-21 re-running.
|
|
40
|
+
test('an explicit footer icon colour is preserved (referencified, not blacked out)', async () => {
|
|
95
41
|
const persisted = {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
},
|
|
100
|
-
pageFooterDesktop: { iconStyle: {} },
|
|
42
|
+
styles: {},
|
|
43
|
+
pages: [],
|
|
44
|
+
pageFooterDesktop: { iconStyle: { fill: '#abcdef' } },
|
|
101
45
|
pageFooterMobile: { iconStyle: {} },
|
|
102
46
|
};
|
|
103
47
|
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const { brandPageModel } = await migrateBrandPageModel(model, {
|
|
109
|
-
isUserDraft: false,
|
|
110
|
-
isServerRender: false,
|
|
111
|
-
});
|
|
48
|
+
const { brandPageModel } = await migrateBrandPageModel(
|
|
49
|
+
new BrandPageModel(persisted),
|
|
50
|
+
{ isUserDraft: false, isServerRender: false },
|
|
51
|
+
);
|
|
112
52
|
|
|
113
|
-
//
|
|
114
|
-
expect(brandPageModel.pageFooterDesktop.iconStyle.fill).
|
|
115
|
-
|
|
53
|
+
// The explicit colour rode pass B into a palette reference; the empty one took black.
|
|
54
|
+
expect(brandPageModel.pageFooterDesktop.iconStyle.fill).toMatch(
|
|
55
|
+
/^var\(--palette-/,
|
|
116
56
|
);
|
|
57
|
+
expect(brandPageModel.pageFooterMobile.iconStyle.fill).toBe('#000000');
|
|
117
58
|
});
|
|
118
59
|
|
|
119
|
-
test('
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// theme-seeded (like the legacy header nav) so the seed rides the re-wrap
|
|
123
|
-
// and migration-21 referencifies it — otherwise legacy sites keep empty
|
|
124
|
-
// footer icons after full migration.
|
|
125
|
-
const legacy = {
|
|
60
|
+
test('an already-migrated page is left untouched (version gate skips the bake)', async () => {
|
|
61
|
+
const persisted = {
|
|
62
|
+
v: GetLatestModelVersion(),
|
|
126
63
|
styles: {},
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
footerStyles: { iconStyle: {} },
|
|
64
|
+
pageFooterDesktop: { iconStyle: {} },
|
|
65
|
+
pageFooterMobile: { iconStyle: {} },
|
|
130
66
|
};
|
|
131
67
|
|
|
132
68
|
const { brandPageModel } = await migrateBrandPageModel(
|
|
133
|
-
new BrandPageModel(
|
|
69
|
+
new BrandPageModel(persisted),
|
|
134
70
|
{ isUserDraft: false, isServerRender: false },
|
|
135
71
|
);
|
|
136
72
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
);
|
|
73
|
+
// migration-21 does not re-run, so the empty icon is left for the SVG default.
|
|
74
|
+
expect(brandPageModel.pageFooterDesktop.iconStyle).toEqual({});
|
|
75
|
+
expect(brandPageModel.pageFooterMobile.iconStyle).toEqual({});
|
|
140
76
|
});
|
|
141
77
|
});
|
|
142
78
|
});
|
|
@@ -849,7 +849,7 @@ export abstract class AbstractPageFooterStyles {
|
|
|
849
849
|
|
|
850
850
|
protected constructor(
|
|
851
851
|
options: AbstractPageFooterStylesType = {},
|
|
852
|
-
|
|
852
|
+
_theme?: Theme,
|
|
853
853
|
) {
|
|
854
854
|
if (typeof options.viewport === 'undefined') {
|
|
855
855
|
throw new Error('"viewport" is required');
|
|
@@ -920,14 +920,11 @@ export abstract class AbstractPageFooterStyles {
|
|
|
920
920
|
imageLink: '',
|
|
921
921
|
},
|
|
922
922
|
];
|
|
923
|
-
//
|
|
924
|
-
//
|
|
925
|
-
//
|
|
926
|
-
//
|
|
927
|
-
this.iconStyle =
|
|
928
|
-
theme?.primaryColor ? { fill: theme.primaryColor } : {},
|
|
929
|
-
options.iconStyle || {},
|
|
930
|
-
);
|
|
923
|
+
// No colour seed: the social icons carry no fill of their own, so an unstyled footer
|
|
924
|
+
// leaves them unset and the fallback is decided downstream — migration-21 bakes an
|
|
925
|
+
// explicit black, a not-yet-migrated model renders the SVG default. Seeding a site
|
|
926
|
+
// text colour here need not read against the footer band.
|
|
927
|
+
this.iconStyle = options.iconStyle ? { ...options.iconStyle } : {};
|
|
931
928
|
this.textFontFamily = options.textFontFamily;
|
|
932
929
|
}
|
|
933
930
|
}
|
|
@@ -41,7 +41,7 @@ exports[`migration.util > Banner Contents - text Align and Font Family should no
|
|
|
41
41
|
"copyrightText": "© 2026 by [Company Name]. All Rights Reserved.",
|
|
42
42
|
"displayStyle": "centered-menu",
|
|
43
43
|
"iconStyle": {
|
|
44
|
-
"fill": "
|
|
44
|
+
"fill": "#000000",
|
|
45
45
|
},
|
|
46
46
|
"isLogoOptional": true,
|
|
47
47
|
"isLogoVisible": true,
|
|
@@ -101,7 +101,7 @@ exports[`migration.util > Banner Contents - text Align and Font Family should no
|
|
|
101
101
|
"copyrightText": "© 2026 by [Company Name]. All Rights Reserved.",
|
|
102
102
|
"displayStyle": "centered-menu",
|
|
103
103
|
"iconStyle": {
|
|
104
|
-
"fill": "
|
|
104
|
+
"fill": "#000000",
|
|
105
105
|
},
|
|
106
106
|
"isLogoOptional": true,
|
|
107
107
|
"isLogoVisible": true,
|
|
@@ -534,7 +534,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
|
|
|
534
534
|
"copyrightText": "© 2026 by [Company Name]. All Rights Reserved.",
|
|
535
535
|
"displayStyle": "centered-menu",
|
|
536
536
|
"iconStyle": {
|
|
537
|
-
"fill": "
|
|
537
|
+
"fill": "#000000",
|
|
538
538
|
},
|
|
539
539
|
"isLogoOptional": true,
|
|
540
540
|
"isLogoVisible": true,
|
|
@@ -594,7 +594,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
|
|
|
594
594
|
"copyrightText": "© 2026 by [Company Name]. All Rights Reserved.",
|
|
595
595
|
"displayStyle": "centered-menu",
|
|
596
596
|
"iconStyle": {
|
|
597
|
-
"fill": "
|
|
597
|
+
"fill": "#000000",
|
|
598
598
|
},
|
|
599
599
|
"isLogoOptional": true,
|
|
600
600
|
"isLogoVisible": true,
|
|
@@ -1011,7 +1011,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
|
|
|
1011
1011
|
"copyrightText": "© 2026 by [Company Name]. All Rights Reserved.",
|
|
1012
1012
|
"displayStyle": "centered-menu",
|
|
1013
1013
|
"iconStyle": {
|
|
1014
|
-
"fill": "
|
|
1014
|
+
"fill": "#000000",
|
|
1015
1015
|
},
|
|
1016
1016
|
"isLogoOptional": true,
|
|
1017
1017
|
"isLogoVisible": true,
|
|
@@ -1071,7 +1071,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
|
|
|
1071
1071
|
"copyrightText": "© 2026 by [Company Name]. All Rights Reserved.",
|
|
1072
1072
|
"displayStyle": "centered-menu",
|
|
1073
1073
|
"iconStyle": {
|
|
1074
|
-
"fill": "
|
|
1074
|
+
"fill": "#000000",
|
|
1075
1075
|
},
|
|
1076
1076
|
"isLogoOptional": true,
|
|
1077
1077
|
"isLogoVisible": true,
|
|
@@ -26,9 +26,7 @@ exports[`migration02.ts > migration02 should return converted letterspacing 1`]
|
|
|
26
26
|
},
|
|
27
27
|
"copyrightText": "© 2026 by [Company Name]. All Rights Reserved.",
|
|
28
28
|
"displayStyle": "centered-menu",
|
|
29
|
-
"iconStyle": {
|
|
30
|
-
"fill": "logo-text-color",
|
|
31
|
-
},
|
|
29
|
+
"iconStyle": {},
|
|
32
30
|
"isLogoOptional": true,
|
|
33
31
|
"isLogoVisible": true,
|
|
34
32
|
"logoDraftToken": "",
|
|
@@ -87,9 +85,7 @@ exports[`migration02.ts > migration02 should return converted letterspacing 1`]
|
|
|
87
85
|
},
|
|
88
86
|
"copyrightText": "© 2026 by [Company Name]. All Rights Reserved.",
|
|
89
87
|
"displayStyle": "centered-menu",
|
|
90
|
-
"iconStyle": {
|
|
91
|
-
"fill": "logo-text-color",
|
|
92
|
-
},
|
|
88
|
+
"iconStyle": {},
|
|
93
89
|
"isLogoOptional": true,
|
|
94
90
|
"isLogoVisible": true,
|
|
95
91
|
"logoDraftToken": "",
|
|
@@ -25,9 +25,7 @@ BrandPageModel {
|
|
|
25
25
|
},
|
|
26
26
|
"copyrightText": "© 2026 by [Company Name]. All Rights Reserved.",
|
|
27
27
|
"displayStyle": "centered-menu",
|
|
28
|
-
"iconStyle": {
|
|
29
|
-
"fill": "logo-text-color",
|
|
30
|
-
},
|
|
28
|
+
"iconStyle": {},
|
|
31
29
|
"isLogoOptional": true,
|
|
32
30
|
"isLogoVisible": true,
|
|
33
31
|
"logoDraftToken": "",
|
|
@@ -86,9 +84,7 @@ BrandPageModel {
|
|
|
86
84
|
},
|
|
87
85
|
"copyrightText": "© 2026 by [Company Name]. All Rights Reserved.",
|
|
88
86
|
"displayStyle": "centered-menu",
|
|
89
|
-
"iconStyle": {
|
|
90
|
-
"fill": "logo-text-color",
|
|
91
|
-
},
|
|
87
|
+
"iconStyle": {},
|
|
92
88
|
"isLogoOptional": true,
|
|
93
89
|
"isLogoVisible": true,
|
|
94
90
|
"logoDraftToken": "",
|
|
@@ -486,9 +482,7 @@ BrandPageModel {
|
|
|
486
482
|
},
|
|
487
483
|
"copyrightText": "© 2026 by [Company Name]. All Rights Reserved.",
|
|
488
484
|
"displayStyle": "centered-menu",
|
|
489
|
-
"iconStyle": {
|
|
490
|
-
"fill": "logo-text-color",
|
|
491
|
-
},
|
|
485
|
+
"iconStyle": {},
|
|
492
486
|
"isLogoOptional": true,
|
|
493
487
|
"isLogoVisible": true,
|
|
494
488
|
"logoDraftToken": "",
|
|
@@ -547,9 +541,7 @@ BrandPageModel {
|
|
|
547
541
|
},
|
|
548
542
|
"copyrightText": "© 2026 by [Company Name]. All Rights Reserved.",
|
|
549
543
|
"displayStyle": "centered-menu",
|
|
550
|
-
"iconStyle": {
|
|
551
|
-
"fill": "logo-text-color",
|
|
552
|
-
},
|
|
544
|
+
"iconStyle": {},
|
|
553
545
|
"isLogoOptional": true,
|
|
554
546
|
"isLogoVisible": true,
|
|
555
547
|
"logoDraftToken": "",
|
|
@@ -323,6 +323,22 @@ describe('rteFrameFallbackColour', () => {
|
|
|
323
323
|
).toBeUndefined();
|
|
324
324
|
});
|
|
325
325
|
|
|
326
|
+
// The maker's default text/profile background is the literal `'transparent'`, which the
|
|
327
|
+
// PRE-V2 renderer framed (its gate was `!isHexTransparent`, and that only excludes an
|
|
328
|
+
// alpha-00 hex) — so the RTE inherited the button colour. The bake must reproduce it, or
|
|
329
|
+
// those sites silently recolour from the button colour to the root paint.
|
|
330
|
+
test.each(['text', 'profile'] as const)(
|
|
331
|
+
'returns the button text colour for a %s section with a literal `transparent` background',
|
|
332
|
+
(type) => {
|
|
333
|
+
expect(
|
|
334
|
+
rteFrameFallbackColour(
|
|
335
|
+
framed(type, { backgroundColor: 'transparent' }),
|
|
336
|
+
stylesWith({ backgroundColor: BUTTON_FILL, color: BUTTON_TEXT }),
|
|
337
|
+
),
|
|
338
|
+
).toBe(BUTTON_TEXT);
|
|
339
|
+
},
|
|
340
|
+
);
|
|
341
|
+
|
|
326
342
|
// --- container-bound blocks: always framed, whatever the card colour ---
|
|
327
343
|
test('mailing-list on a CUSTOM (non-button-fill) card: still the button text colour', () => {
|
|
328
344
|
expect(
|
|
@@ -17,7 +17,11 @@ import type {
|
|
|
17
17
|
} from '../models';
|
|
18
18
|
import { BRAND_PAGE_TYPES } from '../models';
|
|
19
19
|
import type { ContentTextStylePropertyKey } from '../constants';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
backgroundColorFromStyle,
|
|
22
|
+
isTransparentColour,
|
|
23
|
+
isHexTransparent,
|
|
24
|
+
} from './color.util';
|
|
21
25
|
import {
|
|
22
26
|
resolveColorReference,
|
|
23
27
|
bestContrastBrandRole,
|
|
@@ -253,10 +257,13 @@ const OWN_BACKGROUND_FRAME_TYPES: ReadonlySet<Content['type']> = new Set([
|
|
|
253
257
|
* than silently recolouring published sites to the linked global — the exact trap ADR 0014
|
|
254
258
|
* names, here applied to the migration bake (ADR 0015).
|
|
255
259
|
*
|
|
256
|
-
* Two frame families
|
|
257
|
-
* - **text / profile** — framed when the content has its own
|
|
258
|
-
* the
|
|
259
|
-
*
|
|
260
|
+
* Two frame families:
|
|
261
|
+
* - **text / profile** — framed when the content has its own painted background. The gate
|
|
262
|
+
* deliberately mirrors the PRE-V2 renderer (`!isHexTransparent`), NOT the current
|
|
263
|
+
* view-model gate (`!isTransparentColour`): the legacy renderer framed the literal
|
|
264
|
+
* `'transparent'` (the maker's default background) too — only an alpha-`00` hex was
|
|
265
|
+
* unframed — so its empty-mark RTE inherited the button colour. This bake must reproduce
|
|
266
|
+
* that, or every default text/profile section silently recolours to the root paint.
|
|
260
267
|
* - **the container-bound blocks** (mailing-list, request-payment, support-me, non-brandSite
|
|
261
268
|
* contact-form) — always framed; `containerSurfaceForContent` is the existing oracle for
|
|
262
269
|
* "does this block paint a frame" (and encodes the brandSite contact-form carve-out), and
|
|
@@ -286,7 +293,9 @@ export const rteFrameFallbackColour = (
|
|
|
286
293
|
).backgroundColor;
|
|
287
294
|
const reference = background?.background || background?.backgroundColor;
|
|
288
295
|
|
|
289
|
-
|
|
296
|
+
// `isHexTransparent`, not `isTransparentColour`: the pre-V2 renderer framed the literal
|
|
297
|
+
// `'transparent'` (only an alpha-00 hex was unframed), so the bake must too — see docblock.
|
|
298
|
+
return reference && !isHexTransparent(reference)
|
|
290
299
|
? buttonTextColour
|
|
291
300
|
: undefined;
|
|
292
301
|
}
|
|
@@ -2206,6 +2206,107 @@ describe('migration21 — step FINAL+1: bake resolved colour into inherit-only R
|
|
|
2206
2206
|
);
|
|
2207
2207
|
});
|
|
2208
2208
|
|
|
2209
|
+
// The maker's DEFAULT text/profile background is the literal `'transparent'`. The pre-V2
|
|
2210
|
+
// renderer framed it (its gate was `!isHexTransparent`, which excludes only an alpha-00
|
|
2211
|
+
// hex), so the empty-mark RTE inherited the button colour — not the root paint. The bake
|
|
2212
|
+
// must reproduce that or every default text section silently recolours (BP-5886 regression).
|
|
2213
|
+
test('brandPage text section with a LITERAL `transparent` background: bakes to the frame button-text colour, not the paragraph global', async () => {
|
|
2214
|
+
const model = {
|
|
2215
|
+
styles: bakeStyles(),
|
|
2216
|
+
brandPageType: 'default',
|
|
2217
|
+
pages: [
|
|
2218
|
+
{
|
|
2219
|
+
contents: [
|
|
2220
|
+
{
|
|
2221
|
+
type: 'text',
|
|
2222
|
+
backgroundColor: { backgroundColor: 'transparent' },
|
|
2223
|
+
text: rteDoc(''),
|
|
2224
|
+
textStyle: { textStyleType: 'paragraphs' },
|
|
2225
|
+
},
|
|
2226
|
+
],
|
|
2227
|
+
},
|
|
2228
|
+
],
|
|
2229
|
+
};
|
|
2230
|
+
|
|
2231
|
+
const { brandPageModel } = await migration21(
|
|
2232
|
+
createMigrationData(model),
|
|
2233
|
+
OPTIONS_NO_LOGO,
|
|
2234
|
+
);
|
|
2235
|
+
|
|
2236
|
+
const content = brandPageModel.pages[0].contents[0] as any;
|
|
2237
|
+
|
|
2238
|
+
expect(rteMarks(content.text)[0].attrs.color).toBe(
|
|
2239
|
+
brandPageModel.styles.buttonStyles.style.color,
|
|
2240
|
+
);
|
|
2241
|
+
expect(rteMarks(content.text)[0].attrs.color).not.toBe(
|
|
2242
|
+
paragraphColour(brandPageModel),
|
|
2243
|
+
);
|
|
2244
|
+
});
|
|
2245
|
+
|
|
2246
|
+
// The exact user-reported shape: an UNLINKED text style (no `textStyleType`) whose mark is
|
|
2247
|
+
// `color: ''`, on the default `transparent` section. Without the frame it drops to the root
|
|
2248
|
+
// paint; with it, the empty mark bakes to the button colour it showed pre-V2.
|
|
2249
|
+
test('brandContact text section, UNLINKED style + empty `transparent` background: empty mark bakes to the button colour', async () => {
|
|
2250
|
+
const model = {
|
|
2251
|
+
styles: bakeStyles(),
|
|
2252
|
+
brandPageType: 'brandContact',
|
|
2253
|
+
pages: [
|
|
2254
|
+
{
|
|
2255
|
+
contents: [
|
|
2256
|
+
{
|
|
2257
|
+
type: 'text',
|
|
2258
|
+
backgroundColor: { backgroundColor: 'transparent' },
|
|
2259
|
+
text: rteDoc(''),
|
|
2260
|
+
textStyle: { fontFamily: 'Lato' }, // no textStyleType, no color
|
|
2261
|
+
},
|
|
2262
|
+
],
|
|
2263
|
+
},
|
|
2264
|
+
],
|
|
2265
|
+
};
|
|
2266
|
+
|
|
2267
|
+
const { brandPageModel } = await migration21(
|
|
2268
|
+
createMigrationData(model),
|
|
2269
|
+
OPTIONS_NO_LOGO,
|
|
2270
|
+
);
|
|
2271
|
+
|
|
2272
|
+
const content = brandPageModel.pages[0].contents[0] as any;
|
|
2273
|
+
|
|
2274
|
+
expect(rteMarks(content.text)[0].attrs.color).toBe(
|
|
2275
|
+
brandPageModel.styles.buttonStyles.style.color,
|
|
2276
|
+
);
|
|
2277
|
+
});
|
|
2278
|
+
|
|
2279
|
+
test('profile with a LITERAL `transparent` background: uncoloured title bakes to the frame button-text colour', async () => {
|
|
2280
|
+
const model = {
|
|
2281
|
+
styles: bakeStyles(),
|
|
2282
|
+
brandPageType: 'default',
|
|
2283
|
+
pages: [
|
|
2284
|
+
{
|
|
2285
|
+
contents: [
|
|
2286
|
+
{
|
|
2287
|
+
type: 'profile',
|
|
2288
|
+
backgroundColor: { backgroundColor: 'transparent' },
|
|
2289
|
+
title: rteDoc(''),
|
|
2290
|
+
titleStyle: { textStyleType: 'headings' },
|
|
2291
|
+
},
|
|
2292
|
+
],
|
|
2293
|
+
},
|
|
2294
|
+
],
|
|
2295
|
+
};
|
|
2296
|
+
|
|
2297
|
+
const { brandPageModel } = await migration21(
|
|
2298
|
+
createMigrationData(model),
|
|
2299
|
+
OPTIONS_NO_LOGO,
|
|
2300
|
+
);
|
|
2301
|
+
|
|
2302
|
+
const content = brandPageModel.pages[0].contents[0] as any;
|
|
2303
|
+
|
|
2304
|
+
expect(titleMarkColour(content)).toBe(
|
|
2305
|
+
brandPageModel.styles.buttonStyles.style.color,
|
|
2306
|
+
);
|
|
2307
|
+
expect(titleMarkColour(content)).not.toBe(headingColour(brandPageModel));
|
|
2308
|
+
});
|
|
2309
|
+
|
|
2209
2310
|
test('profile on a SOLID background: uncoloured title/description bake to the frame button-text colour', async () => {
|
|
2210
2311
|
const model = {
|
|
2211
2312
|
styles: bakeStyles(),
|
|
@@ -2594,3 +2695,71 @@ describe('migration21 — step FINAL+1: bake resolved colour into inherit-only R
|
|
|
2594
2695
|
);
|
|
2595
2696
|
});
|
|
2596
2697
|
});
|
|
2698
|
+
|
|
2699
|
+
describe('migration21 — icon fills (social-links + footer)', () => {
|
|
2700
|
+
test('social-links: an unset iconFillStyles.fill is baked from the referencified root text colour', async () => {
|
|
2701
|
+
const model = {
|
|
2702
|
+
styles: { textStyles: { style: { color: '#123456' } }, colors: [] },
|
|
2703
|
+
pages: [{ contents: [{ type: 'social-links', iconFillStyles: {} }] }],
|
|
2704
|
+
};
|
|
2705
|
+
|
|
2706
|
+
const { brandPageModel } = await migration21(
|
|
2707
|
+
createMigrationData(model),
|
|
2708
|
+
OPTIONS,
|
|
2709
|
+
);
|
|
2710
|
+
|
|
2711
|
+
const rootRef = refFor(brandPageModel, '#123456');
|
|
2712
|
+
|
|
2713
|
+
expect(rootRef).toMatch(/^var\(--palette-/);
|
|
2714
|
+
expect(
|
|
2715
|
+
(brandPageModel.pages[0].contents[0] as any).iconFillStyles.fill,
|
|
2716
|
+
).toBe(rootRef);
|
|
2717
|
+
});
|
|
2718
|
+
|
|
2719
|
+
test('social-links: an explicit iconFillStyles.fill is preserved, not overwritten', async () => {
|
|
2720
|
+
const model = {
|
|
2721
|
+
styles: { textStyles: { style: { color: '#123456' } }, colors: [] },
|
|
2722
|
+
pages: [
|
|
2723
|
+
{
|
|
2724
|
+
contents: [
|
|
2725
|
+
{ type: 'social-links', iconFillStyles: { fill: '#abcdef' } },
|
|
2726
|
+
],
|
|
2727
|
+
},
|
|
2728
|
+
],
|
|
2729
|
+
};
|
|
2730
|
+
|
|
2731
|
+
const { brandPageModel } = await migration21(
|
|
2732
|
+
createMigrationData(model),
|
|
2733
|
+
OPTIONS,
|
|
2734
|
+
);
|
|
2735
|
+
|
|
2736
|
+
// Its own colour rode pass B into a reference; the root ref did not replace it.
|
|
2737
|
+
expect(
|
|
2738
|
+
(brandPageModel.pages[0].contents[0] as any).iconFillStyles.fill,
|
|
2739
|
+
).toBe(refFor(brandPageModel, '#abcdef'));
|
|
2740
|
+
});
|
|
2741
|
+
|
|
2742
|
+
test('footer: an unset iconStyle.fill is baked to a literal black; an explicit one is kept', async () => {
|
|
2743
|
+
const model = {
|
|
2744
|
+
styles: { textStyles: { style: { color: '#123456' } }, colors: [] },
|
|
2745
|
+
pages: [],
|
|
2746
|
+
pageFooterDesktop: { iconStyle: {} },
|
|
2747
|
+
pageFooterMobile: { iconStyle: { fill: '#654321' } },
|
|
2748
|
+
};
|
|
2749
|
+
|
|
2750
|
+
const { brandPageModel } = await migration21(
|
|
2751
|
+
createMigrationData(model),
|
|
2752
|
+
OPTIONS,
|
|
2753
|
+
);
|
|
2754
|
+
|
|
2755
|
+
// Literal, not a palette ref: painted after pass B, so it mints no custom.
|
|
2756
|
+
expect(brandPageModel.pageFooterDesktop.iconStyle.fill).toBe('#000000');
|
|
2757
|
+
expect(
|
|
2758
|
+
brandPageModel.styles.colors.some((c: any) => c.value === '#000000'),
|
|
2759
|
+
).toBe(false);
|
|
2760
|
+
// The explicit footer colour was referencified, not blacked out.
|
|
2761
|
+
expect(brandPageModel.pageFooterMobile.iconStyle.fill).toBe(
|
|
2762
|
+
refFor(brandPageModel, '#654321'),
|
|
2763
|
+
);
|
|
2764
|
+
});
|
|
2765
|
+
});
|
|
@@ -491,6 +491,75 @@ export const moveRootTextColourIntoGlobalStyles = (
|
|
|
491
491
|
delete (textStyles as unknown as Record<string, unknown>)['style'];
|
|
492
492
|
};
|
|
493
493
|
|
|
494
|
+
/* -------------------------------------------------------------------------- *
|
|
495
|
+
* Step FINAL−: bake the social-icon fills the palette walk leaves unset
|
|
496
|
+
* -------------------------------------------------------------------------- */
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* The footer social icons carry no `fill` in their SVGs, so an unset footer icon painted
|
|
500
|
+
* the SVG default (black). Baked as a LITERAL, not a palette ref — a fixed fallback for
|
|
501
|
+
* that previously-implicit default, so it does not mint a palette custom or follow palette
|
|
502
|
+
* edits. Painted after pass B, so referencify never sees it.
|
|
503
|
+
*/
|
|
504
|
+
const FOOTER_ICON_FALLBACK_COLOUR = '#000000';
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Bake the two social-icon fills that neither the palette walk nor a resolver sets, so
|
|
508
|
+
* each persists an explicit colour instead of a render-time seed that changed under V2:
|
|
509
|
+
*
|
|
510
|
+
* - **social-links content** (`iconFillStyles.fill`) → the site's ROOT text colour, the
|
|
511
|
+
* colour the icon followed before the palette split (`styles.textStyles.style.color`).
|
|
512
|
+
* Read here — already referencified by pass B — BEFORE
|
|
513
|
+
* `moveRootTextColourIntoGlobalStyles` retires it, which is why this runs at Step FINAL−.
|
|
514
|
+
* - **footer** (`pageFooter{Desktop,Mobile}.iconStyle.fill`) → a literal black
|
|
515
|
+
* (`FOOTER_ICON_FALLBACK_COLOUR`).
|
|
516
|
+
*
|
|
517
|
+
* Blank-only: an explicit persisted colour is never touched (`Object`-spread with the fill
|
|
518
|
+
* appended only when absent). A re-run finds every fill set, so it is a no-op.
|
|
519
|
+
*/
|
|
520
|
+
const bakeIconColours = (brandPageModel: BrandPageModel): void => {
|
|
521
|
+
const rootTextColour = legacyRootTextColour(brandPageModel);
|
|
522
|
+
|
|
523
|
+
if (!isBlankColorValue(rootTextColour)) {
|
|
524
|
+
for (const page of brandPageModel.pages ?? []) {
|
|
525
|
+
for (const content of page.contents ?? []) {
|
|
526
|
+
if (content.type !== 'social-links') {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const fields = content as unknown as Record<string, unknown>;
|
|
531
|
+
const iconFillStyles = (fields.iconFillStyles ?? {}) as Record<
|
|
532
|
+
string,
|
|
533
|
+
unknown
|
|
534
|
+
>;
|
|
535
|
+
|
|
536
|
+
if (isBlankColorValue(iconFillStyles.fill as string | undefined)) {
|
|
537
|
+
fields.iconFillStyles = { ...iconFillStyles, fill: rootTextColour };
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
for (const footer of [
|
|
544
|
+
brandPageModel.pageFooterDesktop,
|
|
545
|
+
brandPageModel.pageFooterMobile,
|
|
546
|
+
]) {
|
|
547
|
+
if (!footer) {
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const footerRecord = footer as unknown as Record<string, unknown>;
|
|
552
|
+
const iconStyle = (footerRecord.iconStyle ?? {}) as Record<string, unknown>;
|
|
553
|
+
|
|
554
|
+
if (isBlankColorValue(iconStyle.fill as string | undefined)) {
|
|
555
|
+
footerRecord.iconStyle = {
|
|
556
|
+
...iconStyle,
|
|
557
|
+
fill: FOOTER_ICON_FALLBACK_COLOUR,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
|
|
494
563
|
/* -------------------------------------------------------------------------- *
|
|
495
564
|
* Step 0.1: retire styles.logoStyles.backgroundStyle onto the logo content
|
|
496
565
|
* -------------------------------------------------------------------------- */
|
|
@@ -974,6 +1043,12 @@ export const migration21 = async (
|
|
|
974
1043
|
|
|
975
1044
|
brandPageModel.styles.colors = builder.toColorItems();
|
|
976
1045
|
|
|
1046
|
+
// ---- Step FINAL−: bake icon fills before the root colour is retired ----
|
|
1047
|
+
// Social-links icons take the now-referencified root text colour they followed before
|
|
1048
|
+
// the palette split; footer icons take a literal black fallback. Reads the root ref, so
|
|
1049
|
+
// it MUST precede its retirement below.
|
|
1050
|
+
bakeIconColours(brandPageModel);
|
|
1051
|
+
|
|
977
1052
|
// ---- Step FINAL: retire the root text colour (order matters — see docblock) ----
|
|
978
1053
|
moveRootTextColourIntoGlobalStyles(brandPageModel);
|
|
979
1054
|
|