From 64a2ae18cb074d741c4433d3f3b91b352a1827e7 Mon Sep 17 00:00:00 2001 From: Javier Iglesias Date: Fri, 9 Sep 2022 10:28:00 +0200 Subject: [PATCH 1/6] feat: add `uiLang` parameter EX-6855 --- .../src/x-installer/api/api.types.ts | 14 +- .../x-installer/__tests__/x-installer.spec.ts | 134 ++++++++++++------ .../x-installer/x-installer/x-installer.ts | 29 +++- .../snippet-config-extra-params.vue | 2 +- packages/x-utils/src/types/index.ts | 1 + packages/x-utils/src/types/object.types.ts | 12 ++ 6 files changed, 142 insertions(+), 50 deletions(-) create mode 100644 packages/x-utils/src/types/object.types.ts diff --git a/packages/x-components/src/x-installer/api/api.types.ts b/packages/x-components/src/x-installer/api/api.types.ts index 1c27b6f97c..351204e669 100644 --- a/packages/x-components/src/x-installer/api/api.types.ts +++ b/packages/x-components/src/x-installer/api/api.types.ts @@ -1,3 +1,4 @@ +import { RequiredProperties } from '@empathyco/x-utils'; import { XBus } from '../../plugins/x-bus.types'; import { DocumentDirection } from '../../plugins/x-plugin.types'; import { XEvent, XEventPayload } from '../../wiring/events.types'; @@ -94,13 +95,13 @@ export interface SnippetConfig { /** Customer instance. */ instance: string; /** Backend services environment. */ - env?: 'live' | 'staging'; + env?: 'staging'; /** Execution scope (desktop, mobile, app, ...). */ scope: string; /** Language to display. */ lang: string; - /** Language to send to backend services. */ - searchLang?: string; + /** Language to use for the messages. Defaults to {@link SnippetConfig.lang}. */ + uiLang?: string; /** User GDPR consent. */ consent?: boolean; /** Document direction. */ @@ -121,6 +122,13 @@ export interface SnippetConfig { [extra: string]: unknown; } +/** + * A normalised version of the snippet config. + * + * @public + */ +export type NormalisedSnippetConfig = RequiredProperties; + /** * Information to render a query preview with. * diff --git a/packages/x-components/src/x-installer/x-installer/__tests__/x-installer.spec.ts b/packages/x-components/src/x-installer/x-installer/__tests__/x-installer.spec.ts index 7071618f54..d69d5f7031 100644 --- a/packages/x-components/src/x-installer/x-installer/__tests__/x-installer.spec.ts +++ b/packages/x-components/src/x-installer/x-installer/__tests__/x-installer.spec.ts @@ -8,6 +8,7 @@ import { XComponentsAdapterDummy } from '../../../__tests__/adapter.dummy'; import { AnyXModule } from '../../../x-modules/x-modules.types'; import { InitWrapper, InstallXOptions } from '../types'; import { XInstaller } from '../x-installer'; +import { SnippetConfig } from '../../api/index'; describe('testing `XInstaller` utility', () => { const adapter = XComponentsAdapterDummy; @@ -25,11 +26,30 @@ describe('testing `XInstaller` utility', () => { mounted: jest.fn() }; - const snippetConfig = { + const getMinimumSnippetConfig = (): SnippetConfig => ({ instance: 'test', lang: 'test', scope: 'test' - }; + }); + + /** + * Creates a Vue component injecting the snippet config. + * + * @param snippetProperty + * @returns A Vue component with the injected snippet config. + * + * @internal + */ + function createSnippetConfigComponent( + snippetProperty: keyof SnippetConfig = 'instance' + ): VueConstructor { + return Vue.extend({ + inject: ['snippetConfig'], + render(h) { + return h('h1', [(this as any).snippetConfig[snippetProperty]]); + } + }); + } beforeEach(() => { delete window.initX; @@ -45,7 +65,7 @@ describe('testing `XInstaller` utility', () => { __PRIVATE__xModules, initialXModules: [initialXModule], vue: createLocalVue() - }).init(snippetConfig); + }).init(getMinimumSnippetConfig()); const params = xPluginMock.install.mock.calls[0][1]; expect(xPluginMock.install).toHaveBeenCalledTimes(1); @@ -59,21 +79,23 @@ describe('testing `XInstaller` utility', () => { it('creates the public API in global scope by default', () => { delete window.InterfaceX; - new XInstaller({ adapter, plugin, vue: createLocalVue() }).init(snippetConfig); + new XInstaller({ adapter, plugin, vue: createLocalVue() }).init(getMinimumSnippetConfig()); expect(window.InterfaceX).toBeDefined(); delete window.InterfaceX; }); it('does not create the public API passing the api parameter to false', () => { - new XInstaller({ adapter, plugin, api: false, vue: createLocalVue() }).init(snippetConfig); + new XInstaller({ adapter, plugin, api: false, vue: createLocalVue() }).init( + getMinimumSnippetConfig() + ); expect(window.InterfaceX).not.toBeDefined(); }); it('installs the XPlugin using the passed vue', () => { const localVue = createLocalVue(); - new XInstaller({ adapter, plugin, vue: localVue }).init(snippetConfig); + new XInstaller({ adapter, plugin, vue: localVue }).init(getMinimumSnippetConfig()); const vueParam = xPluginMock.install.mock.calls[0][0]; expect(xPluginMock.install).toHaveBeenCalledTimes(1); @@ -82,7 +104,7 @@ describe('testing `XInstaller` utility', () => { it('creates a Vue application using the component passed in the app option', async () => { await new XInstaller({ adapter, plugin, app: component, vue: createLocalVue() }).init( - snippetConfig + getMinimumSnippetConfig() ); // eslint-disable-next-line @typescript-eslint/unbound-method @@ -102,7 +124,7 @@ describe('testing `XInstaller` utility', () => { vue, vueOptions, app: component - }).init(snippetConfig); + }).init(getMinimumSnippetConfig()); expect(app).toHaveProperty('testMethod'); expect(app).toHaveProperty('$router'); @@ -128,40 +150,40 @@ describe('testing `XInstaller` utility', () => { }; }, app: component - }).init(snippetConfig); + }).init(getMinimumSnippetConfig()); expect(app).toHaveProperty('$router'); expect(app).toHaveProperty('bus'); - expect(app).toHaveProperty('snippet', snippetConfig); + expect(app).toHaveProperty('snippet', { ...getMinimumSnippetConfig(), uiLang: 'test' }); }); it('initializes the app with the provided snippet config', async () => { const vue = createLocalVue(); - const { app } = await new XInstaller({ + const { app, api } = await new XInstaller({ adapter, vue, - app: injectSnippetConfigComponent() - }).init(snippetConfig); + app: createSnippetConfigComponent() + }).init(getMinimumSnippetConfig()); - expect(app?.$el).toHaveTextContent(snippetConfig.instance); + expect(app?.$el).toHaveTextContent(getMinimumSnippetConfig().instance); - snippetConfig.instance = 'test-2'; + api?.setSnippetConfig({ instance: 'test-2' }); await vue.nextTick(); expect(app?.$el).toHaveTextContent('test-2'); }); it('initializes the app when window.initX has the snippet config object', async () => { const vue = createLocalVue(); - window.initX = snippetConfig; - const { app } = (await new XInstaller({ + window.initX = getMinimumSnippetConfig(); + const { app, api } = (await new XInstaller({ adapter, vue, - app: injectSnippetConfigComponent() + app: createSnippetConfigComponent() }).init()) as InitWrapper; - expect(app?.$el).toHaveTextContent(snippetConfig.instance); + expect(app?.$el).toHaveTextContent(getMinimumSnippetConfig().instance); - snippetConfig.instance = 'test-2'; + api?.setSnippetConfig({ instance: 'test-2' }); await vue.nextTick(); expect(app?.$el).toHaveTextContent('test-2'); }); @@ -169,16 +191,16 @@ describe('testing `XInstaller` utility', () => { // eslint-disable-next-line max-len it('initializes the app when window.initX is a function retrieving the snippet config', async () => { const vue = createLocalVue(); - window.initX = () => snippetConfig; - const { app } = (await new XInstaller({ + window.initX = () => getMinimumSnippetConfig(); + const { app, api } = (await new XInstaller({ adapter, vue, - app: injectSnippetConfigComponent() + app: createSnippetConfigComponent() }).init()) as InitWrapper; - expect(app?.$el).toHaveTextContent(snippetConfig.instance); + expect(app?.$el).toHaveTextContent(getMinimumSnippetConfig().instance); - snippetConfig.instance = 'test-2'; + api?.setSnippetConfig({ instance: 'test-2' }); await vue.nextTick(); expect(app?.$el).toHaveTextContent('test-2'); }); @@ -187,22 +209,50 @@ describe('testing `XInstaller` utility', () => { it('does not initialize XComponents when no snippet config is passed and no window.initX is not defined', async () => { expect(await new XInstaller({ adapter, plugin, vue: createLocalVue() }).init()).toBeUndefined(); }); -}); -/** - * Creates a Vue component injecting the snippet config. - * - * @returns A Vue component with the injected snippet config. - * - * @internal - */ -function injectSnippetConfigComponent(): VueConstructor { - return Vue.extend({ - inject: ['snippetConfig'], - render(h) { - // Vue does not provide type safety for inject - const instance = (this as any).snippetConfig.instance; - return h('h1', [instance]); - } + describe('`lang` & `uiLang`', () => { + it('provides a `uiLang` by default', async () => { + const { app } = await new XInstaller({ + adapter, + plugin, + vue: createLocalVue(), + app: createSnippetConfigComponent('uiLang') + }).init({ ...getMinimumSnippetConfig(), lang: 'en' }); + + expect(app?.$el).toHaveTextContent('en'); + }); + + it('respects user `uiLang` value', async () => { + const { app } = await new XInstaller({ + adapter, + plugin, + vue: createLocalVue(), + app: createSnippetConfigComponent('uiLang') + }).init({ ...getMinimumSnippetConfig(), lang: 'en', uiLang: 'it' }); + expect(app?.$el).toHaveTextContent('it'); + }); + + it('updates `uiLang` when `lang` is changed', async () => { + const vue = createLocalVue(); + const { app, api } = await new XInstaller({ + adapter, + plugin, + vue, + app: createSnippetConfigComponent('uiLang') + }).init({ ...getMinimumSnippetConfig(), lang: 'en', uiLang: 'it' }); + expect(app?.$el).toHaveTextContent('it'); + + api!.setSnippetConfig({ lang: 'es' }); + await vue.nextTick(); + expect(app?.$el).toHaveTextContent('es'); + + api!.setSnippetConfig({ uiLang: 'en' }); + await vue.nextTick(); + expect(app?.$el).toHaveTextContent('en'); + + api!.setSnippetConfig({ lang: 'fr', uiLang: 'it' }); + await vue.nextTick(); + expect(app?.$el).toHaveTextContent('it'); + }); }); -} +}); diff --git a/packages/x-components/src/x-installer/x-installer/x-installer.ts b/packages/x-components/src/x-installer/x-installer/x-installer.ts index 710a6bcb50..988dfd5ac7 100644 --- a/packages/x-components/src/x-installer/x-installer/x-installer.ts +++ b/packages/x-components/src/x-installer/x-installer/x-installer.ts @@ -4,7 +4,7 @@ import { BaseXBus } from '../../plugins/x-bus'; import { XBus } from '../../plugins/x-bus.types'; import { XPlugin } from '../../plugins/x-plugin'; import { XPluginOptions } from '../../plugins/x-plugin.types'; -import { SnippetConfig, XAPI } from '../api/api.types'; +import { NormalisedSnippetConfig, SnippetConfig, XAPI } from '../api/api.types'; import { BaseXAPI } from '../api/base-api'; import { InitWrapper, InstallXOptions, VueConstructorPartialArgument } from './types'; @@ -276,13 +276,15 @@ export class XInstaller { ): Vue | undefined { if (this.options.app !== undefined) { const vue = this.getVue(); - this.snippetConfig = vue.observable(snippetConfig); + const normalisedSnippetConfig = (this.snippetConfig = vue.observable( + this.normaliseSnippetConfig(snippetConfig) + )); return new vue({ ...extraPlugins, ...this.options.vueOptions, provide() { return { - snippetConfig + snippetConfig: normalisedSnippetConfig }; }, store: this.options.store, @@ -292,6 +294,25 @@ export class XInstaller { } } + protected normaliseSnippetConfig(snippetConfig: SnippetConfig): NormalisedSnippetConfig; + protected normaliseSnippetConfig(snippetConfig: Partial): Partial; + /** + * Transforms the snippet configuration. + * - If `lang` is provided and `uiLang` is not, it sets `uiLang=lang`. + * + * @param snippetConfig - The snippet config to normalise. + * @returns The normalised version of the given snippet config. + * @internal + */ + protected normaliseSnippetConfig( + snippetConfig: SnippetConfig | Partial + ): NormalisedSnippetConfig | Partial { + if (snippetConfig.lang) { + snippetConfig.uiLang ??= snippetConfig.lang; + } + return snippetConfig; + } + /** * It returns the HTML element to mount the Vue Application. If the `domElement` parameter in the * {@link InstallXOptions} is an Element or a string, then it is used. If it is @@ -326,7 +347,7 @@ export class XInstaller { * @internal */ protected updateSnippetConfig(snippetConfig: Partial): void { - forEach(snippetConfig, (name, value) => { + forEach(this.normaliseSnippetConfig(snippetConfig), (name, value) => { this.getVue().set(this.snippetConfig, name, value); }); } diff --git a/packages/x-components/src/x-modules/extra-params/components/snippet-config-extra-params.vue b/packages/x-components/src/x-modules/extra-params/components/snippet-config-extra-params.vue index 5d52882fbe..8a9e05c6d8 100644 --- a/packages/x-components/src/x-modules/extra-params/components/snippet-config-extra-params.vue +++ b/packages/x-components/src/x-modules/extra-params/components/snippet-config-extra-params.vue @@ -58,7 +58,7 @@ default: (): Array => [ 'callbacks', 'productId', - 'searchLang', + 'uiLang', 'consent', 'documentDirection', 'currency', diff --git a/packages/x-utils/src/types/index.ts b/packages/x-utils/src/types/index.ts index 342e70b8d0..3054ce5cc6 100644 --- a/packages/x-utils/src/types/index.ts +++ b/packages/x-utils/src/types/index.ts @@ -1,2 +1,3 @@ export * from './paths.types'; export * from './utils.types'; +export * from './object.types'; diff --git a/packages/x-utils/src/types/object.types.ts b/packages/x-utils/src/types/object.types.ts new file mode 100644 index 0000000000..ce6a89a039 --- /dev/null +++ b/packages/x-utils/src/types/object.types.ts @@ -0,0 +1,12 @@ +/** + * Makes required the specified properties of the given type. + * + * @example + * ```ts + * type Required = RequiredProperties<{ a?: string; b?: number; }, 'a'>; + * // { a: string; b?: number; } + * ``` + * @public + */ +export type RequiredProperties = Something & + Required>; From 2bea94370cc12961ca37ac220abf36d464268f55 Mon Sep 17 00:00:00 2001 From: Javier Iglesias Date: Mon, 12 Sep 2022 08:49:47 +0200 Subject: [PATCH 2/6] Use provide object syntax --- .../src/x-installer/x-installer/x-installer.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/x-components/src/x-installer/x-installer/x-installer.ts b/packages/x-components/src/x-installer/x-installer/x-installer.ts index 988dfd5ac7..cdaab33119 100644 --- a/packages/x-components/src/x-installer/x-installer/x-installer.ts +++ b/packages/x-components/src/x-installer/x-installer/x-installer.ts @@ -276,16 +276,12 @@ export class XInstaller { ): Vue | undefined { if (this.options.app !== undefined) { const vue = this.getVue(); - const normalisedSnippetConfig = (this.snippetConfig = vue.observable( - this.normaliseSnippetConfig(snippetConfig) - )); + this.snippetConfig = vue.observable(this.normaliseSnippetConfig(snippetConfig)); return new vue({ ...extraPlugins, ...this.options.vueOptions, - provide() { - return { - snippetConfig: normalisedSnippetConfig - }; + provide: { + snippetConfig: this.snippetConfig }, store: this.options.store, el: this.getMountingTarget(this.options.domElement), From 3a744d302a0ca17fcf3fb01b71fc4cee9dcf70c3 Mon Sep 17 00:00:00 2001 From: Javier Iglesias Date: Mon, 12 Sep 2022 08:51:21 +0200 Subject: [PATCH 3/6] Improve doc --- packages/x-components/src/x-installer/api/api.types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-components/src/x-installer/api/api.types.ts b/packages/x-components/src/x-installer/api/api.types.ts index 351204e669..aeae092905 100644 --- a/packages/x-components/src/x-installer/api/api.types.ts +++ b/packages/x-components/src/x-installer/api/api.types.ts @@ -98,7 +98,7 @@ export interface SnippetConfig { env?: 'staging'; /** Execution scope (desktop, mobile, app, ...). */ scope: string; - /** Language to display. */ + /** Language for the API request, and default value for {@link SnippetConfig.uiLang}. */ lang: string; /** Language to use for the messages. Defaults to {@link SnippetConfig.lang}. */ uiLang?: string; From 1c56f1eee1863439ca679e190dce78b6f86abfed Mon Sep 17 00:00:00 2001 From: Javier Iglesias Date: Mon, 12 Sep 2022 09:07:40 +0200 Subject: [PATCH 4/6] Fix tests. Improve doc --- .../x-components/src/x-installer/x-installer/x-installer.ts | 6 +++--- .../__tests__/snippet-config-extra-params.spec.ts | 4 ++-- .../build-search-ui/web-archetype-integration-guide.md | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/x-components/src/x-installer/x-installer/x-installer.ts b/packages/x-components/src/x-installer/x-installer/x-installer.ts index cdaab33119..a6dd33e2ff 100644 --- a/packages/x-components/src/x-installer/x-installer/x-installer.ts +++ b/packages/x-components/src/x-installer/x-installer/x-installer.ts @@ -52,7 +52,7 @@ declare global { * ``` * * 2.3 When the script of the project build is loaded it searches for a global `initX` - * variable that the customer must have in their web site. This variable can be a + * variable that the customer must have in their website. This variable can be a * function that returns the {@link SnippetConfig} or an object that contains the * {@link SnippetConfig} itself: * @@ -63,7 +63,7 @@ declare global { * env, * scope, * lang, - * searchLang, + * uiLang, * currency, * consent, * documentDirection @@ -77,7 +77,7 @@ declare global { * env, * scope, * lang, - * searchLang, + * uiLang, * currency, * consent, * documentDirection diff --git a/packages/x-components/src/x-modules/extra-params/components/__tests__/snippet-config-extra-params.spec.ts b/packages/x-components/src/x-modules/extra-params/components/__tests__/snippet-config-extra-params.spec.ts index 8c15ef8920..bfd810d6b6 100644 --- a/packages/x-components/src/x-modules/extra-params/components/__tests__/snippet-config-extra-params.spec.ts +++ b/packages/x-components/src/x-modules/extra-params/components/__tests__/snippet-config-extra-params.spec.ts @@ -101,7 +101,7 @@ describe('testing snippet config extra params component', () => { }); // eslint-disable-next-line max-len - it('not emits the ExtraParamsProvided event when any no extra params in the snippet config changes', async () => { + it('does not emit ExtraParamsProvided when any no extra params in the snippet config changes', async () => { const { wrapper, setSnippetConfig } = renderSnippetConfigExtraParams(); const extraParamsProvidedCallback = jest.fn(); @@ -114,7 +114,7 @@ describe('testing snippet config extra params component', () => { }) ); - await setSnippetConfig({ searchLang: 'es' }); + await setSnippetConfig({ uiLang: 'es' }); expect(extraParamsProvidedCallback).toHaveBeenCalledTimes(1); diff --git a/packages/x-components/static-docs/build-search-ui/web-archetype-integration-guide.md b/packages/x-components/static-docs/build-search-ui/web-archetype-integration-guide.md index b1368850fb..b6dc008c8c 100644 --- a/packages/x-components/static-docs/build-search-ui/web-archetype-integration-guide.md +++ b/packages/x-components/static-docs/build-search-ui/web-archetype-integration-guide.md @@ -227,8 +227,8 @@ following configuration parameters: | `instance` | `string` | _Required._ ID of the API client instance. It's provided by Empathy. | | `env` | `staging` | _Optional_. API environment to use. Note that if you **do not** declare this parameter, you'll use the production API. Instead, use `env: 'staging'` to point to the staging API. | | `scope` | `string` | _Optional_. Context where the search interface is executed, i.e. `mobile`, `mobile-app`, `tablet`, `desktop`. | -| `lang` | `string` | _Required._ Language to use. By default, it's used for both the frontend and the API requests. | -| `searchLang` | `string` | _Optional_. Language to use for the API requests **only**. | +| `lang` | `string` | _Required._ Language to use. By default, it's used for both the interface messages and the API requests. | +| `uiLang` | `string` | _Optional_. Language to use for the interface messages **only**. | | `consent` | `boolean` | _Required._ Determines whether the shopper has accepted the use of cookies so that the `sessionId` is sent to the Empathy's Search and Tagging APIs or not. | | `documentDirection` | `'ltr'` | `'rtl'` | _Optional_. Writing direction script that the X Components should, i.e. left-to-right or right-to-left. | | `currency` | `string` | _Required._ Currency identifier to configure how prices are displayed. | From 0c190703537c819ba9091b245971e3f48c65dc89 Mon Sep 17 00:00:00 2001 From: Javier Iglesias Date: Mon, 12 Sep 2022 14:44:46 +0200 Subject: [PATCH 5/6] PR fixes --- .../src/x-installer/x-installer/types.ts | 47 ++++++++++++------- .../x-installer/x-installer/x-installer.ts | 36 +++++++------- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/packages/x-components/src/x-installer/x-installer/types.ts b/packages/x-components/src/x-installer/x-installer/types.ts index f5cff5b103..5dc1753796 100644 --- a/packages/x-components/src/x-installer/x-installer/types.ts +++ b/packages/x-components/src/x-installer/x-installer/types.ts @@ -1,7 +1,7 @@ import { ComponentOptions, PluginObject, VueConstructor } from 'vue'; import { XBus } from '../../plugins/x-bus.types'; import { XPluginOptions } from '../../plugins/x-plugin.types'; -import { SnippetConfig, XAPI } from '../api/api.types'; +import { NormalisedSnippetConfig, XAPI } from '../api/api.types'; /** * Interface for the parameter of the constructor of {@link XInstaller} function. It is an extended @@ -10,25 +10,37 @@ import { SnippetConfig, XAPI } from '../api/api.types'; * @public */ export interface InstallXOptions extends XPluginOptions { - /** The Vue component used as root of the application. If is not passed no Vue Application is - * initialized, only plugin installed. */ + /** + * The Vue component used as root of the application. If it is not passed, no Vue Application is + * initialized, only plugin installed. + */ app?: VueConstructor | ComponentOptions; - /** The API to expose globally. If is not passed the default {@link BaseXAPI} will be used. If - * a `false` value is passed then the API is not created.*/ + /** + * The API to expose globally. If is not passed the default {@link BaseXAPI} will be used. If + * a `false` value is passed then the API is not created. + */ api?: API | false; - /** The {@link XBus} used in the {@link XPlugin}. If not passed an instance of {@link BaseXBus} - * will be used.*/ + /** + * The {@link XBus} used in the {@link XPlugin}. If not passed an instance of {@link BaseXBus} + * will be used. + */ bus?: XBus; - /** An Element | string to indicate the HTML element that will contain the Vue + /** + * An Element | string to indicate the HTML element that will contain the Vue * application. If string selector is passed and the element doesn't exits, the - * {@link XInstaller} will create it. */ + * {@link XInstaller} will create it. + */ domElement?: Element | string; - /** The XPlugin which will be installed. If not passed, an instance of {@link XPlugin} will be - * installed.*/ + /** + * The XPlugin which will be installed. If not passed, an instance of {@link XPlugin} will be + * installed. + */ plugin?: PluginObject; - /** The Vue instance used to install the plugin and to create the Application. If not + /** + * The Vue instance used to install the plugin and to create the Application. If not * passed the default Vue instance is used. This can be useful to use the `localVue` - * in the unit tests.*/ + * in the unit tests. + */ vue?: VueConstructor; /** * This object can contain any option to pass to Vue instance at the moment of creating the App @@ -45,6 +57,7 @@ export interface InstallXOptions extends XPluginOptions * ``` */ vueOptions?: VueConstructorPartialArgument; + /** * Adds the option to install more Vue plugins, giving access to the {@link SnippetConfig} and * the {@link XBus}. @@ -68,9 +81,11 @@ export interface ExtraPluginsOptions { vue: VueConstructor; /** The events bus instance used to communicate different part of the x-components. */ bus: XBus; - /** Configuration coming from the client website with options like the lang, or the active - * currency. */ - snippet: SnippetConfig; + /** + * Configuration coming from the client website with options like the lang, or the active + * currency. + */ + snippet: NormalisedSnippetConfig; } /** diff --git a/packages/x-components/src/x-installer/x-installer/x-installer.ts b/packages/x-components/src/x-installer/x-installer/x-installer.ts index a6dd33e2ff..6375e5fb26 100644 --- a/packages/x-components/src/x-installer/x-installer/x-installer.ts +++ b/packages/x-components/src/x-installer/x-installer/x-installer.ts @@ -94,7 +94,7 @@ export class XInstaller { * * @internal */ - protected snippetConfig!: SnippetConfig; + protected snippetConfig?: NormalisedSnippetConfig; /** * Receives the {@link InstallXOptions} and merges it with the default fallback options. Also @@ -160,11 +160,12 @@ export class XInstaller { init(): Promise; async init(snippetConfig = this.retrieveSnippetConfig()): Promise { if (snippetConfig) { + this.snippetConfig = this.normaliseSnippetConfig(snippetConfig); const bus = this.createBus(); const pluginOptions = this.getPluginOptions(); const plugin = this.installPlugin(pluginOptions, bus); - const extraPlugins = await this.installExtraPlugins(snippetConfig, bus); - const app = this.createApp(extraPlugins, snippetConfig); + const extraPlugins = await this.installExtraPlugins(bus); + const app = this.createApp(extraPlugins); this.api?.setBus(bus); return { @@ -247,17 +248,15 @@ export class XInstaller { /** * Install more plugins to Vue defined by the user. * - * @param snippet - The snippet configuration. * @param bus - The events bus used in the application. * @returns The arguments from the plugins installation to be used in Vue's constructor. * @internal */ - protected installExtraPlugins( - snippet: SnippetConfig, - bus: XBus - ): Promise { + protected installExtraPlugins(bus: XBus): Promise { const vue = this.getVue(); - return Promise.resolve(this.options.installExtraPlugins?.({ vue, snippet, bus })); + return Promise.resolve( + this.options.installExtraPlugins?.({ vue, snippet: this.snippetConfig!, bus }) + ); } /** @@ -265,18 +264,14 @@ export class XInstaller { * application is created using that app. * * @param extraPlugins - Vue plugins initialisation data. - * @param snippetConfig - Configuration from the client snippet. * @returns The Created Vue application or undefined if not created. * * @internal */ - protected createApp( - extraPlugins: VueConstructorPartialArgument, - snippetConfig: SnippetConfig - ): Vue | undefined { + protected createApp(extraPlugins: VueConstructorPartialArgument): Vue | undefined { if (this.options.app !== undefined) { const vue = this.getVue(); - this.snippetConfig = vue.observable(this.normaliseSnippetConfig(snippetConfig)); + vue.observable(this.snippetConfig); return new vue({ ...extraPlugins, ...this.options.vueOptions, @@ -338,13 +333,16 @@ export class XInstaller { /** * It updates all the provided properties from the current snippet config. * - * @param snippetConfig - All the properties to be updated in the {@link SnippetConfig}. + * @param newSnippetConfig - All the properties to be updated in the {@link SnippetConfig}. * * @internal */ - protected updateSnippetConfig(snippetConfig: Partial): void { - forEach(this.normaliseSnippetConfig(snippetConfig), (name, value) => { - this.getVue().set(this.snippetConfig, name, value); + protected updateSnippetConfig(newSnippetConfig: Partial): void { + if (!this.snippetConfig) { + return; + } + forEach(this.normaliseSnippetConfig(newSnippetConfig), (name, value) => { + this.getVue().set(this.snippetConfig!, name, value); }); } } From 3fd60c6ad9e0f3d5ffa50ae74ee1c03d0f3af58e Mon Sep 17 00:00:00 2001 From: Javier Iglesias Date: Tue, 13 Sep 2022 09:31:37 +0200 Subject: [PATCH 6/6] PR fixes --- .../x-components/src/x-installer/x-installer/x-installer.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/x-components/src/x-installer/x-installer/x-installer.ts b/packages/x-components/src/x-installer/x-installer/x-installer.ts index 6375e5fb26..5eb0494850 100644 --- a/packages/x-components/src/x-installer/x-installer/x-installer.ts +++ b/packages/x-components/src/x-installer/x-installer/x-installer.ts @@ -271,12 +271,11 @@ export class XInstaller { protected createApp(extraPlugins: VueConstructorPartialArgument): Vue | undefined { if (this.options.app !== undefined) { const vue = this.getVue(); - vue.observable(this.snippetConfig); return new vue({ ...extraPlugins, ...this.options.vueOptions, provide: { - snippetConfig: this.snippetConfig + snippetConfig: (this.snippetConfig = vue.observable(this.snippetConfig)) }, store: this.options.store, el: this.getMountingTarget(this.options.domElement),