@designcrowd/library.brand-page 6.2.0 → 6.2.2-live-preview-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-VQCQvlMg.js → CreatedOnBrandCrowd-de-de-Cn0M-eZ1.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-DSf3BjCG.js → CreatedOnBrandCrowd-en-us-DRLMSDZr.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-DGcdM9Ec.js → CreatedOnBrandCrowd-es-es-BglTu73U.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-C9g1fc39.js → CreatedOnBrandCrowd-fr-ca-Bj6YZ_el.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-C9g1fc39.js → CreatedOnBrandCrowd-fr-fr-Bj6YZ_el.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-DSXr7Bxl.js → CreatedOnBrandCrowd-pt-br-DEoOmQ1F.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-DSXr7Bxl.js → CreatedOnBrandCrowd-pt-pt-DEoOmQ1F.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-BPdCL6wS.js → CreatedOnDcom-de-de-BZ7gTt4Z.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-CLkSTQRu.js → CreatedOnDcom-en-us-CD6Km5sH.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-CRknMAGv.js → CreatedOnDcom-es-es-CNpZHvEl.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-ClAY_dHI.js → CreatedOnDcom-fr-ca-URS-ydJ1.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-DQtv9rR-.js → CreatedOnDcom-fr-fr-D2AVeA3H.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-BbIxvmxv.js → CreatedOnDcom-pt-br-C32frnFs.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-BOfOesBj.js → CreatedOnDcom-pt-pt-TDYGjRXS.js} +1 -1
- package/lib/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.d.ts +16 -2
- package/lib/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.js +17 -0
- package/lib/src/brand-page/components/page/Page.mixin.d.ts +2 -1
- package/lib/src/brand-page/components/page/Page.mixin.js +12 -7
- package/lib/src/brand-page/components/page-header/HeaderLayout.stories.d.ts +1 -0
- package/lib/src/brand-page/components/page-header/HeaderLayout.stories.js +9 -0
- package/lib/src/brand-page/components/page-header/PageHeader.mixin.d.ts +3 -1
- package/lib/src/brand-page/components/page-header/PageHeader.mixin.js +10 -4
- package/lib/src/brand-page/constants/sections/header-display.constants.d.ts +3 -0
- package/lib/src/brand-page/constants/sections/header-display.constants.js +15 -0
- package/lib/src/brand-page/models/brand-page-context.model.d.ts +0 -2
- package/lib/src/brand-page/models/brand-page-context.model.js +0 -12
- package/lib/src/brand-page/models/index.d.ts +12 -0
- package/lib/src/brand-page/models/index.js +4 -0
- package/lib/src/brand-page/utils/context.util.d.ts +1 -0
- package/lib/src/brand-page/utils/context.util.js +9 -0
- package/lib/src/brand-page/utils/route.util.d.ts +1 -0
- package/lib/src/brand-page/utils/route.util.js +9 -0
- package/lib/src/brand-page/utils/view-model.util.js +53 -1
- package/lib/src/brand-page/view-models.d.ts +3 -1
- package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.d.ts +0 -1
- package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.js +0 -9
- package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +9 -44
- package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +8 -43
- package/lib/src/brand-page-maker/components/preview/Preview.mixin.d.ts +0 -14
- package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +0 -42
- package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +0 -42
- package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +0 -42
- package/lib/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.mixin.d.ts +0 -42
- package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +0 -42
- package/lib/src/brand-page-maker/components/upgrade-to-premium-button/UpgradeToPremiumButton.mixin.d.ts +0 -42
- package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +3 -0
- package/lib/src/brand-page-maker/composables/useEventTracker.js +10 -0
- package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +1626 -0
- package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +0 -42
- package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +2 -4
- package/lib/src/brand-page-maker/validators/section-button.validator.d.ts +5 -2
- package/lib/src/brand-page-maker/validators/section-button.validator.js +11 -15
- package/lib/src/{bundled-translations-B2q5GWTQ.js → bundled-translations-BYUfWBT3.js} +17 -17
- package/lib/src/{bundled-translations.de-DE-FhH2H_rd.js → bundled-translations.de-DE-MmzUu1tt.js} +21 -21
- package/lib/src/{bundled-translations.es-ES-_ZExxMi7.js → bundled-translations.es-ES-D-tG33XL.js} +5 -5
- package/lib/src/{bundled-translations.fr-CA-Tg-ODIiC.js → bundled-translations.fr-CA-CNc1H1h3.js} +1 -1
- package/lib/src/{bundled-translations.fr-FR-C0sU51Mz.js → bundled-translations.fr-FR-DX0-yepT.js} +9 -9
- package/lib/src/{bundled-translations.pt-BR-MYFIhA4u.js → bundled-translations.pt-BR-ih1-LRgy.js} +9 -9
- package/lib/src/{bundled-translations.pt-PT-DeUBSzCB.js → bundled-translations.pt-PT-Dq2CmD1p.js} +22 -22
- package/lib/src/bundles/bundled-translations.de-DE.json +9 -2
- package/lib/src/bundles/bundled-translations.es-ES.json +9 -2
- package/lib/src/bundles/bundled-translations.fr-CA.json +9 -2
- package/lib/src/bundles/bundled-translations.fr-FR.json +9 -2
- package/lib/src/bundles/bundled-translations.json +9 -2
- package/lib/src/bundles/bundled-translations.pt-BR.json +9 -2
- package/lib/src/bundles/bundled-translations.pt-PT.json +9 -2
- package/lib/src/composables/useTranslate.d.ts +1 -1
- package/lib/src/composables/useTranslate.js +1 -1
- package/lib/src/css/library.brand-page-brandCrowd.min.css +577 -139
- package/lib/src/css/library.brand-page-brandPage.min.css +577 -139
- package/lib/src/css/library.brand-page-designCom.min.css +577 -139
- package/lib/src/css/library.brand-page-preview.min.css +577 -139
- package/lib/src/index.mjs +2 -2
- package/lib/src/{lib-BMVF4uNy.js → lib-Dm9AuOJL.js} +22487 -21900
- package/lib/src/metadata-builder/builders/item-id-utils.d.ts +2 -0
- package/lib/src/metadata-builder/builders/item-id-utils.js +8 -2
- package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +10 -2
- package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +7 -1
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +6 -1
- package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +6 -43
- package/lib/src/shared/content-settings/components/text-field/TextField.mixin.d.ts +14 -2
- package/lib/src/shared/content-settings/components/text-field/TextField.mixin.js +14 -0
- package/lib/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.d.ts +9 -1
- package/lib/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.js +31 -9
- package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +1 -0
- package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +6 -43
- package/lib/src/shared/page-navigation/store/page-navigation-store.d.ts +3 -1
- package/lib/src/shared/page-navigation/store/page-navigation-store.js +34 -9
- package/lib/src/shared/page-settings/components/delete-page-modal/DeletePageModal.mixin.d.ts +1 -1
- package/lib/src/shared/page-settings/components/new-page-modal/NewPageModal.mixin.d.ts +9 -2
- package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +200 -44
- package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.js +12 -35
- package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +190 -44
- package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.js +34 -2
- package/lib/src/shared/page-settings/duplicate-page.util.d.ts +4 -0
- package/lib/src/shared/page-settings/duplicate-page.util.js +82 -0
- package/lib/src/shared/page-settings/models.d.ts +1 -0
- package/lib/src/shared/page-settings/store/page-settings-store.d.ts +7 -2
- package/lib/src/shared/page-settings/store/page-settings-store.js +90 -7
- package/lib/src/shared/page-settings/utils.d.ts +3 -0
- package/lib/src/shared/page-settings/utils.js +6 -0
- package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +1 -56
- package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +0 -42
- package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +1 -0
- package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +4 -42
- package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +1 -0
- package/lib/src/tracker.mjs +1 -1
- package/lib/src/{useEventTracker-C5Ql5p7u.js → useEventTracker-DwPTmzpa.js} +13 -3
- package/package.json +2 -2
- package/src/brand-page/components/navigation/hamburger/HamburgerNavigation.vue +1 -0
- package/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.ts +18 -1
- package/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.vue +73 -3
- package/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.vue.test.ts +181 -0
- package/src/brand-page/components/page/Page.mixin.ts +21 -11
- package/src/brand-page/components/page/Page.vue +2 -0
- package/src/brand-page/components/page-header/HeaderLayout.stories.ts +10 -0
- package/src/brand-page/components/page-header/HeaderLayout.vue +415 -71
- package/src/brand-page/components/page-header/PageHeader.mixin.ts +14 -10
- package/src/brand-page/components/page-header/PageHeader.vue +16 -1
- package/src/brand-page/components/page-header/PageHeader.vue.test.ts +339 -0
- package/src/brand-page/constants/sections/header-display.constants.test.ts +44 -0
- package/src/brand-page/constants/sections/header-display.constants.ts +35 -0
- package/src/brand-page/models/brand-page-context.model.ts +0 -23
- package/src/brand-page/models/header-button.model.test.ts +85 -0
- package/src/brand-page/models/index.ts +19 -0
- package/src/brand-page/utils/context.util.ts +15 -0
- package/src/brand-page/utils/route.util.ts +13 -0
- package/src/brand-page/utils/view-model.util.test.ts +151 -1
- package/src/brand-page/utils/view-model.util.ts +73 -1
- package/src/brand-page/view-models.ts +3 -0
- package/src/brand-page-maker/components/action-bar/ActionBar.mixin.ts +0 -10
- package/src/brand-page-maker/components/action-bar/ActionBarNavigation.test.ts +8 -49
- package/src/brand-page-maker/components/action-bar/ActionBarNavigation.vue +1 -1
- package/src/brand-page-maker/composables/useEventTracker.ts +13 -0
- package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +2 -4
- package/src/brand-page-maker/validators/section-button.validator.ts +23 -19
- package/src/bundles/bundled-translations.de-DE.json +9 -2
- package/src/bundles/bundled-translations.es-ES.json +9 -2
- package/src/bundles/bundled-translations.fr-CA.json +9 -2
- package/src/bundles/bundled-translations.fr-FR.json +9 -2
- package/src/bundles/bundled-translations.json +9 -2
- package/src/bundles/bundled-translations.pt-BR.json +9 -2
- package/src/bundles/bundled-translations.pt-PT.json +9 -2
- package/src/composables/useTranslate.ts +2 -1
- package/src/metadata-builder/builders/item-id-utils.ts +9 -2
- package/src/shared/content-settings/components/text-field/TextField.mixin.ts +15 -0
- package/src/shared/content-settings/components/url-field/UrlField.vue +1 -0
- package/src/shared/ecommerce-settings/components/product-config-field/ProductConfigOption.vue +0 -2
- package/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.ts +32 -10
- package/src/shared/page-navigation/store/page-navigation-store.ts +57 -13
- package/src/shared/page-settings/components/header-settings-panel/HeaderSettingsPanel.button-section.test.ts +131 -0
- package/src/shared/page-settings/components/header-settings-panel/HeaderSettingsPanel.vue +65 -10
- package/src/shared/page-settings/components/page-settings/NestedDraggable.vue +3 -0
- package/src/shared/page-settings/components/page-settings/PageElement.vue +2 -0
- package/src/shared/page-settings/components/page-settings/PageSettingMenu.test.ts +105 -0
- package/src/shared/page-settings/components/page-settings/PageSettingMenu.vue +16 -0
- package/src/shared/page-settings/components/page-settings/PageSettings.duplicate.test.ts +108 -0
- package/src/shared/page-settings/components/page-settings/PageSettings.mixin.ts +22 -50
- package/src/shared/page-settings/components/page-settings/PageSettings.vue +1 -0
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.de-DE.json +3 -1
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.es-ES.json +3 -1
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.fr-CA.json +3 -1
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.fr-FR.json +3 -1
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.json +3 -1
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.pt-BR.json +3 -1
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.pt-PT.json +3 -1
- package/src/shared/page-settings/components/page-settings-section-styles/HeaderButtonSettings.test.ts +305 -0
- package/src/shared/page-settings/components/page-settings-section-styles/HeaderButtonSettings.vue +265 -0
- package/src/shared/page-settings/components/page-settings-section-styles/HeaderDisplayStyle.test.ts +354 -0
- package/src/shared/page-settings/components/page-settings-section-styles/HeaderDisplayStyle.vue +48 -9
- package/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.header-button.test.ts +114 -0
- package/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.ts +38 -1
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.de-DE.json +6 -1
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.es-ES.json +6 -1
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.fr-CA.json +6 -1
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.fr-FR.json +6 -1
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.json +6 -1
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.pt-BR.json +6 -1
- package/src/shared/page-settings/components/page-settings-section-styles/i18n/page-settings-section.pt-PT.json +6 -1
- package/src/shared/page-settings/duplicate-page.util.test.ts +382 -0
- package/src/shared/page-settings/duplicate-page.util.ts +124 -0
- package/src/shared/page-settings/models.ts +1 -0
- package/src/shared/page-settings/store/page-settings-store.test.ts +441 -1
- package/src/shared/page-settings/store/page-settings-store.ts +147 -11
- package/src/shared/page-settings/utils.ts +10 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { createPinia, setActivePinia } from 'pinia';
|
|
3
|
+
import { shallowMount } from '@vue/test-utils';
|
|
4
|
+
import type { VueWrapper } from '@vue/test-utils';
|
|
5
|
+
import type { HeaderButton, LogoTheme } from '@brand-page';
|
|
6
|
+
import HeaderButtonSettings from './HeaderButtonSettings.vue';
|
|
7
|
+
import { useUiPageSettingsStore } from '../../store';
|
|
8
|
+
import { useUiStyleSettingsStore } from '../../../style-settings/store/style-settings-store';
|
|
9
|
+
import { availableButtonStyleConfigs } from '@brand-page-maker/models';
|
|
10
|
+
import type { ContentPage } from '../../models';
|
|
11
|
+
|
|
12
|
+
// Explicit stub: the auto-stub misses mixin-declared props, so props('value') would be undefined.
|
|
13
|
+
const TextStyleFieldStub = {
|
|
14
|
+
name: 'TextStyleField',
|
|
15
|
+
props: ['value'],
|
|
16
|
+
template: '<div />',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const UrlFieldStub = {
|
|
20
|
+
name: 'UrlField',
|
|
21
|
+
props: ['settings'],
|
|
22
|
+
template: '<div />',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const TextFieldStub = {
|
|
26
|
+
name: 'TextField',
|
|
27
|
+
// showErrorWhenEmpty typed so the bare attribute casts to true, as on the real component.
|
|
28
|
+
props: { settings: Object, showErrorWhenEmpty: Boolean },
|
|
29
|
+
template: '<div />',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Fields are found by data-test hook (two of them are ColorFields).
|
|
33
|
+
const emitFrom = (
|
|
34
|
+
wrapper: VueWrapper,
|
|
35
|
+
hook: string,
|
|
36
|
+
payload: unknown,
|
|
37
|
+
): void => {
|
|
38
|
+
(wrapper.findComponent(hook) as unknown as VueWrapper).vm.$emit(
|
|
39
|
+
'update:modelValue',
|
|
40
|
+
payload,
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Every edit leaves through one on-change patch (the mixin writes it to both viewports).
|
|
45
|
+
describe('HeaderButtonSettings', () => {
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
setActivePinia(createPinia());
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const mountPanel = (value?: HeaderButton) =>
|
|
51
|
+
shallowMount(HeaderButtonSettings, {
|
|
52
|
+
props: {
|
|
53
|
+
value,
|
|
54
|
+
isAdminMaker: false,
|
|
55
|
+
logoTheme: {} as LogoTheme,
|
|
56
|
+
},
|
|
57
|
+
global: {
|
|
58
|
+
stubs: {
|
|
59
|
+
TextStyleField: TextStyleFieldStub,
|
|
60
|
+
UrlField: UrlFieldStub,
|
|
61
|
+
TextField: TextFieldStub,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const enabled: HeaderButton = {
|
|
67
|
+
isVisible: true,
|
|
68
|
+
text: '',
|
|
69
|
+
buttonLinkType: 'url',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const fieldHooks = [
|
|
73
|
+
'[data-test-header-button-text]',
|
|
74
|
+
'[data-test-header-button-link-type]',
|
|
75
|
+
'[data-test-header-button-text-style]',
|
|
76
|
+
'[data-test-header-button-fill-color]',
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
// The show/hide toggle lives in HeaderSettingsPanel, so every field here renders unconditionally.
|
|
80
|
+
test('every field renders', () => {
|
|
81
|
+
const wrapper = mountPanel(enabled);
|
|
82
|
+
|
|
83
|
+
fieldHooks.forEach((hook) => {
|
|
84
|
+
expect(wrapper.find(hook).exists()).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Border color only shows when the selected button style draws a border.
|
|
89
|
+
test('the border colour field is hidden while the selected button style draws no border', () => {
|
|
90
|
+
// default selectedButtonStyleId is BUTTON_STYLE_1, whose options carry no border
|
|
91
|
+
const wrapper = mountPanel(enabled);
|
|
92
|
+
|
|
93
|
+
expect(
|
|
94
|
+
wrapper.find('[data-test-header-button-border-color]').exists(),
|
|
95
|
+
).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('the border colour field renders when the selected button style has a border', () => {
|
|
99
|
+
useUiStyleSettingsStore().$patch(($state) => {
|
|
100
|
+
// availableButtonStyleConfigs starts empty until the store initialises
|
|
101
|
+
$state.availableButtonStyleConfigs = availableButtonStyleConfigs;
|
|
102
|
+
$state.buttonStyles.selectedButtonStyleId = 'BUTTON_STYLE_4';
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const wrapper = mountPanel(enabled);
|
|
106
|
+
|
|
107
|
+
expect(
|
|
108
|
+
wrapper.find('[data-test-header-button-border-color]').exists(),
|
|
109
|
+
).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('the link-value field follows the selected link kind', () => {
|
|
113
|
+
const kinds = {
|
|
114
|
+
url: '[data-test-header-button-url]',
|
|
115
|
+
page: '[data-test-header-button-page]',
|
|
116
|
+
mailTo: '[data-test-header-button-email]',
|
|
117
|
+
tel: '[data-test-header-button-tel]',
|
|
118
|
+
} as const;
|
|
119
|
+
|
|
120
|
+
Object.entries(kinds).forEach(([kind, hook]) => {
|
|
121
|
+
const wrapper = mountPanel({
|
|
122
|
+
...enabled,
|
|
123
|
+
buttonLinkType: kind as HeaderButton['buttonLinkType'],
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
Object.values(kinds).forEach((otherHook) => {
|
|
127
|
+
expect(wrapper.find(otherHook).exists()).toBe(otherHook === hook);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const linkTypeValues = (wrapper: VueWrapper): string[] => {
|
|
133
|
+
const group = wrapper.findComponent(
|
|
134
|
+
'[data-test-header-button-link-type]',
|
|
135
|
+
) as unknown as { props(name: string): unknown };
|
|
136
|
+
|
|
137
|
+
return (group.props('options') as { value: string }[]).map(
|
|
138
|
+
(option) => option.value,
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
test('the four link kinds are offered in a 2-column group', () => {
|
|
143
|
+
const wrapper = mountPanel(enabled);
|
|
144
|
+
|
|
145
|
+
expect(linkTypeValues(wrapper)).toEqual(['url', 'page', 'mailTo', 'tel']);
|
|
146
|
+
expect(
|
|
147
|
+
wrapper.find('[data-test-header-button-link-type]').attributes('columns'),
|
|
148
|
+
).toBe('2');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('a single-page site drops the Page kind (buttonLinkStyleConvertor)', () => {
|
|
152
|
+
useUiPageSettingsStore().setPages({
|
|
153
|
+
pages: [{ id: 'page-1', title: 'Home', contents: [] }] as ContentPage[],
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
expect(linkTypeValues(mountPanel(enabled))).toEqual([
|
|
157
|
+
'url',
|
|
158
|
+
'mailTo',
|
|
159
|
+
'tel',
|
|
160
|
+
]);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('a label edit emits only the text patch', () => {
|
|
164
|
+
const wrapper = mountPanel(enabled);
|
|
165
|
+
|
|
166
|
+
emitFrom(wrapper, '[data-test-header-button-text]', {
|
|
167
|
+
id: 'headerButton',
|
|
168
|
+
name: 'text',
|
|
169
|
+
value: 'Book now',
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
expect(wrapper.emitted('on-change')).toEqual([[{ text: 'Book now' }]]);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Unset text style must show the renderer's Buttons default, not None/blank.
|
|
176
|
+
test('an unset text style presents the Buttons-linked default', () => {
|
|
177
|
+
const wrapper = mountPanel(enabled);
|
|
178
|
+
|
|
179
|
+
expect(
|
|
180
|
+
wrapper.findComponent({ name: 'TextStyleField' }).props('value'),
|
|
181
|
+
).toEqual({
|
|
182
|
+
textStyleType: 'buttons',
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test('an authored text style is passed through untouched', () => {
|
|
187
|
+
const wrapper = mountPanel({
|
|
188
|
+
...enabled,
|
|
189
|
+
buttonTextStyle: { fontFamily: 'Lora', textAlign: 'center' },
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(
|
|
193
|
+
wrapper.findComponent({ name: 'TextStyleField' }).props('value'),
|
|
194
|
+
).toEqual({
|
|
195
|
+
fontFamily: 'Lora',
|
|
196
|
+
textAlign: 'center',
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Stricter than the banner: the link is always required, whatever the label holds.
|
|
201
|
+
test('a labelled button with an empty link shows the error', () => {
|
|
202
|
+
const wrapper = mountPanel({ ...enabled, text: 'Contact Us' });
|
|
203
|
+
|
|
204
|
+
expect(
|
|
205
|
+
wrapper.findComponent({ name: 'UrlField' }).props('settings'),
|
|
206
|
+
).toMatchObject({ isValid: false });
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('an unlabelled button with an empty link also shows the error', () => {
|
|
210
|
+
const wrapper = mountPanel(enabled);
|
|
211
|
+
|
|
212
|
+
expect(
|
|
213
|
+
wrapper.findComponent({ name: 'UrlField' }).props('settings'),
|
|
214
|
+
).toMatchObject({ isValid: false });
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test('a valid link is accepted while the label is still empty', () => {
|
|
218
|
+
const wrapper = mountPanel({ ...enabled, url: 'https://example.com' });
|
|
219
|
+
|
|
220
|
+
expect(
|
|
221
|
+
wrapper.findComponent({ name: 'UrlField' }).props('settings'),
|
|
222
|
+
).toMatchObject({ isValid: true });
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('switching the link kind clears the previous value', () => {
|
|
226
|
+
const wrapper = mountPanel({ ...enabled, url: 'https://example.com' });
|
|
227
|
+
|
|
228
|
+
(
|
|
229
|
+
wrapper.findComponent(
|
|
230
|
+
'[data-test-header-button-link-type]',
|
|
231
|
+
) as unknown as VueWrapper
|
|
232
|
+
).vm.$emit('on-select', { value: 'mailTo' });
|
|
233
|
+
|
|
234
|
+
expect(wrapper.emitted('on-change')).toEqual([
|
|
235
|
+
[{ buttonLinkType: 'mailTo', url: '' }],
|
|
236
|
+
]);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// The label is required — an empty one hides the button on the site.
|
|
240
|
+
test.each([[''], [' ']])(
|
|
241
|
+
'a blank label shows the required error (%j)',
|
|
242
|
+
(text) => {
|
|
243
|
+
const wrapper = mountPanel({ ...enabled, text });
|
|
244
|
+
|
|
245
|
+
expect(
|
|
246
|
+
wrapper.findComponent({ name: 'TextField' }).props('settings'),
|
|
247
|
+
).toMatchObject({ isValid: false });
|
|
248
|
+
},
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
test('the label shows its error as soon as it is cleared', () => {
|
|
252
|
+
const wrapper = mountPanel(enabled);
|
|
253
|
+
|
|
254
|
+
expect(
|
|
255
|
+
wrapper.findComponent({ name: 'TextField' }).props('showErrorWhenEmpty'),
|
|
256
|
+
).toBe(true);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('a filled label is valid', () => {
|
|
260
|
+
const wrapper = mountPanel({ ...enabled, text: 'Contact Us' });
|
|
261
|
+
|
|
262
|
+
expect(
|
|
263
|
+
wrapper.findComponent({ name: 'TextField' }).props('settings'),
|
|
264
|
+
).toMatchObject({ isValid: true });
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// A transparent fill or border would leave a ghost button, so neither offers the option.
|
|
268
|
+
test.each([
|
|
269
|
+
['fill', '[data-test-header-button-fill-color]', 'BUTTON_STYLE_1'],
|
|
270
|
+
['border', '[data-test-header-button-border-color]', 'BUTTON_STYLE_4'],
|
|
271
|
+
])(
|
|
272
|
+
'the %s colour field does not offer transparent',
|
|
273
|
+
(_name, hook, styleId) => {
|
|
274
|
+
useUiStyleSettingsStore().$patch(($state) => {
|
|
275
|
+
$state.availableButtonStyleConfigs = availableButtonStyleConfigs;
|
|
276
|
+
// BUTTON_STYLE_4 draws a border, so the border field renders.
|
|
277
|
+
$state.buttonStyles.selectedButtonStyleId = styleId;
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const wrapper = mountPanel(enabled);
|
|
281
|
+
|
|
282
|
+
expect(
|
|
283
|
+
(
|
|
284
|
+
wrapper.findComponent(hook) as unknown as {
|
|
285
|
+
props(name: string): unknown;
|
|
286
|
+
}
|
|
287
|
+
).props('disableTransparent'),
|
|
288
|
+
).toBe(true);
|
|
289
|
+
},
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
test('a fill-colour pick emits only the fillColor patch', () => {
|
|
293
|
+
const wrapper = mountPanel(enabled);
|
|
294
|
+
|
|
295
|
+
emitFrom(wrapper, '[data-test-header-button-fill-color]', {
|
|
296
|
+
id: 'fillColor',
|
|
297
|
+
name: 'fillColor',
|
|
298
|
+
value: 'var(--palette-2)',
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
expect(wrapper.emitted('on-change')).toEqual([
|
|
302
|
+
[{ fillColor: 'var(--palette-2)' }],
|
|
303
|
+
]);
|
|
304
|
+
});
|
|
305
|
+
});
|
package/src/shared/page-settings/components/page-settings-section-styles/HeaderButtonSettings.vue
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div data-component="header-button-settings">
|
|
3
|
+
<p class="header-button-settings_option-title tw-mb-2">
|
|
4
|
+
{{ tr('pageSettingsSection.buttonLabel') }}
|
|
5
|
+
</p>
|
|
6
|
+
<TextField
|
|
7
|
+
id="headerButton"
|
|
8
|
+
data-test-header-button-text
|
|
9
|
+
name="text"
|
|
10
|
+
show-error-when-empty
|
|
11
|
+
:value="text"
|
|
12
|
+
:placeholder="tr('pageSettingsSection.buttonLabelPlaceholder')"
|
|
13
|
+
:max-length="BUTTON_TEXT_MAX_LENGTH"
|
|
14
|
+
:settings="labelSettings"
|
|
15
|
+
@update:model-value="onTextChange"
|
|
16
|
+
/>
|
|
17
|
+
|
|
18
|
+
<p class="header-button-settings_option-title tw-mt-4 tw-mb-2">
|
|
19
|
+
{{ tr('pageSettingsSection.buttonLink') }}
|
|
20
|
+
</p>
|
|
21
|
+
<ButtonGroup
|
|
22
|
+
data-test-header-button-link-type
|
|
23
|
+
:options="linkTypeOptions"
|
|
24
|
+
:columns="linkTypeColumns"
|
|
25
|
+
@on-select="onSelectLinkType"
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
<div class="tw-mt-2">
|
|
29
|
+
<PageDropdownField
|
|
30
|
+
v-if="buttonLinkType === BUTTON_LINK_TYPE.page"
|
|
31
|
+
id="headerButton"
|
|
32
|
+
data-test-header-button-page
|
|
33
|
+
name="buttonPageId"
|
|
34
|
+
:value="buttonPageId"
|
|
35
|
+
:placeholder="contentSettingsTr('selectAPagePlaceholder')"
|
|
36
|
+
@update:model-value="onPageChange"
|
|
37
|
+
/>
|
|
38
|
+
<EmailField
|
|
39
|
+
v-else-if="buttonLinkType === BUTTON_LINK_TYPE.mailTo"
|
|
40
|
+
id="headerButton"
|
|
41
|
+
data-test-header-button-email
|
|
42
|
+
name="url"
|
|
43
|
+
role="email"
|
|
44
|
+
:value="url"
|
|
45
|
+
:placeholder="contentSettingsTr('emailPlaceholder')"
|
|
46
|
+
:settings="linkValueSettings"
|
|
47
|
+
@update:model-value="onUrlChange"
|
|
48
|
+
/>
|
|
49
|
+
<TelField
|
|
50
|
+
v-else-if="buttonLinkType === BUTTON_LINK_TYPE.tel"
|
|
51
|
+
id="headerButton"
|
|
52
|
+
data-test-header-button-tel
|
|
53
|
+
name="url"
|
|
54
|
+
role="tel"
|
|
55
|
+
:value="url"
|
|
56
|
+
:placeholder="TEL_PLACEHOLDER"
|
|
57
|
+
:settings="linkValueSettings"
|
|
58
|
+
@update:model-value="onUrlChange"
|
|
59
|
+
/>
|
|
60
|
+
<UrlField
|
|
61
|
+
v-else
|
|
62
|
+
id="headerButton"
|
|
63
|
+
data-test-header-button-url
|
|
64
|
+
name="url"
|
|
65
|
+
role="url"
|
|
66
|
+
:value="url"
|
|
67
|
+
:placeholder="contentSettingsTr('exampleWebsitePlaceholder')"
|
|
68
|
+
:settings="linkValueSettings"
|
|
69
|
+
@update:model-value="onUrlChange"
|
|
70
|
+
/>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<TextStyleField
|
|
74
|
+
id="header-button-text-style"
|
|
75
|
+
data-test-header-button-text-style
|
|
76
|
+
name="buttonTextStyle"
|
|
77
|
+
:label="contentSettingsTr('buttonTextStyleLabel')"
|
|
78
|
+
:is-admin-maker="isAdminMaker"
|
|
79
|
+
:logo-theme="logoTheme"
|
|
80
|
+
:value="buttonTextStyle"
|
|
81
|
+
:show-text-alignment-options="false"
|
|
82
|
+
@update:model-value="onTextStyleChange"
|
|
83
|
+
/>
|
|
84
|
+
|
|
85
|
+
<ColorField
|
|
86
|
+
id="fillColor"
|
|
87
|
+
data-test-header-button-fill-color
|
|
88
|
+
name="fillColor"
|
|
89
|
+
:label="tr('pageSettingsSection.buttonFillColor')"
|
|
90
|
+
:value="fillColor"
|
|
91
|
+
:is-admin-maker="isAdminMaker"
|
|
92
|
+
:logo-theme="logoTheme"
|
|
93
|
+
:disable-transparent="true"
|
|
94
|
+
@update:model-value="onFillColorChange"
|
|
95
|
+
/>
|
|
96
|
+
|
|
97
|
+
<ColorField
|
|
98
|
+
v-if="supportsBorderColor"
|
|
99
|
+
id="borderColor"
|
|
100
|
+
data-test-header-button-border-color
|
|
101
|
+
name="borderColor"
|
|
102
|
+
:label="tr('pageSettingsSection.buttonBorderColor')"
|
|
103
|
+
:value="borderColor"
|
|
104
|
+
:is-admin-maker="isAdminMaker"
|
|
105
|
+
:logo-theme="logoTheme"
|
|
106
|
+
:disable-transparent="true"
|
|
107
|
+
@update:model-value="onBorderColorChange"
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
</template>
|
|
111
|
+
<script setup lang="ts">
|
|
112
|
+
import { computed } from 'vue';
|
|
113
|
+
import { ButtonGroup } from '@designcrowd/fe-shared-lib';
|
|
114
|
+
import type {
|
|
115
|
+
BrandPageContext,
|
|
116
|
+
HeaderButton,
|
|
117
|
+
LogoTheme,
|
|
118
|
+
TextStyleDeclaration,
|
|
119
|
+
} from '@brand-page';
|
|
120
|
+
import { BUTTON_LINK_TYPE, TEXT_STYLE_TYPES } from '@brand-page';
|
|
121
|
+
import type { ButtonLinkType } from '@brand-page';
|
|
122
|
+
import type { ButtonGroupOption } from '@brand-page-maker/models/button-group.model';
|
|
123
|
+
import { buttonLinkStyleConvertor } from '@brand-page-maker/convertors';
|
|
124
|
+
import { buttonLinkValidator } from '@brand-page-maker/validators';
|
|
125
|
+
import { useUiPageSettingsStore } from '../../store';
|
|
126
|
+
import { useUiStyleSettingsStore } from '../../../style-settings/store/style-settings-store';
|
|
127
|
+
import type { ContentItemFieldSettings } from '../../../content-settings/models';
|
|
128
|
+
import ColorField from '../../../content-settings/components/color-field/ColorField.vue';
|
|
129
|
+
import EmailField from '../../../content-settings/components/email-field/EmailField.vue';
|
|
130
|
+
import PageDropdownField from '../../../content-settings/components/page-dropdown-field/PageDropdownField.vue';
|
|
131
|
+
import TelField from '../../../content-settings/components/tel-field/TelField.vue';
|
|
132
|
+
import TextField from '../../../content-settings/components/text-field/TextField.vue';
|
|
133
|
+
import TextStyleField from '../../../content-settings/components/text-style-field/TextStyleField.vue';
|
|
134
|
+
import UrlField from '../../../content-settings/components/url-field/UrlField.vue';
|
|
135
|
+
import {
|
|
136
|
+
contentSettingsTr,
|
|
137
|
+
tr,
|
|
138
|
+
validatorTr,
|
|
139
|
+
} from '@src/composables/useTranslate';
|
|
140
|
+
|
|
141
|
+
/** Same label length cap as the section buttons. */
|
|
142
|
+
const BUTTON_TEXT_MAX_LENGTH = 60;
|
|
143
|
+
|
|
144
|
+
/** Format example, not translated — same as the banner's tel placeholder. */
|
|
145
|
+
const TEL_PLACEHOLDER = '12345-6789';
|
|
146
|
+
|
|
147
|
+
interface Props {
|
|
148
|
+
value?: HeaderButton;
|
|
149
|
+
isAdminMaker: boolean;
|
|
150
|
+
logoTheme: LogoTheme;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const props = defineProps<Props>();
|
|
154
|
+
|
|
155
|
+
const pageSettingsStore = useUiPageSettingsStore();
|
|
156
|
+
const styleSettingsStore = useUiStyleSettingsStore();
|
|
157
|
+
|
|
158
|
+
// Only show Border color when the site's button style actually draws a border.
|
|
159
|
+
const supportsBorderColor = computed(
|
|
160
|
+
(): boolean =>
|
|
161
|
+
styleSettingsStore.selectedButtonStyleConfig?.options.includes(
|
|
162
|
+
'buttonBorderColor',
|
|
163
|
+
) === true,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const emit = defineEmits<{
|
|
167
|
+
(e: 'on-change', patch: Partial<HeaderButton>): void;
|
|
168
|
+
}>();
|
|
169
|
+
|
|
170
|
+
const text = computed((): string => props.value?.text || '');
|
|
171
|
+
// Validation matches the renderer, which trims: a whitespace-only label hides the button.
|
|
172
|
+
const trimmedLabel = computed((): string => text.value.trim());
|
|
173
|
+
const url = computed((): string => props.value?.url || '');
|
|
174
|
+
const buttonPageId = computed((): string => props.value?.buttonPageId || '');
|
|
175
|
+
const fillColor = computed((): string => props.value?.fillColor || '');
|
|
176
|
+
const borderColor = computed((): string => props.value?.borderColor || '');
|
|
177
|
+
const buttonTextStyle = computed(
|
|
178
|
+
// When unset, show the same Buttons-style default the site actually renders with.
|
|
179
|
+
(): TextStyleDeclaration =>
|
|
180
|
+
props.value?.buttonTextStyle ?? { textStyleType: TEXT_STYLE_TYPES.buttons },
|
|
181
|
+
);
|
|
182
|
+
const buttonLinkType = computed(
|
|
183
|
+
(): ButtonLinkType => props.value?.buttonLinkType || BUTTON_LINK_TYPE.url,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const validFieldSettings: ContentItemFieldSettings = {
|
|
187
|
+
errorMessage: '',
|
|
188
|
+
isValid: true,
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// The label is required — a button with no text is hidden on the site.
|
|
192
|
+
const labelSettings = computed(
|
|
193
|
+
(): ContentItemFieldSettings =>
|
|
194
|
+
trimmedLabel.value
|
|
195
|
+
? validFieldSettings
|
|
196
|
+
: { isValid: false, errorMessage: validatorTr('emptyStringMessage') },
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// The link is always required, unlike the banner's (which is optional without button text).
|
|
200
|
+
const linkValueSettings = computed((): ContentItemFieldSettings => {
|
|
201
|
+
const { isValid, message } = buttonLinkValidator({
|
|
202
|
+
buttonLinkType: buttonLinkType.value,
|
|
203
|
+
url: url.value.trim(),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return { isValid, errorMessage: message };
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const linkTypeConfig = computed(() =>
|
|
210
|
+
buttonLinkStyleConvertor.convert(
|
|
211
|
+
buttonLinkType.value,
|
|
212
|
+
// Only `pages` is read (drops the Page option on single-page sites).
|
|
213
|
+
{ pages: pageSettingsStore.pages } as unknown as BrandPageContext,
|
|
214
|
+
),
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
const linkTypeOptions = computed(
|
|
218
|
+
(): ButtonGroupOption[] => linkTypeConfig.value.options,
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
const linkTypeColumns = computed(
|
|
222
|
+
(): number | undefined => linkTypeConfig.value.columns,
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const onTextChange = ({ value }: { value: string }): void => {
|
|
226
|
+
emit('on-change', { text: value });
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// Switching the link kind clears the old value — a url is not a valid email/phone (same as the banner).
|
|
230
|
+
const onSelectLinkType = (option: ButtonGroupOption): void => {
|
|
231
|
+
emit('on-change', {
|
|
232
|
+
buttonLinkType: option.value as ButtonLinkType,
|
|
233
|
+
url: '',
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const onUrlChange = ({ value }: { value: string }): void => {
|
|
238
|
+
emit('on-change', { url: value });
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const onPageChange = ({ value }: { value: string }): void => {
|
|
242
|
+
emit('on-change', { buttonPageId: value });
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const onTextStyleChange = ({
|
|
246
|
+
value,
|
|
247
|
+
}: {
|
|
248
|
+
value: TextStyleDeclaration;
|
|
249
|
+
}): void => {
|
|
250
|
+
emit('on-change', { buttonTextStyle: value });
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const onFillColorChange = ({ value }: { value: string }): void => {
|
|
254
|
+
emit('on-change', { fillColor: value });
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const onBorderColorChange = ({ value }: { value: string }): void => {
|
|
258
|
+
emit('on-change', { borderColor: value });
|
|
259
|
+
};
|
|
260
|
+
</script>
|
|
261
|
+
<style scoped>
|
|
262
|
+
.header-button-settings_option-title {
|
|
263
|
+
@apply tw-font-sans tw-font-bold tw-text-secondary-800 tw-text-sm;
|
|
264
|
+
}
|
|
265
|
+
</style>
|