@designcrowd/library.brand-page 6.1.7 → 6.1.8-ast-header
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-Dh3Paq4X.js → CreatedOnBrandCrowd-de-de-CmXTpyAA.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-DLWYV4xe.js → CreatedOnBrandCrowd-en-us-FUfcPskv.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-CcjzRbm_.js → CreatedOnBrandCrowd-es-es-BkRUovXn.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-UuUNeB_L.js → CreatedOnBrandCrowd-fr-ca-xsLZEhPK.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-UuUNeB_L.js → CreatedOnBrandCrowd-fr-fr-xsLZEhPK.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-ZVYZ5AnV.js → CreatedOnBrandCrowd-pt-br-BJ4DkilI.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-ZVYZ5AnV.js → CreatedOnBrandCrowd-pt-pt-BJ4DkilI.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-BPVyqOPL.js → CreatedOnDcom-de-de-D7f4iceH.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-BUSRV01e.js → CreatedOnDcom-en-us-BHbqZcVl.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-BuABJnvQ.js → CreatedOnDcom-es-es-BT_eFhYg.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-sjHceLRY.js → CreatedOnDcom-fr-ca-DCWCvJTS.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-D_qGXL3E.js → CreatedOnDcom-fr-fr-DpLretAn.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-D3Sb_YEi.js → CreatedOnDcom-pt-br-DmyQqot9.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-BI47Hv58.js → CreatedOnDcom-pt-pt-CRLWHfnY.js} +1 -1
- package/lib/src/brand-page/components/page-footer/PageFooter.mixin.d.ts +0 -1
- package/lib/src/brand-page/components/page-footer/PageFooter.mixin.js +1 -4
- package/lib/src/brand-page/utils/route.util.d.ts +12 -0
- package/lib/src/brand-page/utils/route.util.js +56 -0
- package/lib/src/brand-page-maker/components/index.d.ts +1 -0
- package/lib/src/brand-page-maker/components/index.js +1 -0
- package/lib/src/bundled-translations-BlUyLdcg.js +179 -0
- package/lib/src/bundled-translations.de-DE-DZm_F46B.js +179 -0
- package/lib/src/bundled-translations.es-ES-DiOMEwZz.js +179 -0
- package/lib/src/bundled-translations.fr-CA-BPBNxV69.js +179 -0
- package/lib/src/bundled-translations.fr-FR-bCNdWkJW.js +179 -0
- package/lib/src/bundled-translations.pt-BR-CC6okekB.js +179 -0
- package/lib/src/bundled-translations.pt-PT-CSgMSLFn.js +179 -0
- package/lib/src/bundles/bundled-translations.de-DE.json +5 -4
- package/lib/src/bundles/bundled-translations.es-ES.json +5 -4
- package/lib/src/bundles/bundled-translations.fr-CA.json +5 -4
- package/lib/src/bundles/bundled-translations.fr-FR.json +5 -4
- package/lib/src/bundles/bundled-translations.json +9 -4
- package/lib/src/bundles/bundled-translations.pt-BR.json +5 -4
- package/lib/src/bundles/bundled-translations.pt-PT.json +5 -4
- package/lib/src/css/library.brand-page-brandCrowd.min.css +159 -222
- package/lib/src/css/library.brand-page-brandPage.min.css +159 -222
- package/lib/src/css/library.brand-page-designCom.min.css +159 -222
- package/lib/src/css/library.brand-page-preview.min.css +159 -222
- package/lib/src/index.d.ts +1 -1
- package/lib/src/index.mjs +110 -109
- package/lib/src/{lib-BBX3eBci.js → lib-CZMytXbN.js} +19842 -19669
- package/lib/src/lib.js +1 -1
- package/lib/src/shared/components/live-preview-modal/live-preview-modal.utils.d.ts +4 -0
- package/lib/src/shared/components/live-preview-modal/live-preview-modal.utils.js +3 -0
- package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +3 -1
- package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.js +23 -11
- package/package.json +1 -1
- package/src/brand-page/components/button/Button.vue +41 -18
- package/src/brand-page/components/button/Button.vue.test.ts +211 -0
- package/src/brand-page/components/images-section/ImagesSection.test.ts +91 -0
- package/src/brand-page/components/images-section/ImagesSection.vue +29 -10
- package/src/brand-page/components/page-footer/PageFooter.mixin.ts +1 -4
- package/src/brand-page/components/page-footer/PageFooter.vue +0 -1
- package/src/brand-page/components/payment/Payment.vue +0 -3
- package/src/brand-page/components/payment/request-payment/RequestPayment.vue +0 -1
- package/src/brand-page/components/payment/support-me/SupportMe.vue +0 -3
- package/src/brand-page/components/save-contact/SaveContactWidget.vue +1 -0
- package/src/brand-page/components/save-contact/popup-card/PopupCard.vue +1 -0
- package/src/brand-page/components/shop/CartModal.test.ts +51 -0
- package/src/brand-page/components/shop/CartModal.vue +1 -0
- package/src/brand-page/components/shop/FeaturedDisplay.vue +8 -3
- package/src/brand-page/components/shop/Shop.vue +5 -5
- package/src/brand-page/components/shop/Shop.vue.test.ts +104 -2
- package/src/brand-page/components/social-links/SocialLinks.vue +1 -9
- package/src/brand-page/components/social-links/SocialLinks.vue.test.ts +28 -8
- package/src/brand-page/components/social-links/SocialLinksItems.vue +6 -2
- package/src/brand-page/components/testimonial-section/TestimonialSection.vue +2 -2
- package/src/brand-page/components/video/Video.vue +0 -3
- package/src/brand-page/utils/route.util.test.ts +116 -0
- package/src/brand-page/utils/route.util.ts +82 -0
- package/src/brand-page-maker/components/action-bar/ActionBarNavigation.test.ts +23 -0
- package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.preview-gate.test.ts +68 -3
- package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.vue +1 -0
- package/src/brand-page-maker/components/index.ts +1 -0
- package/src/brand-page-maker/components/preview-modal/PreviewModalWithState.test.ts +61 -41
- package/src/brand-page-maker/components/preview-modal/PreviewModalWithState.vue +37 -42
- package/src/bundles/bundled-translations.de-DE.json +5 -4
- package/src/bundles/bundled-translations.es-ES.json +5 -4
- package/src/bundles/bundled-translations.fr-CA.json +5 -4
- package/src/bundles/bundled-translations.fr-FR.json +5 -4
- package/src/bundles/bundled-translations.json +9 -4
- package/src/bundles/bundled-translations.pt-BR.json +5 -4
- package/src/bundles/bundled-translations.pt-PT.json +5 -4
- package/src/lib.ts +1 -0
- package/src/{brand-page-maker/components/preview-modal/PreviewModal.test.ts → shared/components/live-preview-modal/LivePreviewModal.test.ts} +96 -3
- package/src/{brand-page-maker/components/preview-modal/PreviewModal.vue → shared/components/live-preview-modal/LivePreviewModal.vue} +103 -89
- package/src/shared/components/live-preview-modal/i18n/preview-modal.de-DE.json +7 -0
- package/src/shared/components/live-preview-modal/i18n/preview-modal.es-ES.json +7 -0
- package/src/shared/components/live-preview-modal/i18n/preview-modal.fr-CA.json +7 -0
- package/src/shared/components/live-preview-modal/i18n/preview-modal.fr-FR.json +7 -0
- package/src/shared/components/live-preview-modal/i18n/preview-modal.json +7 -0
- package/src/shared/components/live-preview-modal/i18n/preview-modal.pt-BR.json +7 -0
- package/src/shared/components/live-preview-modal/i18n/preview-modal.pt-PT.json +7 -0
- package/src/shared/components/live-preview-modal/live-preview-modal.utils.ts +11 -0
- package/src/shared/ecommerce-settings/store/cart-store.test.ts +38 -0
- package/src/shared/page-settings/components/header-settings-panel/HeaderSettingsPanel.vue +205 -0
- package/src/shared/page-settings/components/page-settings/PageSettings.header-settings-panel.test.ts +97 -0
- package/src/shared/page-settings/components/page-settings/PageSettings.mixin.ts +27 -12
- package/src/shared/page-settings/components/page-settings/PageSettings.vue +113 -92
- package/src/shared/page-settings/components/page-settings/i18n/page-settings.json +4 -0
- package/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.vue +1 -57
- package/lib/src/bundled-translations-D42tE0D2.js +0 -179
- package/lib/src/bundled-translations.de-DE-D9aWGgFt.js +0 -179
- package/lib/src/bundled-translations.es-ES-lk4B0xQ7.js +0 -179
- package/lib/src/bundled-translations.fr-CA-DhOBNt6q.js +0 -179
- package/lib/src/bundled-translations.fr-FR-CnpafQeG.js +0 -179
- package/lib/src/bundled-translations.pt-BR-C4PnKrd9.js +0 -179
- package/lib/src/bundled-translations.pt-PT-CfKDVQC-.js +0 -179
- package/src/brand-page-maker/components/preview-modal/i18n/preview-modal.de-DE.json +0 -6
- package/src/brand-page-maker/components/preview-modal/i18n/preview-modal.es-ES.json +0 -6
- package/src/brand-page-maker/components/preview-modal/i18n/preview-modal.fr-CA.json +0 -6
- package/src/brand-page-maker/components/preview-modal/i18n/preview-modal.fr-FR.json +0 -6
- package/src/brand-page-maker/components/preview-modal/i18n/preview-modal.json +0 -6
- package/src/brand-page-maker/components/preview-modal/i18n/preview-modal.pt-BR.json +0 -6
- package/src/brand-page-maker/components/preview-modal/i18n/preview-modal.pt-PT.json +0 -6
package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.preview-gate.test.ts
CHANGED
|
@@ -7,16 +7,21 @@ import { EVENTS } from '../../../constants/brand-page-maker';
|
|
|
7
7
|
// flow. Post-purchase: the modal saves through the maker store itself, so it can open
|
|
8
8
|
// first and show its loader.
|
|
9
9
|
//
|
|
10
|
-
//
|
|
11
|
-
// `livePreviewUrl` watcher
|
|
10
|
+
// Pre-purchase: the HOST owns the save, so nothing opens until that save hands back a
|
|
11
|
+
// signed URL — the `livePreviewUrl` watcher is what opens the modal.
|
|
12
|
+
//
|
|
12
13
|
// Mounting the whole editor to reach these branches would dwarf what is tested, so the
|
|
13
|
-
// mixin's methods are driven directly.
|
|
14
|
+
// mixin's methods and watcher are driven directly.
|
|
14
15
|
const onPreview = (BrandPageMakerMixin as any).methods.onPreview;
|
|
16
|
+
const onLivePreviewUrlChanged = (BrandPageMakerMixin as any).watch[
|
|
17
|
+
'containerStore.livePreviewUrl'
|
|
18
|
+
];
|
|
15
19
|
|
|
16
20
|
const createContext = (
|
|
17
21
|
overrides: {
|
|
18
22
|
isUserAuthenticated?: boolean;
|
|
19
23
|
isPreviewModalVisible?: boolean;
|
|
24
|
+
containerStore?: { isPrePurchase: boolean; livePreviewUrl?: string };
|
|
20
25
|
} = {},
|
|
21
26
|
) => ({
|
|
22
27
|
isUserAuthenticated: true,
|
|
@@ -45,4 +50,64 @@ describe('BrandPageMaker.onPreview', () => {
|
|
|
45
50
|
expect(context.$emit).toHaveBeenCalledWith(EVENTS.Preview);
|
|
46
51
|
expect(context.$emit).not.toHaveBeenCalledWith(EVENTS.LivePreviewClicked);
|
|
47
52
|
});
|
|
53
|
+
|
|
54
|
+
// Opening here would mean waiting inside the modal on a host save it can't see the
|
|
55
|
+
// outcome of — which is how the loader used to hang forever (BP-5870).
|
|
56
|
+
test('pre-purchase asks the host to save and stays closed', () => {
|
|
57
|
+
const context = createContext({
|
|
58
|
+
containerStore: { isPrePurchase: true, livePreviewUrl: 'https://stale' },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
onPreview.call(context);
|
|
62
|
+
|
|
63
|
+
expect(context.isPreviewModalVisible).toBe(false);
|
|
64
|
+
expect(context.$emit).toHaveBeenCalledWith(EVENTS.LivePreviewClicked);
|
|
65
|
+
expect(context.$emit).not.toHaveBeenCalledWith(EVENTS.Preview);
|
|
66
|
+
// A stale URL from an earlier preview must not open the frame on the next click.
|
|
67
|
+
expect(context.containerStore.livePreviewUrl).toBeUndefined();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('BrandPageMaker livePreviewUrl watcher', () => {
|
|
72
|
+
test('opens the preview once the host save hands back a URL', () => {
|
|
73
|
+
const context = createContext({
|
|
74
|
+
containerStore: { isPrePurchase: true, livePreviewUrl: undefined },
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
onLivePreviewUrlChanged.call(
|
|
78
|
+
context,
|
|
79
|
+
'https://brand.site/_p/request?token=x',
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
expect(context.isPreviewModalVisible).toBe(true);
|
|
83
|
+
expect(context.$emit).toHaveBeenCalledWith(EVENTS.Preview);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// A failed save or a customer sent to register never sets a URL, and the editor has
|
|
87
|
+
// already surfaced that — so nothing should open.
|
|
88
|
+
test('stays closed when the save produced no URL', () => {
|
|
89
|
+
const context = createContext({
|
|
90
|
+
containerStore: { isPrePurchase: true, livePreviewUrl: undefined },
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
onLivePreviewUrlChanged.call(context, undefined);
|
|
94
|
+
|
|
95
|
+
expect(context.isPreviewModalVisible).toBe(false);
|
|
96
|
+
expect(context.$emit).not.toHaveBeenCalledWith(EVENTS.Preview);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Post-purchase opens the modal itself, so the watcher must not double-open it.
|
|
100
|
+
test('ignores post-purchase, which opens the modal itself', () => {
|
|
101
|
+
const context = createContext({
|
|
102
|
+
containerStore: { isPrePurchase: false, livePreviewUrl: undefined },
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
onLivePreviewUrlChanged.call(
|
|
106
|
+
context,
|
|
107
|
+
'https://brand.site/_p/request?token=x',
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
expect(context.isPreviewModalVisible).toBe(false);
|
|
111
|
+
expect(context.$emit).not.toHaveBeenCalledWith(EVENTS.Preview);
|
|
112
|
+
});
|
|
48
113
|
});
|
|
@@ -5,3 +5,4 @@ export { default as SensitiveContent } from './sensitive-content/SensitiveConten
|
|
|
5
5
|
export { default as SeoPageSettings } from './seo-settings/SeoPageSettings.vue';
|
|
6
6
|
export { default as GoogleAnalytics } from './google-analytics/GoogleAnalytics.vue';
|
|
7
7
|
export { default as PublishBrandPageModalWithState } from '@shared/publish-brand-page-with-state/PublishBrandPageModalWithState.vue';
|
|
8
|
+
export { default as PreviewModalWithState } from './preview-modal/PreviewModalWithState.vue';
|
|
@@ -2,12 +2,11 @@ import { describe, test, expect, beforeEach, vi } from 'vitest';
|
|
|
2
2
|
import { createPinia, setActivePinia } from 'pinia';
|
|
3
3
|
import { flushPromises, shallowMount } from '@vue/test-utils';
|
|
4
4
|
import PreviewModalWithState from './PreviewModalWithState.vue';
|
|
5
|
-
import
|
|
5
|
+
import LivePreviewModal from '@shared/components/live-preview-modal/LivePreviewModal.vue';
|
|
6
6
|
import {
|
|
7
7
|
useBrandPageContainerStore,
|
|
8
|
-
|
|
8
|
+
useBrandPageMakerStore,
|
|
9
9
|
} from '@brand-page-maker/stores';
|
|
10
|
-
import { UI_SCREENSIZE_MODES } from '@shared/models';
|
|
11
10
|
|
|
12
11
|
const PREVIEW_URL = 'https://brand.site/_p/request?token=signed';
|
|
13
12
|
|
|
@@ -17,7 +16,7 @@ const mountModal = () =>
|
|
|
17
16
|
});
|
|
18
17
|
|
|
19
18
|
const modalProps = (wrapper: ReturnType<typeof mountModal>) =>
|
|
20
|
-
wrapper.findComponent(
|
|
19
|
+
wrapper.findComponent(LivePreviewModal).props();
|
|
21
20
|
|
|
22
21
|
describe('PreviewModalWithState', () => {
|
|
23
22
|
let containerStore: ReturnType<typeof useBrandPageContainerStore>;
|
|
@@ -44,7 +43,6 @@ describe('PreviewModalWithState', () => {
|
|
|
44
43
|
|
|
45
44
|
const props = modalProps(wrapper);
|
|
46
45
|
|
|
47
|
-
expect(props.loading).toBe(false);
|
|
48
46
|
expect(props.errored).toBe(false);
|
|
49
47
|
expect(props.previewUrl).toBe(PREVIEW_URL);
|
|
50
48
|
});
|
|
@@ -98,9 +96,9 @@ describe('PreviewModalWithState', () => {
|
|
|
98
96
|
expect(props.previewUrl).toBe('');
|
|
99
97
|
});
|
|
100
98
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
99
|
+
// The shell now owns device state (toggle visibility + the small-screen desktop
|
|
100
|
+
// force); the wrapper only seeds the page type's natural default.
|
|
101
|
+
test('seeds the shell with the page type default device mode', async () => {
|
|
104
102
|
vi.spyOn(containerStore, 'saveBrandPage').mockResolvedValue({
|
|
105
103
|
saved: true,
|
|
106
104
|
canPublish: true,
|
|
@@ -111,37 +109,8 @@ describe('PreviewModalWithState', () => {
|
|
|
111
109
|
await wrapper.setProps({ visible: true });
|
|
112
110
|
await flushPromises();
|
|
113
111
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
// Not 'mobile' — that mode draws a phone bezel, which on a phone would be a
|
|
117
|
-
// device frame inside a device.
|
|
118
|
-
expect(props.deviceMode).toBe('desktop');
|
|
119
|
-
expect(props.showDeviceToggle).toBe(false);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// The mobile work must not reach desktop — the toggle is still there and it
|
|
123
|
-
// still drives the frame.
|
|
124
|
-
test('desktop keeps the device toggle and honours the viewport it picks', async () => {
|
|
125
|
-
useUiStore().screenSizeMode = UI_SCREENSIZE_MODES.desktop;
|
|
126
|
-
|
|
127
|
-
vi.spyOn(containerStore, 'saveBrandPage').mockResolvedValue({
|
|
128
|
-
saved: true,
|
|
129
|
-
canPublish: true,
|
|
130
|
-
} as any);
|
|
131
|
-
|
|
132
|
-
const wrapper = mountModal();
|
|
133
|
-
|
|
134
|
-
await wrapper.setProps({ visible: true });
|
|
135
|
-
await flushPromises();
|
|
136
|
-
|
|
137
|
-
expect(modalProps(wrapper).showDeviceToggle).toBe(true);
|
|
138
|
-
|
|
139
|
-
await wrapper
|
|
140
|
-
.findComponent(PreviewModal)
|
|
141
|
-
.vm.$emit('update:deviceMode', 'mobile');
|
|
142
|
-
await flushPromises();
|
|
143
|
-
|
|
144
|
-
expect(modalProps(wrapper).deviceMode).toBe('mobile');
|
|
112
|
+
// A link-in-bio (default) page is mobile-first.
|
|
113
|
+
expect(modalProps(wrapper).defaultDeviceMode).toBe('mobile');
|
|
145
114
|
});
|
|
146
115
|
|
|
147
116
|
// The preview sits at z-1000 and drops to z-40 for a host flow modal. Tracking
|
|
@@ -180,7 +149,7 @@ describe('PreviewModalWithState', () => {
|
|
|
180
149
|
await wrapper.setProps({ visible: true });
|
|
181
150
|
await flushPromises();
|
|
182
151
|
|
|
183
|
-
await wrapper.findComponent(
|
|
152
|
+
await wrapper.findComponent(LivePreviewModal).vm.$emit('publish');
|
|
184
153
|
await flushPromises();
|
|
185
154
|
|
|
186
155
|
// The host (BrandPageMaker → bc-net) runs the actual publish flow so
|
|
@@ -207,13 +176,42 @@ describe('PreviewModalWithState', () => {
|
|
|
207
176
|
|
|
208
177
|
expect(showSaveSuccessMessage).not.toHaveBeenCalled();
|
|
209
178
|
|
|
210
|
-
await wrapper.findComponent(
|
|
179
|
+
await wrapper.findComponent(LivePreviewModal).vm.$emit('close');
|
|
211
180
|
await flushPromises();
|
|
212
181
|
|
|
213
182
|
expect(wrapper.emitted('close')).toHaveLength(1);
|
|
214
183
|
expect(showSaveSuccessMessage).toHaveBeenCalledOnce();
|
|
215
184
|
});
|
|
216
185
|
|
|
186
|
+
// Pre-purchase skips saveBrandPage in openPreview (the host already saved), so
|
|
187
|
+
// containerStore.triggerSaveSuccessMessage is never armed for showSaveSuccessMessage
|
|
188
|
+
// to gate on — announce the host's save directly instead, same as it does elsewhere.
|
|
189
|
+
test('raises the save banner directly on close when pre-purchase', async () => {
|
|
190
|
+
containerStore.isPrePurchase = true;
|
|
191
|
+
|
|
192
|
+
const showSaveSuccessMessage = vi.spyOn(
|
|
193
|
+
containerStore,
|
|
194
|
+
'showSaveSuccessMessage',
|
|
195
|
+
);
|
|
196
|
+
const enqueueSaveSuccessMessage = vi
|
|
197
|
+
.spyOn(useBrandPageMakerStore(), 'enqueueSaveSuccessMessage')
|
|
198
|
+
.mockResolvedValue(undefined);
|
|
199
|
+
|
|
200
|
+
const wrapper = mountModal();
|
|
201
|
+
|
|
202
|
+
await wrapper.setProps({ visible: true });
|
|
203
|
+
await flushPromises();
|
|
204
|
+
|
|
205
|
+
await wrapper.findComponent(LivePreviewModal).vm.$emit('close');
|
|
206
|
+
await flushPromises();
|
|
207
|
+
|
|
208
|
+
expect(wrapper.emitted('close')).toHaveLength(1);
|
|
209
|
+
expect(enqueueSaveSuccessMessage).toHaveBeenCalledWith({
|
|
210
|
+
noDescription: true,
|
|
211
|
+
});
|
|
212
|
+
expect(showSaveSuccessMessage).not.toHaveBeenCalled();
|
|
213
|
+
});
|
|
214
|
+
|
|
217
215
|
// BP-5957: the modal uppercases its labels on BrandCrowd, not Design.com.
|
|
218
216
|
test.each([true, false])(
|
|
219
217
|
'passes isDesignCom=%s to the modal',
|
|
@@ -223,4 +221,26 @@ describe('PreviewModalWithState', () => {
|
|
|
223
221
|
expect(modalProps(mountModal()).isDesignCom).toBe(isDesignCom);
|
|
224
222
|
},
|
|
225
223
|
);
|
|
224
|
+
|
|
225
|
+
// The editor keeps its publish CTA with no prop; other surfaces override it.
|
|
226
|
+
test('defaults the CTA to publish so the editor is unchanged', () => {
|
|
227
|
+
expect(modalProps(mountModal()).cta).toBe('publish');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test('passes the surface CTA through to the shell', () => {
|
|
231
|
+
const wrapper = shallowMount(PreviewModalWithState, {
|
|
232
|
+
props: { visible: false, cta: 'edit' },
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
expect(wrapper.findComponent(LivePreviewModal).props().cta).toBe('edit');
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Manage surface: the shell's Edit CTA is relayed so the host navigates to the editor.
|
|
239
|
+
test('relays the shell edit CTA to the host', async () => {
|
|
240
|
+
const wrapper = mountModal();
|
|
241
|
+
|
|
242
|
+
await wrapper.findComponent(LivePreviewModal).vm.$emit('edit');
|
|
243
|
+
|
|
244
|
+
expect(wrapper.emitted('edit')).toHaveLength(1);
|
|
245
|
+
});
|
|
226
246
|
});
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<LivePreviewModal
|
|
3
3
|
data-component="preview-modal-with-state"
|
|
4
4
|
:visible="visible"
|
|
5
5
|
:preview-url="previewUrl"
|
|
6
|
-
:edit-label="previewModalTr('edit')"
|
|
7
6
|
:cta="cta"
|
|
8
|
-
:device-mode="
|
|
9
|
-
:show-device-toggle="!isMobileScreenSize"
|
|
7
|
+
:default-device-mode="defaultDeviceMode"
|
|
10
8
|
:is-design-com="containerStore.isDesignCom"
|
|
11
|
-
:loading="loading"
|
|
12
9
|
:errored="errored"
|
|
13
10
|
:publishing="publishing"
|
|
14
11
|
:flow-modal-open="isFlowModalOpen"
|
|
15
|
-
@
|
|
16
|
-
@edit="onClose"
|
|
12
|
+
@edit="onEdit"
|
|
17
13
|
@publish="onPublish"
|
|
18
14
|
@retry="openPreview"
|
|
19
15
|
@close="onClose"
|
|
@@ -22,57 +18,42 @@
|
|
|
22
18
|
|
|
23
19
|
<script setup lang="ts">
|
|
24
20
|
import { ref, computed, watch, type PropType } from 'vue';
|
|
25
|
-
import
|
|
21
|
+
import LivePreviewModal from '@shared/components/live-preview-modal/LivePreviewModal.vue';
|
|
22
|
+
import {
|
|
23
|
+
getDefaultPreviewDeviceMode,
|
|
24
|
+
type LivePreviewCta,
|
|
25
|
+
} from '@shared/components/live-preview-modal/live-preview-modal.utils';
|
|
26
26
|
import {
|
|
27
27
|
useBrandPageContainerStore,
|
|
28
28
|
useBrandPageMakerStore,
|
|
29
|
-
useUiStore,
|
|
30
29
|
} from '@brand-page-maker/stores';
|
|
31
|
-
import {
|
|
32
|
-
import { previewModalTr } from '@src/composables/useTranslate';
|
|
33
|
-
import { UI_SCREENSIZE_MODES, type UiDeviceMode } from '@shared/models';
|
|
30
|
+
import type { UiDeviceMode } from '@shared/models';
|
|
34
31
|
|
|
35
32
|
const props = defineProps({
|
|
36
33
|
visible: { type: Boolean, default: false },
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
default: 'publish',
|
|
40
|
-
},
|
|
34
|
+
// Per-surface header CTA; defaults to 'publish' so the editor is unchanged.
|
|
35
|
+
cta: { type: String as PropType<LivePreviewCta>, default: 'publish' },
|
|
41
36
|
});
|
|
42
37
|
|
|
43
|
-
const emit = defineEmits<{ close: []; publish: [] }>();
|
|
38
|
+
const emit = defineEmits<{ close: []; publish: []; edit: [] }>();
|
|
44
39
|
|
|
45
40
|
const containerStore = useBrandPageContainerStore();
|
|
46
41
|
const makerStore = useBrandPageMakerStore();
|
|
47
|
-
const uiStore = useUiStore();
|
|
48
|
-
|
|
49
|
-
const isMobileScreenSize = computed(
|
|
50
|
-
() => uiStore.screenSizeMode === UI_SCREENSIZE_MODES.mobile,
|
|
51
|
-
);
|
|
52
42
|
|
|
53
43
|
// The preview must step back for any modal opening over the editor.
|
|
54
44
|
const isFlowModalOpen = computed(() => containerStore.isAnyModalVisible);
|
|
55
45
|
|
|
56
|
-
//
|
|
57
|
-
const defaultDeviceMode = ()
|
|
58
|
-
makerStore.brandPageType
|
|
59
|
-
? 'desktop'
|
|
60
|
-
: 'mobile';
|
|
61
|
-
|
|
62
|
-
const deviceMode = ref<UiDeviceMode>(defaultDeviceMode());
|
|
63
|
-
|
|
64
|
-
const effectiveDeviceMode = computed<UiDeviceMode>(() =>
|
|
65
|
-
isMobileScreenSize.value ? 'desktop' : deviceMode.value,
|
|
46
|
+
// Page type's default; the shell handles the small-screen case itself.
|
|
47
|
+
const defaultDeviceMode = computed<UiDeviceMode>(() =>
|
|
48
|
+
getDefaultPreviewDeviceMode(makerStore.brandPageType),
|
|
66
49
|
);
|
|
67
50
|
|
|
68
51
|
const previewUrl = ref('');
|
|
69
|
-
const loading = ref(false);
|
|
70
52
|
const errored = ref(false);
|
|
71
53
|
const publishing = ref(false);
|
|
72
54
|
|
|
73
55
|
// Save first so the preview is never stale; saveOnly stops a live site republishing.
|
|
74
56
|
const openPreview = async (): Promise<void> => {
|
|
75
|
-
loading.value = true;
|
|
76
57
|
errored.value = false;
|
|
77
58
|
previewUrl.value = '';
|
|
78
59
|
|
|
@@ -82,7 +63,6 @@ const openPreview = async (): Promise<void> => {
|
|
|
82
63
|
|
|
83
64
|
if (!result || !result.saved) {
|
|
84
65
|
errored.value = true;
|
|
85
|
-
loading.value = false;
|
|
86
66
|
|
|
87
67
|
return;
|
|
88
68
|
}
|
|
@@ -93,22 +73,38 @@ const openPreview = async (): Promise<void> => {
|
|
|
93
73
|
|
|
94
74
|
if (!url) {
|
|
95
75
|
errored.value = true;
|
|
96
|
-
loading.value = false;
|
|
97
76
|
|
|
98
77
|
return;
|
|
99
78
|
}
|
|
100
79
|
|
|
101
80
|
previewUrl.value = url;
|
|
102
|
-
loading.value = false;
|
|
103
81
|
};
|
|
104
82
|
|
|
105
|
-
// The preview covers the editor, so its save is announced on the way out
|
|
106
|
-
//
|
|
83
|
+
// The preview covers the editor, so its save is announced on the way out — from either
|
|
84
|
+
// exit. Pre-purchase already saved before the preview opened (host-owned), so there's
|
|
85
|
+
// nothing armed on containerStore for it to gate — announce that save directly instead.
|
|
86
|
+
// Post-purchase routes through the store's own armed flag (no-ops if it wasn't ours, or
|
|
87
|
+
// was already announced by publish).
|
|
88
|
+
const announceExit = async (): Promise<void> => {
|
|
89
|
+
if (containerStore.isPrePurchase) {
|
|
90
|
+
await makerStore.enqueueSaveSuccessMessage({ noDescription: true });
|
|
91
|
+
} else {
|
|
92
|
+
await containerStore.showSaveSuccessMessage();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
await makerStore.showNextNotificationMessage();
|
|
96
|
+
};
|
|
97
|
+
|
|
107
98
|
const onClose = async (): Promise<void> => {
|
|
108
99
|
emit('close');
|
|
109
100
|
|
|
110
|
-
await
|
|
111
|
-
|
|
101
|
+
await announceExit();
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const onEdit = async (): Promise<void> => {
|
|
105
|
+
emit('edit');
|
|
106
|
+
|
|
107
|
+
await announceExit();
|
|
112
108
|
};
|
|
113
109
|
|
|
114
110
|
const onPublish = (): void => {
|
|
@@ -121,7 +117,6 @@ watch(
|
|
|
121
117
|
() => props.visible,
|
|
122
118
|
(isVisible) => {
|
|
123
119
|
if (isVisible) {
|
|
124
|
-
deviceMode.value = defaultDeviceMode();
|
|
125
120
|
openPreview();
|
|
126
121
|
} else {
|
|
127
122
|
previewUrl.value = '';
|
|
@@ -1126,10 +1126,6 @@
|
|
|
1126
1126
|
"linkInBioFriendlyName": "Link in der Biografie",
|
|
1127
1127
|
"digitalBusinessCardFriendlyName": "Digitale Visitenkarte"
|
|
1128
1128
|
},
|
|
1129
|
-
"previewModal": {
|
|
1130
|
-
"edit": "Bearbeiten",
|
|
1131
|
-
"retry": "Erneut versuchen"
|
|
1132
|
-
},
|
|
1133
1129
|
"quickLinkBar": {
|
|
1134
1130
|
"myWebPageLabel": "Meine Webseite:",
|
|
1135
1131
|
"copyButton": "Kopieren",
|
|
@@ -1429,6 +1425,11 @@
|
|
|
1429
1425
|
"contactFormFieldRequired": "Please fill out this field.",
|
|
1430
1426
|
"contactFormFieldTooLong": "The field you entered is too long. The maximum length is {{MAX_LENGTH}}."
|
|
1431
1427
|
},
|
|
1428
|
+
"previewModal": {
|
|
1429
|
+
"edit": "Website bearbeiten",
|
|
1430
|
+
"backToEditor": "Zurück zum Editor",
|
|
1431
|
+
"retry": "Erneut versuchen"
|
|
1432
|
+
},
|
|
1432
1433
|
"confirmModal": {
|
|
1433
1434
|
"confirmMessageTitle": "Sind Sie sicher?",
|
|
1434
1435
|
"noLabel": "Nein",
|
|
@@ -1126,10 +1126,6 @@
|
|
|
1126
1126
|
"linkInBioFriendlyName": "Enlace en la bio",
|
|
1127
1127
|
"digitalBusinessCardFriendlyName": "Tarjeta de visita digital"
|
|
1128
1128
|
},
|
|
1129
|
-
"previewModal": {
|
|
1130
|
-
"edit": "Editar",
|
|
1131
|
-
"retry": "Vuelve a intentarlo"
|
|
1132
|
-
},
|
|
1133
1129
|
"quickLinkBar": {
|
|
1134
1130
|
"myWebPageLabel": "Mi página web:",
|
|
1135
1131
|
"copyButton": "Copiar",
|
|
@@ -1429,6 +1425,11 @@
|
|
|
1429
1425
|
"contactFormFieldRequired": "Please fill out this field.",
|
|
1430
1426
|
"contactFormFieldTooLong": "The field you entered is too long. The maximum length is {{MAX_LENGTH}}."
|
|
1431
1427
|
},
|
|
1428
|
+
"previewModal": {
|
|
1429
|
+
"edit": "Edita el sitio web",
|
|
1430
|
+
"backToEditor": "Volver al editor",
|
|
1431
|
+
"retry": "Vuelve a intentarlo"
|
|
1432
|
+
},
|
|
1432
1433
|
"confirmModal": {
|
|
1433
1434
|
"confirmMessageTitle": "¿Estás seguro?",
|
|
1434
1435
|
"noLabel": "No",
|
|
@@ -1126,10 +1126,6 @@
|
|
|
1126
1126
|
"linkInBioFriendlyName": "Lien dans la bio",
|
|
1127
1127
|
"digitalBusinessCardFriendlyName": "Carte de visite numérique"
|
|
1128
1128
|
},
|
|
1129
|
-
"previewModal": {
|
|
1130
|
-
"edit": "Éditer",
|
|
1131
|
-
"retry": "Réessayez"
|
|
1132
|
-
},
|
|
1133
1129
|
"quickLinkBar": {
|
|
1134
1130
|
"myWebPageLabel": "Ma page Web :",
|
|
1135
1131
|
"copyButton": "Copier",
|
|
@@ -1429,6 +1425,11 @@
|
|
|
1429
1425
|
"contactFormFieldRequired": "Please fill out this field.",
|
|
1430
1426
|
"contactFormFieldTooLong": "The field you entered is too long. The maximum length is {{MAX_LENGTH}}."
|
|
1431
1427
|
},
|
|
1428
|
+
"previewModal": {
|
|
1429
|
+
"edit": "Modifier le site Web",
|
|
1430
|
+
"backToEditor": "Retour à l'éditeur",
|
|
1431
|
+
"retry": "Réessayez"
|
|
1432
|
+
},
|
|
1432
1433
|
"confirmModal": {
|
|
1433
1434
|
"confirmMessageTitle": "Êtes-vous sûr ?",
|
|
1434
1435
|
"noLabel": "Non",
|
|
@@ -1126,10 +1126,6 @@
|
|
|
1126
1126
|
"linkInBioFriendlyName": "Lien dans la bio",
|
|
1127
1127
|
"digitalBusinessCardFriendlyName": "Carte de visite numérique"
|
|
1128
1128
|
},
|
|
1129
|
-
"previewModal": {
|
|
1130
|
-
"edit": "Éditer",
|
|
1131
|
-
"retry": "Réessayez"
|
|
1132
|
-
},
|
|
1133
1129
|
"quickLinkBar": {
|
|
1134
1130
|
"myWebPageLabel": "Ma page Web :",
|
|
1135
1131
|
"copyButton": "Copier",
|
|
@@ -1429,6 +1425,11 @@
|
|
|
1429
1425
|
"contactFormFieldRequired": "Please fill out this field.",
|
|
1430
1426
|
"contactFormFieldTooLong": "The field you entered is too long. The maximum length is {{MAX_LENGTH}}."
|
|
1431
1427
|
},
|
|
1428
|
+
"previewModal": {
|
|
1429
|
+
"edit": "Modifier le site Web",
|
|
1430
|
+
"backToEditor": "Retour à l'éditeur",
|
|
1431
|
+
"retry": "Réessayez"
|
|
1432
|
+
},
|
|
1432
1433
|
"confirmModal": {
|
|
1433
1434
|
"confirmMessageTitle": "Êtes-vous sûr ?",
|
|
1434
1435
|
"noLabel": "Non",
|
|
@@ -1126,10 +1126,6 @@
|
|
|
1126
1126
|
"linkInBioFriendlyName": "Link in Bio",
|
|
1127
1127
|
"digitalBusinessCardFriendlyName": "Digital Business Card"
|
|
1128
1128
|
},
|
|
1129
|
-
"previewModal": {
|
|
1130
|
-
"edit": "Edit",
|
|
1131
|
-
"retry": "Retry"
|
|
1132
|
-
},
|
|
1133
1129
|
"quickLinkBar": {
|
|
1134
1130
|
"myWebPageLabel": "My web page:",
|
|
1135
1131
|
"copyButton": "Copy",
|
|
@@ -1429,6 +1425,11 @@
|
|
|
1429
1425
|
"contactFormFieldRequired": "Please fill out this field.",
|
|
1430
1426
|
"contactFormFieldTooLong": "The field you entered is too long. The maximum length is {{MAX_LENGTH}}."
|
|
1431
1427
|
},
|
|
1428
|
+
"previewModal": {
|
|
1429
|
+
"edit": "Edit Website",
|
|
1430
|
+
"backToEditor": "Back to Editor",
|
|
1431
|
+
"retry": "Retry"
|
|
1432
|
+
},
|
|
1432
1433
|
"confirmModal": {
|
|
1433
1434
|
"confirmMessageTitle": "Are you sure?",
|
|
1434
1435
|
"noLabel": "No",
|
|
@@ -1832,6 +1833,10 @@
|
|
|
1832
1833
|
},
|
|
1833
1834
|
"pageSettings": {
|
|
1834
1835
|
"headerSettings": "Header Settings",
|
|
1836
|
+
"navigationSectionTitle": "Navigation",
|
|
1837
|
+
"headerSectionTitle": "Header",
|
|
1838
|
+
"logoSectionTitle": "Logo",
|
|
1839
|
+
"buttonSectionTitle": "Button",
|
|
1835
1840
|
"footerSettings": "Footer Settings",
|
|
1836
1841
|
"managePages": "Manage Pages",
|
|
1837
1842
|
"pages": "Pages",
|
|
@@ -1126,10 +1126,6 @@
|
|
|
1126
1126
|
"linkInBioFriendlyName": "Link na Bio",
|
|
1127
1127
|
"digitalBusinessCardFriendlyName": "Cartão de visita digital"
|
|
1128
1128
|
},
|
|
1129
|
-
"previewModal": {
|
|
1130
|
-
"edit": "Editar",
|
|
1131
|
-
"retry": "Tente novamente"
|
|
1132
|
-
},
|
|
1133
1129
|
"quickLinkBar": {
|
|
1134
1130
|
"myWebPageLabel": "Minha página web:",
|
|
1135
1131
|
"copyButton": "Copiar",
|
|
@@ -1429,6 +1425,11 @@
|
|
|
1429
1425
|
"contactFormFieldRequired": "Please fill out this field.",
|
|
1430
1426
|
"contactFormFieldTooLong": "The field you entered is too long. The maximum length is {{MAX_LENGTH}}."
|
|
1431
1427
|
},
|
|
1428
|
+
"previewModal": {
|
|
1429
|
+
"edit": "Editar site",
|
|
1430
|
+
"backToEditor": "Voltar para o editor",
|
|
1431
|
+
"retry": "Tente novamente"
|
|
1432
|
+
},
|
|
1432
1433
|
"confirmModal": {
|
|
1433
1434
|
"confirmMessageTitle": "Tem a certeza?",
|
|
1434
1435
|
"noLabel": "Não",
|
|
@@ -1126,10 +1126,6 @@
|
|
|
1126
1126
|
"linkInBioFriendlyName": "Link na biografia",
|
|
1127
1127
|
"digitalBusinessCardFriendlyName": "Cartão de visita digital"
|
|
1128
1128
|
},
|
|
1129
|
-
"previewModal": {
|
|
1130
|
-
"edit": "Editar",
|
|
1131
|
-
"retry": "Repetir"
|
|
1132
|
-
},
|
|
1133
1129
|
"quickLinkBar": {
|
|
1134
1130
|
"myWebPageLabel": "A minha página Web:",
|
|
1135
1131
|
"copyButton": "Copiar",
|
|
@@ -1429,6 +1425,11 @@
|
|
|
1429
1425
|
"contactFormFieldRequired": "Please fill out this field.",
|
|
1430
1426
|
"contactFormFieldTooLong": "The field you entered is too long. The maximum length is {{MAX_LENGTH}}."
|
|
1431
1427
|
},
|
|
1428
|
+
"previewModal": {
|
|
1429
|
+
"edit": "Editar Website",
|
|
1430
|
+
"backToEditor": "Voltar ao Editor",
|
|
1431
|
+
"retry": "Repetir"
|
|
1432
|
+
},
|
|
1432
1433
|
"confirmModal": {
|
|
1433
1434
|
"confirmMessageTitle": "Tem a certeza?",
|
|
1434
1435
|
"noLabel": "Não",
|