From 408fce67c051a9bfebb41655823f5a23a2b3a69d Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Mon, 7 Aug 2023 14:50:07 +0200 Subject: [PATCH 01/15] client api change signature of renderWebComponent --- core/src/Modal.svelte | 2 +- core/src/navigation/TabHeader.svelte | 2 +- core/src/services/routing.js | 2 +- core/src/services/split-view.js | 2 +- core/src/services/web-components.js | 34 ++++++++++++++++++---------- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/core/src/Modal.svelte b/core/src/Modal.svelte index a6961bbcf1..14972d3679 100644 --- a/core/src/Modal.svelte +++ b/core/src/Modal.svelte @@ -106,7 +106,7 @@ WebComponentService.renderWebComponent( nodeObject.viewUrl, document.querySelector(modalElementClassSelector), - pathData.context, + {context: pathData.context}, nodeObject ); dispatch('wcCreated', { diff --git a/core/src/navigation/TabHeader.svelte b/core/src/navigation/TabHeader.svelte index 48dabdb3ab..2c6b030768 100644 --- a/core/src/navigation/TabHeader.svelte +++ b/core/src/navigation/TabHeader.svelte @@ -21,7 +21,7 @@ // render webcomponent based on passed node object only if it is a webcomponent and showAsTabHeader is set to true if (node.webcomponent && node.tabNav.showAsTabHeader) { const tabHeaderCnt = document.querySelector('.lui-tab-header'); - WebComponentService.renderWebComponent(node.viewUrl, tabHeaderCnt, await getCurrentContext(), node); + WebComponentService.renderWebComponent(node.viewUrl, tabHeaderCnt, {context: await getCurrentContext()}, node); tabHeaderCnt.addEventListener('lui_ctx_update', async () => { const wc = document.querySelector('.lui-tab-header [lui_web_component]'); if (wc) { diff --git a/core/src/services/routing.js b/core/src/services/routing.js index 3c54d74dff..9735e6e7b1 100644 --- a/core/src/services/routing.js +++ b/core/src/services/routing.js @@ -658,7 +658,7 @@ class RoutingClass { const wc_container = this.removeLastChildFromWCContainer(); if (!wc_container) return; - WebComponentService.renderWebComponent(componentData.viewUrl, wc_container, componentData.context, navNode); + WebComponentService.renderWebComponent(componentData.viewUrl, wc_container, componentData, navNode); } navigateWebComponentCompound(component, navNode) { diff --git a/core/src/services/split-view.js b/core/src/services/split-view.js index 02037b2609..c6edae679b 100644 --- a/core/src/services/split-view.js +++ b/core/src/services/split-view.js @@ -84,7 +84,7 @@ class SplitViewSvcClass { WebComponentService.renderWebComponent( lastNode.viewUrl, document.querySelector('.iframeSplitViewCnt'), - pathData.context, + { context: pathData.context }, lastNode ); const wcInfo = { diff --git a/core/src/services/web-components.js b/core/src/services/web-components.js index 9a5e5b588e..307fcf52e5 100644 --- a/core/src/services/web-components.js +++ b/core/src/services/web-components.js @@ -19,21 +19,21 @@ class WebComponentSvcClass { /** Creates a web component with tagname wc_id and adds it to wcItemContainer, * if attached to wc_container */ - attachWC(wc_id, wcItemPlaceholder, wc_container, ctx, viewUrl, nodeId) { + attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, viewUrl, nodeId) { if (wc_container && wc_container.contains(wcItemPlaceholder)) { const wc = document.createElement(wc_id); if (nodeId) { wc.setAttribute('nodeId', nodeId); } wc.setAttribute('lui_web_component', true); - - this.initWC(wc, wc_id, wc_container, viewUrl, ctx, nodeId); + this.initWC(wc, wc_id, wc_container, viewUrl, extendedContext, nodeId); wc_container.replaceChild(wc, wcItemPlaceholder); } } - initWC(wc, wc_id, eventBusElement, viewUrl, ctx, nodeId) { + initWC(wc, wc_id, eventBusElement, viewUrl, extendedContext, nodeId) { + const ctx = extendedContext.ctx; const clientAPI = { linkManager: window.Luigi.navigation, uxManager: window.Luigi.ux, @@ -44,7 +44,17 @@ class WebComponentSvcClass { } }, getActiveFeatureToggleList: () => window.Luigi.featureToggles().getActiveFeatureToggleList(), - getActiveFeatureToggles: () => window.Luigi.featureToggles().getActiveFeatureToggleList() + getActiveFeatureToggles: () => window.Luigi.featureToggles().getActiveFeatureToggleList(), + addNodeParams: (params, keepBrowserHistory) => { + window.Luigi.routing().addNodeParams(params, keepBrowserHistory); + }, + getNodeParams: shouldDesanitise => { + //TODO check shouldDesanitise + return extendedContext.nodeParams; + }, + setAnchor: anchor => { + window.Luigi.routing().setAnchor(anchor); + } }; if (wc.__postProcess) { @@ -177,29 +187,29 @@ class WebComponentSvcClass { /** Adds a web component defined by viewUrl to the wc_container and sets the node context. * If the web component is not defined yet, it gets imported. */ - renderWebComponent(viewUrl, wc_container, context, node, nodeId) { + renderWebComponent(viewUrl, wc_container, extendedContext, node, nodeId) { + const context = extendedContext.context; const i18nViewUrl = RoutingHelpers.substituteViewUrl(viewUrl, { context }); const wc_id = node.webcomponent && node.webcomponent.tagName ? node.webcomponent.tagName : this.generateWCId(i18nViewUrl); const wcItemPlaceholder = document.createElement('div'); wc_container.appendChild(wcItemPlaceholder); wc_container._luigi_node = node; - if (window.customElements.get(wc_id)) { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId); } else { /** Custom import function, if defined */ if (window.luigiWCFn) { window.luigiWCFn(i18nViewUrl, wc_id, wcItemPlaceholder, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId); }); } else if (node.webcomponent && node.webcomponent.selfRegistered) { this.includeSelfRegisteredWCFromUrl(node, i18nViewUrl, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId); }); } else { this.registerWCFromUrl(i18nViewUrl, wc_id).then(() => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId); }); } } @@ -288,7 +298,7 @@ class WebComponentSvcClass { renderer.attachCompoundItem(compoundCnt, compoundItemCnt); const nodeId = wc.id || 'gen_' + index; - this.renderWebComponent(wc.viewUrl, compoundItemCnt, ctx, wc, nodeId); + this.renderWebComponent(wc.viewUrl, compoundItemCnt, { context: ctx }, wc, nodeId); registerEventListeners(ebListeners, wc, nodeId); }); wc_container.appendChild(compoundCnt); From ebfe99d4dc7af9d756605c35220077f203469bf8 Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Mon, 7 Aug 2023 15:51:19 +0200 Subject: [PATCH 02/15] fix --- core/src/services/web-components.js | 6 ++++-- core/test/services/web-components.spec.js | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/core/src/services/web-components.js b/core/src/services/web-components.js index 307fcf52e5..821b6f7070 100644 --- a/core/src/services/web-components.js +++ b/core/src/services/web-components.js @@ -33,7 +33,7 @@ class WebComponentSvcClass { } initWC(wc, wc_id, eventBusElement, viewUrl, extendedContext, nodeId) { - const ctx = extendedContext.ctx; + const ctx = extendedContext.context; const clientAPI = { linkManager: window.Luigi.navigation, uxManager: window.Luigi.ux, @@ -49,7 +49,9 @@ class WebComponentSvcClass { window.Luigi.routing().addNodeParams(params, keepBrowserHistory); }, getNodeParams: shouldDesanitise => { - //TODO check shouldDesanitise + if (shouldDesanitise) { + return RoutingHelpers.sanitizeParamsMap(extendedContext.nodeParams); + } return extendedContext.nodeParams; }, setAnchor: anchor => { diff --git a/core/test/services/web-components.spec.js b/core/test/services/web-components.spec.js index 17b314aa12..dfedc38861 100644 --- a/core/test/services/web-components.spec.js +++ b/core/test/services/web-components.spec.js @@ -43,7 +43,7 @@ describe('WebComponentService', function() { const sb = sinon.createSandbox(); let container; let itemPlaceholder; - const ctx = { someValue: true }; + const extendedContext = { context: { someValue: true } }; beforeAll(() => { window.Luigi = { @@ -63,17 +63,17 @@ describe('WebComponentService', function() { }); it('check dom injection abort if container not attached', () => { - WebComponentService.attachWC('div', itemPlaceholder, container, ctx); + WebComponentService.attachWC('div', itemPlaceholder, container, extendedContext); expect(container.children.length).to.equal(0); }); it('check dom injection', () => { container.appendChild(itemPlaceholder); - WebComponentService.attachWC('div', itemPlaceholder, container, ctx); + WebComponentService.attachWC('div', itemPlaceholder, container, extendedContext); const expectedCmp = container.children[0]; - expect(expectedCmp.context).to.equal(ctx); + expect(expectedCmp.context).to.equal(extendedContext.context); expect(expectedCmp.LuigiClient.linkManager).to.equal(window.Luigi.navigation); expect(expectedCmp.LuigiClient.uxManager).to.equal(window.Luigi.ux); expect(expectedCmp.LuigiClient.getCurrentLocale()).to.equal(window.Luigi.i18n().getCurrentLocale()); @@ -102,7 +102,7 @@ describe('WebComponentService', function() { sb.stub(window, 'location').value({ origin: 'http://localhost' }); container.appendChild(itemPlaceholder); - WebComponentService.attachWC(wc_id, itemPlaceholder, container, ctx, 'http://localhost:8080/'); + WebComponentService.attachWC(wc_id, itemPlaceholder, container, extendedContext, 'http://localhost:8080/'); assert(myEl.__postProcess.calledOnce, '__postProcess should be called'); expect(myEl.setAttribute.calledWith('lui_web_component', true)).to.equal(true); From 6a4f83641f986a80f601cd2ab2e7c0acc40ccb35 Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Mon, 7 Aug 2023 22:44:08 +0200 Subject: [PATCH 03/15] ts declaration, context for compound --- client/luigi-element.d.ts | 23 +++++++++++++++++++++++ core/src/services/routing.js | 2 +- core/src/services/web-components.js | 3 ++- core/test/services/web-components.spec.js | 6 +++--- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/client/luigi-element.d.ts b/client/luigi-element.d.ts index 9bc0046ed3..55ef75e629 100644 --- a/client/luigi-element.d.ts +++ b/client/luigi-element.d.ts @@ -414,4 +414,27 @@ export interface LuigiClient { linkManager: () => LinkManager; uxManager: () => UxManager; publishEvent: (event: Event) => void; + /** + * Sets node parameters in Luigi Core. The parameters will be added to the URL. + * @param {Object} params + * @param {boolean} keepBrowserHistory + * @memberof LuigiClient + */ + addNodeParams: (params: Object, keepBrowserHistory: boolean) => void; + /** + * Returns the node parameters of the active URL. + * Node parameters are defined like URL query parameters but with a specific prefix allowing Luigi to pass them to the micro frontend view. The default prefix is **~** and you can use it in the following way: `https://my.luigi.app/home/products?~sort=asc&~page=3`. + * + * > **NOTE:** some special characters (`<`, `>`, `"`, `'`, `/`) in node parameters are HTML-encoded. + * @param {boolean} shouldDesanitise defines whether the specially encoded characters should be desanitised + * @returns {Object} node parameters, where the object property name is the node parameter name without the prefix, and its value is the value of the node parameter. For example `{sort: 'asc', page: 3}` + * @memberof LuigiClient + */ + getNodeParams: (shouldDesanitise: boolean) => Object; + /** + * Sends anchor to Luigi Core. The anchor will be added to the URL. + * @param {string} anchor + * @memberof LuigiClient + */ + setAnchor: (anchor: string) => void; } diff --git a/core/src/services/routing.js b/core/src/services/routing.js index 9735e6e7b1..054c3bb24c 100644 --- a/core/src/services/routing.js +++ b/core/src/services/routing.js @@ -676,7 +676,7 @@ class RoutingClass { if (compound && compound.children) { compound.children = compound.children.filter(c => NavigationHelpers.checkVisibleForFeatureToggles(c)); } - WebComponentService.renderWebComponentCompound(navNode, wc_container, componentData.context); + WebComponentService.renderWebComponentCompound(navNode, wc_container, componentData); wc_container._luigi_pathParams = componentData.pathParams; } diff --git a/core/src/services/web-components.js b/core/src/services/web-components.js index 821b6f7070..42af1c6b97 100644 --- a/core/src/services/web-components.js +++ b/core/src/services/web-components.js @@ -250,7 +250,8 @@ class WebComponentSvcClass { * @param {*} wc_container the web component container dom element * @param {*} context the luigi node context */ - renderWebComponentCompound(navNode, wc_container, context) { + renderWebComponentCompound(navNode, wc_container, extendedContext) { + const context = extendedContext.context; let renderer; wc_container._luigi_node = navNode; if (navNode.webcomponent && navNode.viewUrl) { diff --git a/core/test/services/web-components.spec.js b/core/test/services/web-components.spec.js index dfedc38861..9ab3fd7a19 100644 --- a/core/test/services/web-components.spec.js +++ b/core/test/services/web-components.spec.js @@ -416,7 +416,7 @@ describe('WebComponentService', function() { describe('check renderWebComponentCompound', function() { const sb = sinon.createSandbox(); - const context = { key: 'value', mario: 'luigi' }; + const extendedContext = { context: { key: 'value', mario: 'luigi' } }; const eventEmitter = 'emitterId'; const eventName = 'emitterId'; @@ -484,7 +484,7 @@ describe('WebComponentService', function() { sb.spy(WebComponentService, 'renderWebComponent'); sb.stub(WebComponentService, 'registerWCFromUrl').resolves(); - WebComponentService.renderWebComponentCompound(navNode, wc_container, context) + WebComponentService.renderWebComponentCompound(navNode, wc_container, extendedContext) .then(compoundCnt => { expect(wc_container.children.length).to.equal(1); @@ -523,7 +523,7 @@ describe('WebComponentService', function() { sb.stub(WebComponentService, 'registerWCFromUrl').resolves(); - WebComponentService.renderWebComponentCompound(node, wc_container, context).then( + WebComponentService.renderWebComponentCompound(node, wc_container, extendedContext).then( compoundCnt => { expect(WebComponentService.registerWCFromUrl.callCount).to.equal(3); From 2a79739d9bb620b606d8ffacc35586265e20b8d3 Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Tue, 8 Aug 2023 17:08:24 +0200 Subject: [PATCH 04/15] restrictedAPI --- core/src/services/web-components.js | 61 ++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/core/src/services/web-components.js b/core/src/services/web-components.js index 42af1c6b97..6d256fab2b 100644 --- a/core/src/services/web-components.js +++ b/core/src/services/web-components.js @@ -19,20 +19,20 @@ class WebComponentSvcClass { /** Creates a web component with tagname wc_id and adds it to wcItemContainer, * if attached to wc_container */ - attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, viewUrl, nodeId) { + attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, viewUrl, nodeId, restrictedClientAPI) { if (wc_container && wc_container.contains(wcItemPlaceholder)) { const wc = document.createElement(wc_id); if (nodeId) { wc.setAttribute('nodeId', nodeId); } wc.setAttribute('lui_web_component', true); - this.initWC(wc, wc_id, wc_container, viewUrl, extendedContext, nodeId); + this.initWC(wc, wc_id, wc_container, viewUrl, extendedContext, nodeId, restrictedClientAPI); wc_container.replaceChild(wc, wcItemPlaceholder); } } - initWC(wc, wc_id, eventBusElement, viewUrl, extendedContext, nodeId) { + initWC(wc, wc_id, eventBusElement, viewUrl, extendedContext, nodeId, restrictedClientAPI) { const ctx = extendedContext.context; const clientAPI = { linkManager: window.Luigi.navigation, @@ -46,16 +46,22 @@ class WebComponentSvcClass { getActiveFeatureToggleList: () => window.Luigi.featureToggles().getActiveFeatureToggleList(), getActiveFeatureToggles: () => window.Luigi.featureToggles().getActiveFeatureToggleList(), addNodeParams: (params, keepBrowserHistory) => { - window.Luigi.routing().addNodeParams(params, keepBrowserHistory); + if (!restrictedClientAPI) { + window.Luigi.routing().addNodeParams(params, keepBrowserHistory); + } }, getNodeParams: shouldDesanitise => { - if (shouldDesanitise) { - return RoutingHelpers.sanitizeParamsMap(extendedContext.nodeParams); + if (!restrictedClientAPI) { + if (shouldDesanitise) { + return RoutingHelpers.sanitizeParamsMap(extendedContext.nodeParams); + } + return extendedContext.nodeParams; } - return extendedContext.nodeParams; }, setAnchor: anchor => { - window.Luigi.routing().setAnchor(anchor); + if (!restrictedClientAPI) { + window.Luigi.routing().setAnchor(anchor); + } } }; @@ -189,7 +195,7 @@ class WebComponentSvcClass { /** Adds a web component defined by viewUrl to the wc_container and sets the node context. * If the web component is not defined yet, it gets imported. */ - renderWebComponent(viewUrl, wc_container, extendedContext, node, nodeId) { + renderWebComponent(viewUrl, wc_container, extendedContext, node, nodeId, restrictedClientAPI) { const context = extendedContext.context; const i18nViewUrl = RoutingHelpers.substituteViewUrl(viewUrl, { context }); const wc_id = @@ -198,20 +204,44 @@ class WebComponentSvcClass { wc_container.appendChild(wcItemPlaceholder); wc_container._luigi_node = node; if (window.customElements.get(wc_id)) { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, restrictedClientAPI); } else { /** Custom import function, if defined */ if (window.luigiWCFn) { window.luigiWCFn(i18nViewUrl, wc_id, wcItemPlaceholder, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId); + this.attachWC( + wc_id, + wcItemPlaceholder, + wc_container, + extendedContext, + i18nViewUrl, + nodeId, + restrictedClientAPI + ); }); } else if (node.webcomponent && node.webcomponent.selfRegistered) { this.includeSelfRegisteredWCFromUrl(node, i18nViewUrl, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId); + this.attachWC( + wc_id, + wcItemPlaceholder, + wc_container, + extendedContext, + i18nViewUrl, + nodeId, + restrictedClientAPI + ); }); } else { this.registerWCFromUrl(i18nViewUrl, wc_id).then(() => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId); + this.attachWC( + wc_id, + wcItemPlaceholder, + wc_container, + extendedContext, + i18nViewUrl, + nodeId, + restrictedClientAPI + ); }); } } @@ -271,7 +301,7 @@ class WebComponentSvcClass { renderer = renderer || new DefaultCompoundRenderer(); return new Promise(resolve => { - this.createCompoundContainerAsync(renderer, context).then(compoundCnt => { + this.createCompoundContainerAsync(renderer, extendedContext).then(compoundCnt => { const ebListeners = {}; compoundCnt.eventBus = { listeners: ebListeners, @@ -301,7 +331,8 @@ class WebComponentSvcClass { renderer.attachCompoundItem(compoundCnt, compoundItemCnt); const nodeId = wc.id || 'gen_' + index; - this.renderWebComponent(wc.viewUrl, compoundItemCnt, { context: ctx }, wc, nodeId); + const restrictedClientAPI = true; //oder compoundItemCnt.restrictedClientAPI + this.renderWebComponent(wc.viewUrl, compoundItemCnt, { context: ctx }, wc, nodeId, restrictedClientAPI); registerEventListeners(ebListeners, wc, nodeId); }); wc_container.appendChild(compoundCnt); From ae7bcc928024d043b516eb80c1b6a70860dfef22 Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Wed, 9 Aug 2023 16:39:30 +0200 Subject: [PATCH 05/15] check isCompoundChild, desanitize getNodeParams --- .../src/services/webcomponents.service.ts | 64 +++++++++++++++---- container/test-app/helloWorldWC.js | 28 +++++++- container/test-app/index.html | 26 ++++++++ core/src/services/routing.js | 1 + core/src/services/web-components.js | 64 +++++++------------ .../helpers/web-component-helpers.js | 15 +++++ 6 files changed, 143 insertions(+), 55 deletions(-) diff --git a/container/src/services/webcomponents.service.ts b/container/src/services/webcomponents.service.ts index 71a937cd48..b7176f2393 100644 --- a/container/src/services/webcomponents.service.ts +++ b/container/src/services/webcomponents.service.ts @@ -25,14 +25,22 @@ export class WebComponentService { /** Creates a web component with tagname wc_id and adds it to wcItemContainer, * if attached to wc_container */ - attachWC(wc_id: string, wcItemPlaceholder: HTMLDivElement, wc_container, ctx, viewUrl: string, nodeId: string) { + attachWC( + wc_id: string, + wcItemPlaceholder: HTMLDivElement, + wc_container, + ctx, + viewUrl: string, + nodeId: string, + isCompoundChild?: boolean + ) { if (wc_container && wc_container.contains(wcItemPlaceholder)) { const wc = document.createElement(wc_id); if (nodeId) { wc.setAttribute('nodeId', nodeId); } - this.initWC(wc, wc_id, wc_container, viewUrl, ctx, nodeId); + this.initWC(wc, wc_id, wc_container, viewUrl, ctx, nodeId, isCompoundChild); wc_container.replaceChild(wc, wcItemPlaceholder); if (wc_container._luigi_node) { wc_container._luigi_mfe_webcomponent = wc; @@ -60,7 +68,7 @@ export class WebComponentService { * @param wc_id a tagname that is used when creating the web component element * @returns an object with the Luigi Client API */ - createClientAPI(eventBusElement, nodeId: string, wc_id: string) { + createClientAPI(eventBusElement, nodeId: string, wc_id: string, isCompoundChild?: boolean) { return { linkManager: () => { return { @@ -103,12 +111,39 @@ export class WebComponentService { }, luigiClientInit: () => { this.dispatchLuigiEvent(Events.INITIALIZED, {}); + }, + addNodeParams: (params, keepBrowserHistory) => { + if (isCompoundChild) { + return; + } + this.dispatchLuigiEvent(Events.ADD_NODE_PARAMS_REQUEST, { params, keepBrowserHistory }); + }, + getNodeParams: shouldDesanitise => { + if (isCompoundChild) { + return {}; + } + //helper Funktion desanitize + return this.thisComponent.getAttribute('node_params') || {}; + }, + setAnchor: anchor => { + if (isCompoundChild) { + return; + } + this.dispatchLuigiEvent(Events.SET_ANCHOR_LINK_REQUEST, anchor); } }; } - initWC(wc: HTMLElement | any, wc_id, eventBusElement, viewUrl: string, ctx, nodeId: string) { - const clientAPI = this.createClientAPI(eventBusElement, nodeId, wc_id); + initWC( + wc: HTMLElement | any, + wc_id, + eventBusElement, + viewUrl: string, + ctx, + nodeId: string, + isCompoundChild?: boolean + ) { + const clientAPI = this.createClientAPI(eventBusElement, nodeId, wc_id, isCompoundChild); if (wc.__postProcess) { const url = @@ -242,7 +277,14 @@ export class WebComponentService { /** Adds a web component defined by viewUrl to the wc_container and sets the node context. * If the web component is not defined yet, it gets imported. */ - renderWebComponent(viewUrl: string, wc_container: HTMLElement | any, context: any, node: any, nodeId?: any) { + renderWebComponent( + viewUrl: string, + wc_container: HTMLElement | any, + context: any, + node: any, + nodeId?: any, + isCompoundChild?: boolean + ) { const i18nViewUrl = this.processViewUrl(viewUrl, { context }); const wc_id = node.webcomponent && node.webcomponent.tagName ? node.webcomponent.tagName : this.generateWCId(i18nViewUrl); @@ -251,21 +293,21 @@ export class WebComponentService { wc_container._luigi_node = node; if (window.customElements.get(wc_id)) { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId, isCompoundChild); } else { /** Custom import function, if defined */ if ((window as any).luigiWCFn) { (window as any).luigiWCFn(i18nViewUrl, wc_id, wcItemPlaceholder, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId, isCompoundChild); }); } else if (node.webcomponent && node.webcomponent.selfRegistered) { this.includeSelfRegisteredWCFromUrl(node, i18nViewUrl, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId, isCompoundChild); }); } else { this.registerWCFromUrl(i18nViewUrl, wc_id) .then(() => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId, isCompoundChild); }) .catch(error => { console.warn('ERROR =>', error); @@ -369,7 +411,7 @@ export class WebComponentService { renderer.attachCompoundItem(compoundCnt, compoundItemCnt); const nodeId = wc.id || 'gen_' + index; - this.renderWebComponent(wc.viewUrl, compoundItemCnt, ctx, wc, nodeId); + this.renderWebComponent(wc.viewUrl, compoundItemCnt, ctx, wc, nodeId, true); registerEventListeners(ebListeners, wc, nodeId); }); wc_container.appendChild(compoundCnt); diff --git a/container/test-app/helloWorldWC.js b/container/test-app/helloWorldWC.js index 2e95901df4..ab93581d28 100644 --- a/container/test-app/helloWorldWC.js +++ b/container/test-app/helloWorldWC.js @@ -8,7 +8,13 @@ export default class extends HTMLElement { template.innerHTML = `

Hello World!

`; const templateBtn = document.createElement('template'); - templateBtn.innerHTML = ''; + templateBtn.innerHTML = ''; + + const addNodeParamsBtn = document.createElement('template'); + addNodeParamsBtn.innerHTML = ''; + + const getNodeParamsBtn = document.createElement('template'); + getNodeParamsBtn.innerHTML = ''; const empty = document.createElement('template'); empty.innerHTML = `

Test!



`; @@ -19,13 +25,15 @@ export default class extends HTMLElement { }); this._shadowRoot.appendChild(template.content.cloneNode(true)); this._shadowRoot.appendChild(templateBtn.content.cloneNode(true)); + this._shadowRoot.appendChild(addNodeParamsBtn.content.cloneNode(true)); + this._shadowRoot.appendChild(getNodeParamsBtn.content.cloneNode(true)); for (let index = 0; index < 10; index++) { this._shadowRoot.appendChild(empty.content.cloneNode(true)); } this.$paragraph = this._shadowRoot.querySelector('p'); - this.$button = this._shadowRoot.querySelector('button'); + this.$button = this._shadowRoot.querySelector('#aButton'); this.$button.addEventListener('click', () => { if (this.LuigiClient) { this.LuigiClient.uxManager().showAlert({ @@ -44,6 +52,22 @@ export default class extends HTMLElement { }); } }); + + this.$button2 = this._shadowRoot.querySelector('#addNodeParams'); + this.$button2.addEventListener('click', () => { + if (this.LuigiClient) { + this.LuigiClient.addNodeParams({ Luigi: 'rocks' }, true); + } + }); + this.$button3 = this._shadowRoot.querySelector('#getNodeParams'); + this.$button3.addEventListener('click', () => { + if (this.LuigiClient) { + this.LuigiClient.uxManager().showAlert({ + text: 'LuigiClient.getNodeParams()=' + this.LuigiClient.getNodeParams(), + type: 'info' + }); + } + }); } set context(ctx) { diff --git a/container/test-app/index.html b/container/test-app/index.html index b40fae9ec9..fbf1e5fb69 100644 --- a/container/test-app/index.html +++ b/container/test-app/index.html @@ -539,6 +539,32 @@ console.log(event.detail); } ); + luigiContainer.addEventListener(MFEventID.ADD_NODE_PARAMS_REQUEST, event => { + console.log(event.detail.params); + const params = event.detail.params; + const url = new URL(location); + let hash = url.hash; + //TODO + let paramPrefix = '~'; + let localhash = hash; + const [hashValue, givenQueryParamsString] = localhash.split('?'); + const searchParams = new URLSearchParams(givenQueryParamsString); + for (const [key, value] of Object.entries(params)) { + const paramKey = paramPrefix ? `${paramPrefix}${key}` : key; + searchParams.set(paramKey, value); + if (value === undefined) { + searchParams.delete(paramKey); + } + } + localhash = hashValue; + if (searchParams.toString() !== '') { + localhash += `?${searchParams.toString()}`; + } + url.hash = localhash; + event.detail.keepBrowserHistory + ? window.history.pushState({}, '', url) + : window.history.replaceState({}, '', url); + }); //... }); diff --git a/core/src/services/routing.js b/core/src/services/routing.js index 054c3bb24c..874626d5b9 100644 --- a/core/src/services/routing.js +++ b/core/src/services/routing.js @@ -652,6 +652,7 @@ class RoutingClass { if (navNode === wc_containerNode) { const wc = document.querySelector(wc_id); wc.context = componentData.context; + wc.extendedContext.nodeParams = componentData.nodeParams; return; } diff --git a/core/src/services/web-components.js b/core/src/services/web-components.js index 6d256fab2b..001b9594bf 100644 --- a/core/src/services/web-components.js +++ b/core/src/services/web-components.js @@ -1,7 +1,8 @@ import { DefaultCompoundRenderer, resolveRenderer, - registerEventListeners + registerEventListeners, + deSanitizeParamsMap } from '../utilities/helpers/web-component-helpers'; import { LuigiConfig } from '../core-api'; import { RoutingHelpers } from '../utilities/helpers'; @@ -19,21 +20,22 @@ class WebComponentSvcClass { /** Creates a web component with tagname wc_id and adds it to wcItemContainer, * if attached to wc_container */ - attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, viewUrl, nodeId, restrictedClientAPI) { + attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, viewUrl, nodeId, isCompoundChild) { if (wc_container && wc_container.contains(wcItemPlaceholder)) { const wc = document.createElement(wc_id); if (nodeId) { wc.setAttribute('nodeId', nodeId); } wc.setAttribute('lui_web_component', true); - this.initWC(wc, wc_id, wc_container, viewUrl, extendedContext, nodeId, restrictedClientAPI); + this.initWC(wc, wc_id, wc_container, viewUrl, extendedContext, nodeId, isCompoundChild); wc_container.replaceChild(wc, wcItemPlaceholder); } } - initWC(wc, wc_id, eventBusElement, viewUrl, extendedContext, nodeId, restrictedClientAPI) { + initWC(wc, wc_id, eventBusElement, viewUrl, extendedContext, nodeId, isCompoundChild) { const ctx = extendedContext.context; + wc.extendedContext = extendedContext; const clientAPI = { linkManager: window.Luigi.navigation, uxManager: window.Luigi.ux, @@ -46,20 +48,22 @@ class WebComponentSvcClass { getActiveFeatureToggleList: () => window.Luigi.featureToggles().getActiveFeatureToggleList(), getActiveFeatureToggles: () => window.Luigi.featureToggles().getActiveFeatureToggleList(), addNodeParams: (params, keepBrowserHistory) => { - if (!restrictedClientAPI) { + if (!isCompoundChild) { window.Luigi.routing().addNodeParams(params, keepBrowserHistory); } }, getNodeParams: shouldDesanitise => { - if (!restrictedClientAPI) { - if (shouldDesanitise) { - return RoutingHelpers.sanitizeParamsMap(extendedContext.nodeParams); - } - return extendedContext.nodeParams; + if (isCompoundChild) { + return {}; + } + const result = wc.extendedContext?.nodeParams ? wc.extendedContext.nodeParams : {}; + if (shouldDesanitise) { + return deSanitizeParamsMap(result); } + return wc.extendedContext.nodeParams; }, setAnchor: anchor => { - if (!restrictedClientAPI) { + if (!isCompoundChild) { window.Luigi.routing().setAnchor(anchor); } } @@ -73,6 +77,7 @@ class WebComponentSvcClass { wc.__postProcess(ctx, clientAPI, url.origin + url.pathname); } else { wc.context = ctx; + wc.nodeParams = extendedContext.nodeParams; wc.LuigiClient = clientAPI; } } @@ -195,7 +200,7 @@ class WebComponentSvcClass { /** Adds a web component defined by viewUrl to the wc_container and sets the node context. * If the web component is not defined yet, it gets imported. */ - renderWebComponent(viewUrl, wc_container, extendedContext, node, nodeId, restrictedClientAPI) { + renderWebComponent(viewUrl, wc_container, extendedContext, node, nodeId, isCompoundChild) { const context = extendedContext.context; const i18nViewUrl = RoutingHelpers.substituteViewUrl(viewUrl, { context }); const wc_id = @@ -204,44 +209,20 @@ class WebComponentSvcClass { wc_container.appendChild(wcItemPlaceholder); wc_container._luigi_node = node; if (window.customElements.get(wc_id)) { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, restrictedClientAPI); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isCompoundChild); } else { /** Custom import function, if defined */ if (window.luigiWCFn) { window.luigiWCFn(i18nViewUrl, wc_id, wcItemPlaceholder, () => { - this.attachWC( - wc_id, - wcItemPlaceholder, - wc_container, - extendedContext, - i18nViewUrl, - nodeId, - restrictedClientAPI - ); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isCompoundChild); }); } else if (node.webcomponent && node.webcomponent.selfRegistered) { this.includeSelfRegisteredWCFromUrl(node, i18nViewUrl, () => { - this.attachWC( - wc_id, - wcItemPlaceholder, - wc_container, - extendedContext, - i18nViewUrl, - nodeId, - restrictedClientAPI - ); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isCompoundChild); }); } else { this.registerWCFromUrl(i18nViewUrl, wc_id).then(() => { - this.attachWC( - wc_id, - wcItemPlaceholder, - wc_container, - extendedContext, - i18nViewUrl, - nodeId, - restrictedClientAPI - ); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isCompoundChild); }); } } @@ -331,8 +312,7 @@ class WebComponentSvcClass { renderer.attachCompoundItem(compoundCnt, compoundItemCnt); const nodeId = wc.id || 'gen_' + index; - const restrictedClientAPI = true; //oder compoundItemCnt.restrictedClientAPI - this.renderWebComponent(wc.viewUrl, compoundItemCnt, { context: ctx }, wc, nodeId, restrictedClientAPI); + this.renderWebComponent(wc.viewUrl, compoundItemCnt, { context: ctx }, wc, nodeId, true); registerEventListeners(ebListeners, wc, nodeId); }); wc_container.appendChild(compoundCnt); diff --git a/core/src/utilities/helpers/web-component-helpers.js b/core/src/utilities/helpers/web-component-helpers.js index d0c1129599..9be314c83a 100644 --- a/core/src/utilities/helpers/web-component-helpers.js +++ b/core/src/utilities/helpers/web-component-helpers.js @@ -174,3 +174,18 @@ export const registerEventListeners = (eventbusListeners, navNode, nodeId, wcEle }); } }; +export const deSanitizeParamsMap = paramsMap => { + return Object.entries(paramsMap).reduce((sanitizedMap, paramPair) => { + sanitizedMap[this.deSanitizeParam(paramPair[0])] = this.deSanitizeParam(paramPair[1]); + return sanitizedMap; + }, {}); +}; + +deSanitizeParam = (param = '') => { + return String(param) + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll(''', "'") + .replaceAll('/', '/'); +}; From 5cfda163beeebc11128c0f6d3a7d258a7b797a13 Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Wed, 9 Aug 2023 17:12:40 +0200 Subject: [PATCH 06/15] fix runtime err --- core/src/services/routing.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/services/routing.js b/core/src/services/routing.js index 874626d5b9..90b2d79b3a 100644 --- a/core/src/services/routing.js +++ b/core/src/services/routing.js @@ -652,7 +652,9 @@ class RoutingClass { if (navNode === wc_containerNode) { const wc = document.querySelector(wc_id); wc.context = componentData.context; - wc.extendedContext.nodeParams = componentData.nodeParams; + if (wc.extendedContext) { + wc.extendedContext.nodeParams = componentData.nodeParams; + } return; } From 0f1ed41953a070b47c6fae89875e8688d19dc071 Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Wed, 9 Aug 2023 18:46:37 +0200 Subject: [PATCH 07/15] getNodeParams Luigicontainer --- container/src/LuigiContainer.svelte | 1 + container/src/services/webcomponents.service.ts | 11 ++++++++++- container/test-app/index.html | 6 +++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/container/src/LuigiContainer.svelte b/container/src/LuigiContainer.svelte index 8bb89d22ad..ae1b9b352a 100644 --- a/container/src/LuigiContainer.svelte +++ b/container/src/LuigiContainer.svelte @@ -10,6 +10,7 @@ export let locale; export let theme; export let active_feature_toggle_list; + export let node_params; let iframeHandle: | { diff --git a/container/src/services/webcomponents.service.ts b/container/src/services/webcomponents.service.ts index b7176f2393..97ea71b7f3 100644 --- a/container/src/services/webcomponents.service.ts +++ b/container/src/services/webcomponents.service.ts @@ -1,4 +1,9 @@ -import { DefaultCompoundRenderer, resolveRenderer, registerEventListeners } from './web-component-helpers'; +import { + DefaultCompoundRenderer, + resolveRenderer, + registerEventListeners, + deSanitizeParamsMap +} from './web-component-helpers'; import { ContainerService } from './container.service'; import { Events } from '../constants/communication'; @@ -123,6 +128,10 @@ export class WebComponentService { return {}; } //helper Funktion desanitize + // const result = wc.extendedContext?.nodeParams ? wc.extendedContext.nodeParams : {}; + // if (shouldDesanitise) { + // return deSanitizeParamsMap(result); + // } return this.thisComponent.getAttribute('node_params') || {}; }, setAnchor: anchor => { diff --git a/container/test-app/index.html b/container/test-app/index.html index fbf1e5fb69..c0f0427e06 100644 --- a/container/test-app/index.html +++ b/container/test-app/index.html @@ -356,13 +356,14 @@ locale="en" theme="sap_fiori_3" active_feature_toggle_list="['ft1','ft2']" + node_params='{"Luigi":"rocks from attribute"}' >
Date: Thu, 10 Aug 2023 11:10:44 +0200 Subject: [PATCH 08/15] normalizeUrl, de/sanitize --- container/public/LuigiContainer.svelte.d.ts | 8 +++ container/src/LuigiCompoundContainer.svelte | 1 + .../src/services/web-component-helpers.ts | 22 +++++++ .../src/services/webcomponents.service.ts | 17 +++--- container/test-app/helloWorldWC.js | 3 +- container/test-app/index.html | 61 ++++++++++--------- container/test-app/nested.js | 14 ++++- core/src/services/web-components.js | 5 +- .../helpers/web-component-helpers.js | 6 ++ 9 files changed, 95 insertions(+), 42 deletions(-) diff --git a/container/public/LuigiContainer.svelte.d.ts b/container/public/LuigiContainer.svelte.d.ts index 17383ce4d6..4fdb7b75a2 100644 --- a/container/public/LuigiContainer.svelte.d.ts +++ b/container/public/LuigiContainer.svelte.d.ts @@ -1,3 +1,6 @@ +export declare interface NodeParams { + [key: string]: string; +} export default class LuigiContainer extends HTMLElement { /** * The URL of the microfrontend to be rendered @@ -34,6 +37,11 @@ export default class LuigiContainer extends HTMLElement { */ active_feature_toggle_list: string[]; + /** + * The parameters to be passed to the web-component-based micro frontend. Will not passed to the compound children. + */ + node_params: NodeParams; + /** * Updates the context of the microfrontend * @param contextObj The context object to be updated diff --git a/container/src/LuigiCompoundContainer.svelte b/container/src/LuigiCompoundContainer.svelte index e20ee069ca..f5ce2581ba 100644 --- a/container/src/LuigiCompoundContainer.svelte +++ b/container/src/LuigiCompoundContainer.svelte @@ -9,6 +9,7 @@ export let locale; export let theme; export let active_feature_toggle_list; + export let node_params; let compoundConfig; diff --git a/container/src/services/web-component-helpers.ts b/container/src/services/web-component-helpers.ts index 799aee8b8d..e00b19123d 100644 --- a/container/src/services/web-component-helpers.ts +++ b/container/src/services/web-component-helpers.ts @@ -179,3 +179,25 @@ export const registerEventListeners = (eventbusListeners, navNode, nodeId: strin }); } }; + +/** + * Desanitization of an object + * @param {Object} paramsMap + * @returns + */ +export const deSanitizeParamsMap = paramsMap => { + return Object.entries(paramsMap).reduce((sanitizedMap, paramPair) => { + sanitizedMap[deSanitizeParam(paramPair[0])] = deSanitizeParam(paramPair[1]); + return sanitizedMap; + }, {}); +}; + +function deSanitizeParam(param: any) { + let desani = (String as any)(param) + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll(''', "'") + .replaceAll('/', '/'); + return desani; +} diff --git a/container/src/services/webcomponents.service.ts b/container/src/services/webcomponents.service.ts index 97ea71b7f3..d39d1e9a39 100644 --- a/container/src/services/webcomponents.service.ts +++ b/container/src/services/webcomponents.service.ts @@ -127,12 +127,12 @@ export class WebComponentService { if (isCompoundChild) { return {}; } - //helper Funktion desanitize - // const result = wc.extendedContext?.nodeParams ? wc.extendedContext.nodeParams : {}; - // if (shouldDesanitise) { - // return deSanitizeParamsMap(result); - // } - return this.thisComponent.getAttribute('node_params') || {}; + let result = this.thisComponent.getAttribute('node_params') || {}; + result = JSON.parse(result); + if (shouldDesanitise) { + return deSanitizeParamsMap(result); + } + return result; }, setAnchor: anchor => { if (isCompoundChild) { @@ -172,8 +172,9 @@ export class WebComponentService { */ generateWCId(viewUrl: string) { let charRep = ''; - for (let i = 0; i < viewUrl.length; i++) { - charRep += viewUrl.charCodeAt(i).toString(16); + let normalizedViewUrl = new URL(viewUrl, location.href).href; + for (let i = 0; i < normalizedViewUrl.length; i++) { + charRep += normalizedViewUrl.charCodeAt(i).toString(16); } return 'luigi-wc-' + charRep; } diff --git a/container/test-app/helloWorldWC.js b/container/test-app/helloWorldWC.js index ab93581d28..e1e22acbb5 100644 --- a/container/test-app/helloWorldWC.js +++ b/container/test-app/helloWorldWC.js @@ -62,8 +62,9 @@ export default class extends HTMLElement { this.$button3 = this._shadowRoot.querySelector('#getNodeParams'); this.$button3.addEventListener('click', () => { if (this.LuigiClient) { + let nodeParams = this.LuigiClient.getNodeParams(false); this.LuigiClient.uxManager().showAlert({ - text: 'LuigiClient.getNodeParams()=' + this.LuigiClient.getNodeParams(), + text: 'LuigiClient.getNodeParams()=' + JSON.stringify(nodeParams), type: 'info' }); } diff --git a/container/test-app/index.html b/container/test-app/index.html index c0f0427e06..6ce1f4efb7 100644 --- a/container/test-app/index.html +++ b/container/test-app/index.html @@ -298,6 +298,7 @@
@@ -306,6 +307,7 @@ context='{"label": "Dashboard2", "title":"Dashboard2"}' skipInitCheck="false" viewUrl="http://localhost:8080/nested.js" + node_params='{"Das ist ein":"compound tets"}' >
@@ -334,7 +336,7 @@
@@ -366,7 +368,7 @@ context=' {"label": "Error Handler" }' > @@ -424,7 +426,7 @@ }, { id: 'input1', - viewUrl: utilMFEDomain + '/input.js', + viewUrl: '/helloWorldWC.js', context: { title: 'Some input', instant: true @@ -543,31 +545,32 @@ luigiContainer.addEventListener(MFEventID.ADD_NODE_PARAMS_REQUEST, event => { console.log(event.detail.params); const params = event.detail.params; - const url = new URL(location); - let hash = url.hash; - //TODO - let paramPrefix = '~'; - let localhash = hash; - const [hashValue, givenQueryParamsString] = localhash.split('?'); - const searchParams = new URLSearchParams(givenQueryParamsString); - for (const [key, value] of Object.entries(params)) { - const paramKey = paramPrefix ? `${paramPrefix}${key}` : key; - searchParams.set(paramKey, value); - if (value === undefined) { - searchParams.delete(paramKey); - } - } - localhash = hashValue; - if (searchParams.toString() !== '') { - localhash += `?${searchParams.toString()}`; - } - url.hash = localhash; - event.detail.keepBrowserHistory - ? window.history.pushState({}, '', url) - : window.history.replaceState({}, '', url); - document - .querySelector('[data-test-id="luigi-client-api-test-01"]') - .setAttribute('node_params', `${JSON.stringify(params)}`); + console.log('params', params); + // const url = new URL(location); + // let hash = url.hash; + // //TODO URLSearchParams does sanitization.. + // let paramPrefix = '~'; + // let localhash = hash; + // const [hashValue, givenQueryParamsString] = localhash.split('?'); + // const searchParams = new URLSearchParams(givenQueryParamsString); + // for (const [key, value] of Object.entries(params)) { + // const paramKey = paramPrefix ? `${paramPrefix}${key}` : key; + // searchParams.set(paramKey, value); + // if (value === undefined) { + // searchParams.delete(paramKey); + // } + // } + // localhash = hashValue; + // if (searchParams.toString() !== '') { + // localhash += `?${searchParams.toString()}`; + // } + // url.hash = localhash; + // event.detail.keepBrowserHistory + // ? window.history.pushState({}, '', url) + // : window.history.replaceState({}, '', url); + // document + // .querySelector('[data-test-id="luigi-client-api-test-01"]') + // .setAttribute('node_params', `${JSON.stringify(params)}`); }); //... diff --git a/container/test-app/nested.js b/container/test-app/nested.js index ddca92bb8e..92777700fc 100644 --- a/container/test-app/nested.js +++ b/container/test-app/nested.js @@ -1,18 +1,28 @@ import { LuigiElement } from './luigi-element.js'; export default class extends LuigiElement { constructor() { - super({ openShadow: 'open', deferLuigiClientWCInit: true }); + super({ openShadow: 'open', deferLuigiClientWCInit: false }); const template = document.createElement('template'); - + //remove buttons after review/AC template.innerHTML = `
+ +
header
content
footer
`; this._shadowRoot.appendChild(template.content.cloneNode(true)); + this.$button = this._shadowRoot.querySelector('#addNodeParams'); + this.$button.addEventListener('click', () => { + this.LuigiClient.addNodeParams({ Luigi: 'rocks' }, true); + }); + this.$button2 = this._shadowRoot.querySelector('#getNodeParams'); + this.$button2.addEventListener('click', () => { + console.log('getNodeParams', this.LuigiClient.getNodeParams()); + }); } connectedCallback() { diff --git a/core/src/services/web-components.js b/core/src/services/web-components.js index 001b9594bf..783c62f636 100644 --- a/core/src/services/web-components.js +++ b/core/src/services/web-components.js @@ -88,8 +88,9 @@ class WebComponentSvcClass { */ generateWCId(viewUrl) { let charRep = ''; - for (let i = 0; i < viewUrl.length; i++) { - charRep += viewUrl.charCodeAt(i).toString(16); + let normalizedViewUrl = new URL(viewUrl, location.href).href; + for (let i = 0; i < normalizedViewUrl.length; i++) { + charRep += normalizedViewUrl.charCodeAt(i).toString(16); } return 'luigi-wc-' + charRep; } diff --git a/core/src/utilities/helpers/web-component-helpers.js b/core/src/utilities/helpers/web-component-helpers.js index 9be314c83a..ceb263bbd4 100644 --- a/core/src/utilities/helpers/web-component-helpers.js +++ b/core/src/utilities/helpers/web-component-helpers.js @@ -174,6 +174,12 @@ export const registerEventListeners = (eventbusListeners, navNode, nodeId, wcEle }); } }; + +/** + * Desanitization of an object + * @param {Object} paramsMap + * @returns + */ export const deSanitizeParamsMap = paramsMap => { return Object.entries(paramsMap).reduce((sanitizedMap, paramPair) => { sanitizedMap[this.deSanitizeParam(paramPair[0])] = this.deSanitizeParam(paramPair[1]); From 1702755f625389a32ae3a6c59b1ac2114df0c88a Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Thu, 10 Aug 2023 13:51:44 +0200 Subject: [PATCH 09/15] test core wc luigi client --- ...gi-client-lifecycle-manager-features.cy.js | 51 +++++++++++++++++++ .../src/assets/helloWorldWC.js | 29 ++++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 test/e2e-test-application/cypress/e2e/tests/1-angular/luigi-client-lifecycle-manager-features.cy.js diff --git a/test/e2e-test-application/cypress/e2e/tests/1-angular/luigi-client-lifecycle-manager-features.cy.js b/test/e2e-test-application/cypress/e2e/tests/1-angular/luigi-client-lifecycle-manager-features.cy.js new file mode 100644 index 0000000000..a9dcdba2ba --- /dev/null +++ b/test/e2e-test-application/cypress/e2e/tests/1-angular/luigi-client-lifecycle-manager-features.cy.js @@ -0,0 +1,51 @@ +describe('Luigi client lifecycle manager features', () => { + const localRetries = { + retries: { + runMode: 3, + openMode: 3 + } + }; + + beforeEach(() => { + cy.visitLoggedIn('/'); + }); + + it('setAnchor with wc luigi client', () => { + cy.visitLoggedIn('/projects/pr1/webcomponent'); + cy.expectPathToBe('/projects/pr1/webcomponent'); + cy.url().should('not.contain', 'LuigiRocks'); + cy.get( + '.wcContainer luigi-wc-687474703a2f2f6c6f63616c686f73743a343230302f6173736574732f68656c6c6f576f726c6457432e6a733f656e' + ) + .shadow() + .contains('setAnchor') + .click() + .then(() => { + cy.url().should('include', 'LuigiRocks'); + }); + }); + it('get/add node params with wc luigi client', () => { + const stub = cy.stub(); + cy.on('window:alert', stub); + cy.visitLoggedIn('/projects/pr1/webcomponent'); + cy.expectPathToBe('/projects/pr1/webcomponent'); + cy.get( + '.wcContainer luigi-wc-687474703a2f2f6c6f63616c686f73743a343230302f6173736574732f68656c6c6f576f726c6457432e6a733f656e' + ) + .shadow() + .contains('addNodeParams') + .click() + .then(() => { + cy.url().should('include', '?%7ELuigi=rocks'); + }); + cy.get( + '.wcContainer luigi-wc-687474703a2f2f6c6f63616c686f73743a343230302f6173736574732f68656c6c6f576f726c6457432e6a733f656e' + ) + .shadow() + .contains('getNodeParams') + .click() + .then(() => { + expect(stub.getCall(0)).to.be.calledWith('{"Luigi":"rocks"}'); + }); + }); +}); diff --git a/test/e2e-test-application/src/assets/helloWorldWC.js b/test/e2e-test-application/src/assets/helloWorldWC.js index ab4a060d12..05adda557f 100644 --- a/test/e2e-test-application/src/assets/helloWorldWC.js +++ b/test/e2e-test-application/src/assets/helloWorldWC.js @@ -5,24 +5,35 @@ export default class extends HTMLElement { template.innerHTML = `

Hello World!

`; const templateBtn = document.createElement('template'); - templateBtn.innerHTML = ''; + templateBtn.innerHTML = ''; const empty = document.createElement('template'); empty.innerHTML = `

Test!



`; + const setAnchorBtn = document.createElement('template'); + setAnchorBtn.innerHTML = ``; + const getNodeParamsBtn = document.createElement('template'); + getNodeParamsBtn.innerHTML = ``; + + const addNodeParamsBtn = document.createElement('template'); + addNodeParamsBtn.innerHTML = ``; + this._shadowRoot = this.attachShadow({ mode: 'open', delegatesFocus: false }); this._shadowRoot.appendChild(template.content.cloneNode(true)); this._shadowRoot.appendChild(templateBtn.content.cloneNode(true)); + this._shadowRoot.appendChild(setAnchorBtn.content.cloneNode(true)); + this._shadowRoot.appendChild(getNodeParamsBtn.content.cloneNode(true)); + this._shadowRoot.appendChild(addNodeParamsBtn.content.cloneNode(true)); for (let index = 0; index < 20; index++) { this._shadowRoot.appendChild(empty.content.cloneNode(true)); } this.$paragraph = this._shadowRoot.querySelector('p'); - this.$button = this._shadowRoot.querySelector('button'); + this.$button = this._shadowRoot.querySelector('#clickme'); this.$button.addEventListener('click', () => { if (this.LuigiClient) { this.LuigiClient.uxManager().showAlert({ @@ -43,6 +54,20 @@ export default class extends HTMLElement { }); } }); + + this.$button2 = this._shadowRoot.querySelector('#setAnchor'); + this.$button2.addEventListener('click', () => { + this.LuigiClient.setAnchor('LuigiRocks'); + }); + this.$button3 = this._shadowRoot.querySelector('#getNodeParams'); + this.$button3.addEventListener('click', () => { + let nodeParams = this.LuigiClient.getNodeParams(); + alert(JSON.stringify(nodeParams)); + }); + this.$button4 = this._shadowRoot.querySelector('#addNodeParams'); + this.$button4.addEventListener('click', () => { + this.LuigiClient.addNodeParams({ Luigi: 'rocks' }); + }); } set context(ctx) { From 3a85815ebf875123e1645054965dd2a681063a82 Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Mon, 14 Aug 2023 11:31:03 +0200 Subject: [PATCH 10/15] add/getNodeParams fix --- container/test-app/index.html | 26 ------------------------- core/src/Modal.svelte | 3 ++- core/src/services/split-view.js | 4 +++- core/src/services/web-components.js | 30 ++++++++++++++--------------- 4 files changed, 19 insertions(+), 44 deletions(-) diff --git a/container/test-app/index.html b/container/test-app/index.html index 6ce1f4efb7..9193f7fb7a 100644 --- a/container/test-app/index.html +++ b/container/test-app/index.html @@ -543,34 +543,8 @@ } ); luigiContainer.addEventListener(MFEventID.ADD_NODE_PARAMS_REQUEST, event => { - console.log(event.detail.params); const params = event.detail.params; console.log('params', params); - // const url = new URL(location); - // let hash = url.hash; - // //TODO URLSearchParams does sanitization.. - // let paramPrefix = '~'; - // let localhash = hash; - // const [hashValue, givenQueryParamsString] = localhash.split('?'); - // const searchParams = new URLSearchParams(givenQueryParamsString); - // for (const [key, value] of Object.entries(params)) { - // const paramKey = paramPrefix ? `${paramPrefix}${key}` : key; - // searchParams.set(paramKey, value); - // if (value === undefined) { - // searchParams.delete(paramKey); - // } - // } - // localhash = hashValue; - // if (searchParams.toString() !== '') { - // localhash += `?${searchParams.toString()}`; - // } - // url.hash = localhash; - // event.detail.keepBrowserHistory - // ? window.history.pushState({}, '', url) - // : window.history.replaceState({}, '', url); - // document - // .querySelector('[data-test-id="luigi-client-api-test-01"]') - // .setAttribute('node_params', `${JSON.stringify(params)}`); }); //... diff --git a/core/src/Modal.svelte b/core/src/Modal.svelte index 547d22d7ba..0197bf08bf 100644 --- a/core/src/Modal.svelte +++ b/core/src/Modal.svelte @@ -107,7 +107,8 @@ nodeObject.viewUrl, document.querySelector(modalElementClassSelector), {context: pathData.context}, - nodeObject + nodeObject, + undefined, {isSpecialWC: true} ); dispatch('wcCreated', { modalWC: document.querySelector(modalElementClassSelector), diff --git a/core/src/services/split-view.js b/core/src/services/split-view.js index c6edae679b..ab31754b95 100644 --- a/core/src/services/split-view.js +++ b/core/src/services/split-view.js @@ -85,7 +85,9 @@ class SplitViewSvcClass { lastNode.viewUrl, document.querySelector('.iframeSplitViewCnt'), { context: pathData.context }, - lastNode + lastNode, + undefined, + { isSpecialWC: true } ); const wcInfo = { splitViewWC: document.querySelector('.iframeSplitViewCnt'), diff --git a/core/src/services/web-components.js b/core/src/services/web-components.js index 783c62f636..88674efa04 100644 --- a/core/src/services/web-components.js +++ b/core/src/services/web-components.js @@ -20,20 +20,20 @@ class WebComponentSvcClass { /** Creates a web component with tagname wc_id and adds it to wcItemContainer, * if attached to wc_container */ - attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, viewUrl, nodeId, isCompoundChild) { + attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, viewUrl, nodeId, specialWCProps) { if (wc_container && wc_container.contains(wcItemPlaceholder)) { const wc = document.createElement(wc_id); if (nodeId) { wc.setAttribute('nodeId', nodeId); } wc.setAttribute('lui_web_component', true); - this.initWC(wc, wc_id, wc_container, viewUrl, extendedContext, nodeId, isCompoundChild); + this.initWC(wc, wc_id, wc_container, viewUrl, extendedContext, nodeId, specialWCProps); wc_container.replaceChild(wc, wcItemPlaceholder); } } - initWC(wc, wc_id, eventBusElement, viewUrl, extendedContext, nodeId, isCompoundChild) { + initWC(wc, wc_id, eventBusElement, viewUrl, extendedContext, nodeId, specialWCProps) { const ctx = extendedContext.context; wc.extendedContext = extendedContext; const clientAPI = { @@ -48,12 +48,11 @@ class WebComponentSvcClass { getActiveFeatureToggleList: () => window.Luigi.featureToggles().getActiveFeatureToggleList(), getActiveFeatureToggles: () => window.Luigi.featureToggles().getActiveFeatureToggleList(), addNodeParams: (params, keepBrowserHistory) => { - if (!isCompoundChild) { - window.Luigi.routing().addNodeParams(params, keepBrowserHistory); - } + if (specialWCProps?.isCompoundChild || specialWCProps?.isSpecialWC) return; + window.Luigi.routing().addNodeParams(params, keepBrowserHistory); }, getNodeParams: shouldDesanitise => { - if (isCompoundChild) { + if (specialWCProps?.isCompoundChild || specialWCProps?.isSpecialWC) { return {}; } const result = wc.extendedContext?.nodeParams ? wc.extendedContext.nodeParams : {}; @@ -63,9 +62,8 @@ class WebComponentSvcClass { return wc.extendedContext.nodeParams; }, setAnchor: anchor => { - if (!isCompoundChild) { - window.Luigi.routing().setAnchor(anchor); - } + if (specialWCProps.isCompoundChild) return; + window.Luigi.routing().setAnchor(anchor); } }; @@ -201,7 +199,7 @@ class WebComponentSvcClass { /** Adds a web component defined by viewUrl to the wc_container and sets the node context. * If the web component is not defined yet, it gets imported. */ - renderWebComponent(viewUrl, wc_container, extendedContext, node, nodeId, isCompoundChild) { + renderWebComponent(viewUrl, wc_container, extendedContext, node, nodeId, specialWCProps) { const context = extendedContext.context; const i18nViewUrl = RoutingHelpers.substituteViewUrl(viewUrl, { context }); const wc_id = @@ -210,20 +208,20 @@ class WebComponentSvcClass { wc_container.appendChild(wcItemPlaceholder); wc_container._luigi_node = node; if (window.customElements.get(wc_id)) { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isCompoundChild); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, specialWCProps); } else { /** Custom import function, if defined */ if (window.luigiWCFn) { window.luigiWCFn(i18nViewUrl, wc_id, wcItemPlaceholder, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isCompoundChild); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, specialWCProps); }); } else if (node.webcomponent && node.webcomponent.selfRegistered) { this.includeSelfRegisteredWCFromUrl(node, i18nViewUrl, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isCompoundChild); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, specialWCProps); }); } else { this.registerWCFromUrl(i18nViewUrl, wc_id).then(() => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isCompoundChild); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, specialWCProps); }); } } @@ -313,7 +311,7 @@ class WebComponentSvcClass { renderer.attachCompoundItem(compoundCnt, compoundItemCnt); const nodeId = wc.id || 'gen_' + index; - this.renderWebComponent(wc.viewUrl, compoundItemCnt, { context: ctx }, wc, nodeId, true); + this.renderWebComponent(wc.viewUrl, compoundItemCnt, { context: ctx }, wc, nodeId, { isCompoundChild: true }); registerEventListeners(ebListeners, wc, nodeId); }); wc_container.appendChild(compoundCnt); From 82f6e4f001485e933d8f1247df466a47f2a02579 Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Mon, 14 Aug 2023 14:19:39 +0200 Subject: [PATCH 11/15] runtime err fix --- core/src/services/web-components.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/services/web-components.js b/core/src/services/web-components.js index 88674efa04..b45cdae41e 100644 --- a/core/src/services/web-components.js +++ b/core/src/services/web-components.js @@ -62,7 +62,7 @@ class WebComponentSvcClass { return wc.extendedContext.nodeParams; }, setAnchor: anchor => { - if (specialWCProps.isCompoundChild) return; + if (specialWCProps?.isCompoundChild) return; window.Luigi.routing().setAnchor(anchor); } }; From fa131ac5f2bff375e8ceeba693bbb5933b62df3d Mon Sep 17 00:00:00 2001 From: Johannes Doberer Date: Mon, 14 Aug 2023 14:20:15 +0200 Subject: [PATCH 12/15] Update container/public/LuigiContainer.svelte.d.ts Co-authored-by: Aleksandra Simeonova --- container/public/LuigiContainer.svelte.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/public/LuigiContainer.svelte.d.ts b/container/public/LuigiContainer.svelte.d.ts index 4fdb7b75a2..1ccdd905ee 100644 --- a/container/public/LuigiContainer.svelte.d.ts +++ b/container/public/LuigiContainer.svelte.d.ts @@ -38,7 +38,7 @@ export default class LuigiContainer extends HTMLElement { active_feature_toggle_list: string[]; /** - * The parameters to be passed to the web-component-based micro frontend. Will not passed to the compound children. + * The parameters to be passed to the web-component-based micro frontend. Will not be passed to the compound children. */ node_params: NodeParams; From 5c8d446c684a02c67b21333c8c96cd922cf152db Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Mon, 14 Aug 2023 20:24:06 +0200 Subject: [PATCH 13/15] no api for special --- core/src/services/web-components.js | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/core/src/services/web-components.js b/core/src/services/web-components.js index b45cdae41e..b3df011779 100644 --- a/core/src/services/web-components.js +++ b/core/src/services/web-components.js @@ -20,20 +20,20 @@ class WebComponentSvcClass { /** Creates a web component with tagname wc_id and adds it to wcItemContainer, * if attached to wc_container */ - attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, viewUrl, nodeId, specialWCProps) { + attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, viewUrl, nodeId, isSpecialWC) { if (wc_container && wc_container.contains(wcItemPlaceholder)) { const wc = document.createElement(wc_id); if (nodeId) { wc.setAttribute('nodeId', nodeId); } wc.setAttribute('lui_web_component', true); - this.initWC(wc, wc_id, wc_container, viewUrl, extendedContext, nodeId, specialWCProps); + this.initWC(wc, wc_id, wc_container, viewUrl, extendedContext, nodeId, isSpecialWC); wc_container.replaceChild(wc, wcItemPlaceholder); } } - initWC(wc, wc_id, eventBusElement, viewUrl, extendedContext, nodeId, specialWCProps) { + initWC(wc, wc_id, eventBusElement, viewUrl, extendedContext, nodeId, isSpecialWC) { const ctx = extendedContext.context; wc.extendedContext = extendedContext; const clientAPI = { @@ -48,11 +48,12 @@ class WebComponentSvcClass { getActiveFeatureToggleList: () => window.Luigi.featureToggles().getActiveFeatureToggleList(), getActiveFeatureToggles: () => window.Luigi.featureToggles().getActiveFeatureToggleList(), addNodeParams: (params, keepBrowserHistory) => { - if (specialWCProps?.isCompoundChild || specialWCProps?.isSpecialWC) return; - window.Luigi.routing().addNodeParams(params, keepBrowserHistory); + if (!isSpecialWC) { + window.Luigi.routing().addNodeParams(params, keepBrowserHistory); + } }, getNodeParams: shouldDesanitise => { - if (specialWCProps?.isCompoundChild || specialWCProps?.isSpecialWC) { + if (isSpecialWC) { return {}; } const result = wc.extendedContext?.nodeParams ? wc.extendedContext.nodeParams : {}; @@ -62,8 +63,9 @@ class WebComponentSvcClass { return wc.extendedContext.nodeParams; }, setAnchor: anchor => { - if (specialWCProps?.isCompoundChild) return; - window.Luigi.routing().setAnchor(anchor); + if (!isSpecialWC) { + window.Luigi.routing().setAnchor(anchor); + } } }; @@ -199,7 +201,7 @@ class WebComponentSvcClass { /** Adds a web component defined by viewUrl to the wc_container and sets the node context. * If the web component is not defined yet, it gets imported. */ - renderWebComponent(viewUrl, wc_container, extendedContext, node, nodeId, specialWCProps) { + renderWebComponent(viewUrl, wc_container, extendedContext, node, nodeId, isSpecialWC) { const context = extendedContext.context; const i18nViewUrl = RoutingHelpers.substituteViewUrl(viewUrl, { context }); const wc_id = @@ -208,20 +210,20 @@ class WebComponentSvcClass { wc_container.appendChild(wcItemPlaceholder); wc_container._luigi_node = node; if (window.customElements.get(wc_id)) { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, specialWCProps); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isSpecialWC); } else { /** Custom import function, if defined */ if (window.luigiWCFn) { window.luigiWCFn(i18nViewUrl, wc_id, wcItemPlaceholder, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, specialWCProps); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isSpecialWC); }); } else if (node.webcomponent && node.webcomponent.selfRegistered) { this.includeSelfRegisteredWCFromUrl(node, i18nViewUrl, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, specialWCProps); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isSpecialWC); }); } else { this.registerWCFromUrl(i18nViewUrl, wc_id).then(() => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, specialWCProps); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isSpecialWC); }); } } @@ -311,7 +313,7 @@ class WebComponentSvcClass { renderer.attachCompoundItem(compoundCnt, compoundItemCnt); const nodeId = wc.id || 'gen_' + index; - this.renderWebComponent(wc.viewUrl, compoundItemCnt, { context: ctx }, wc, nodeId, { isCompoundChild: true }); + this.renderWebComponent(wc.viewUrl, compoundItemCnt, { context: ctx }, wc, nodeId, true); registerEventListeners(ebListeners, wc, nodeId); }); wc_container.appendChild(compoundCnt); From 308a23426b6cfcdbcf3a54fc9275358f67ee4bd3 Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Mon, 14 Aug 2023 20:33:47 +0200 Subject: [PATCH 14/15] renaming --- .../src/services/webcomponents.service.ts | 34 +++++++------------ core/src/Modal.svelte | 2 +- core/src/services/split-view.js | 2 +- core/src/services/web-components.js | 22 ++++++------ 4 files changed, 26 insertions(+), 34 deletions(-) diff --git a/container/src/services/webcomponents.service.ts b/container/src/services/webcomponents.service.ts index d39d1e9a39..282ba41ad3 100644 --- a/container/src/services/webcomponents.service.ts +++ b/container/src/services/webcomponents.service.ts @@ -37,7 +37,7 @@ export class WebComponentService { ctx, viewUrl: string, nodeId: string, - isCompoundChild?: boolean + isSpecialMf?: boolean ) { if (wc_container && wc_container.contains(wcItemPlaceholder)) { const wc = document.createElement(wc_id); @@ -45,7 +45,7 @@ export class WebComponentService { wc.setAttribute('nodeId', nodeId); } - this.initWC(wc, wc_id, wc_container, viewUrl, ctx, nodeId, isCompoundChild); + this.initWC(wc, wc_id, wc_container, viewUrl, ctx, nodeId, isSpecialMf); wc_container.replaceChild(wc, wcItemPlaceholder); if (wc_container._luigi_node) { wc_container._luigi_mfe_webcomponent = wc; @@ -73,7 +73,7 @@ export class WebComponentService { * @param wc_id a tagname that is used when creating the web component element * @returns an object with the Luigi Client API */ - createClientAPI(eventBusElement, nodeId: string, wc_id: string, isCompoundChild?: boolean) { + createClientAPI(eventBusElement, nodeId: string, wc_id: string, isSpecialMf?: boolean) { return { linkManager: () => { return { @@ -118,13 +118,13 @@ export class WebComponentService { this.dispatchLuigiEvent(Events.INITIALIZED, {}); }, addNodeParams: (params, keepBrowserHistory) => { - if (isCompoundChild) { + if (isSpecialMf) { return; } this.dispatchLuigiEvent(Events.ADD_NODE_PARAMS_REQUEST, { params, keepBrowserHistory }); }, getNodeParams: shouldDesanitise => { - if (isCompoundChild) { + if (isSpecialMf) { return {}; } let result = this.thisComponent.getAttribute('node_params') || {}; @@ -135,7 +135,7 @@ export class WebComponentService { return result; }, setAnchor: anchor => { - if (isCompoundChild) { + if (isSpecialMf) { return; } this.dispatchLuigiEvent(Events.SET_ANCHOR_LINK_REQUEST, anchor); @@ -143,16 +143,8 @@ export class WebComponentService { }; } - initWC( - wc: HTMLElement | any, - wc_id, - eventBusElement, - viewUrl: string, - ctx, - nodeId: string, - isCompoundChild?: boolean - ) { - const clientAPI = this.createClientAPI(eventBusElement, nodeId, wc_id, isCompoundChild); + initWC(wc: HTMLElement | any, wc_id, eventBusElement, viewUrl: string, ctx, nodeId: string, isSpecialMf?: boolean) { + const clientAPI = this.createClientAPI(eventBusElement, nodeId, wc_id, isSpecialMf); if (wc.__postProcess) { const url = @@ -293,7 +285,7 @@ export class WebComponentService { context: any, node: any, nodeId?: any, - isCompoundChild?: boolean + isSpecialMf?: boolean ) { const i18nViewUrl = this.processViewUrl(viewUrl, { context }); const wc_id = @@ -303,21 +295,21 @@ export class WebComponentService { wc_container._luigi_node = node; if (window.customElements.get(wc_id)) { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId, isCompoundChild); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId, isSpecialMf); } else { /** Custom import function, if defined */ if ((window as any).luigiWCFn) { (window as any).luigiWCFn(i18nViewUrl, wc_id, wcItemPlaceholder, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId, isCompoundChild); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId, isSpecialMf); }); } else if (node.webcomponent && node.webcomponent.selfRegistered) { this.includeSelfRegisteredWCFromUrl(node, i18nViewUrl, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId, isCompoundChild); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId, isSpecialMf); }); } else { this.registerWCFromUrl(i18nViewUrl, wc_id) .then(() => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId, isCompoundChild); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, context, i18nViewUrl, nodeId, isSpecialMf); }) .catch(error => { console.warn('ERROR =>', error); diff --git a/core/src/Modal.svelte b/core/src/Modal.svelte index 0197bf08bf..b6d1f4eca6 100644 --- a/core/src/Modal.svelte +++ b/core/src/Modal.svelte @@ -108,7 +108,7 @@ document.querySelector(modalElementClassSelector), {context: pathData.context}, nodeObject, - undefined, {isSpecialWC: true} + undefined, true ); dispatch('wcCreated', { modalWC: document.querySelector(modalElementClassSelector), diff --git a/core/src/services/split-view.js b/core/src/services/split-view.js index ab31754b95..8aae7fa3a6 100644 --- a/core/src/services/split-view.js +++ b/core/src/services/split-view.js @@ -87,7 +87,7 @@ class SplitViewSvcClass { { context: pathData.context }, lastNode, undefined, - { isSpecialWC: true } + true ); const wcInfo = { splitViewWC: document.querySelector('.iframeSplitViewCnt'), diff --git a/core/src/services/web-components.js b/core/src/services/web-components.js index b3df011779..cd0c4d8412 100644 --- a/core/src/services/web-components.js +++ b/core/src/services/web-components.js @@ -20,20 +20,20 @@ class WebComponentSvcClass { /** Creates a web component with tagname wc_id and adds it to wcItemContainer, * if attached to wc_container */ - attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, viewUrl, nodeId, isSpecialWC) { + attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, viewUrl, nodeId, isSpecialMf) { if (wc_container && wc_container.contains(wcItemPlaceholder)) { const wc = document.createElement(wc_id); if (nodeId) { wc.setAttribute('nodeId', nodeId); } wc.setAttribute('lui_web_component', true); - this.initWC(wc, wc_id, wc_container, viewUrl, extendedContext, nodeId, isSpecialWC); + this.initWC(wc, wc_id, wc_container, viewUrl, extendedContext, nodeId, isSpecialMf); wc_container.replaceChild(wc, wcItemPlaceholder); } } - initWC(wc, wc_id, eventBusElement, viewUrl, extendedContext, nodeId, isSpecialWC) { + initWC(wc, wc_id, eventBusElement, viewUrl, extendedContext, nodeId, isSpecialMf) { const ctx = extendedContext.context; wc.extendedContext = extendedContext; const clientAPI = { @@ -48,12 +48,12 @@ class WebComponentSvcClass { getActiveFeatureToggleList: () => window.Luigi.featureToggles().getActiveFeatureToggleList(), getActiveFeatureToggles: () => window.Luigi.featureToggles().getActiveFeatureToggleList(), addNodeParams: (params, keepBrowserHistory) => { - if (!isSpecialWC) { + if (!isSpecialMf) { window.Luigi.routing().addNodeParams(params, keepBrowserHistory); } }, getNodeParams: shouldDesanitise => { - if (isSpecialWC) { + if (isSpecialMf) { return {}; } const result = wc.extendedContext?.nodeParams ? wc.extendedContext.nodeParams : {}; @@ -63,7 +63,7 @@ class WebComponentSvcClass { return wc.extendedContext.nodeParams; }, setAnchor: anchor => { - if (!isSpecialWC) { + if (!isSpecialMf) { window.Luigi.routing().setAnchor(anchor); } } @@ -201,7 +201,7 @@ class WebComponentSvcClass { /** Adds a web component defined by viewUrl to the wc_container and sets the node context. * If the web component is not defined yet, it gets imported. */ - renderWebComponent(viewUrl, wc_container, extendedContext, node, nodeId, isSpecialWC) { + renderWebComponent(viewUrl, wc_container, extendedContext, node, nodeId, isSpecialMf) { const context = extendedContext.context; const i18nViewUrl = RoutingHelpers.substituteViewUrl(viewUrl, { context }); const wc_id = @@ -210,20 +210,20 @@ class WebComponentSvcClass { wc_container.appendChild(wcItemPlaceholder); wc_container._luigi_node = node; if (window.customElements.get(wc_id)) { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isSpecialWC); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isSpecialMf); } else { /** Custom import function, if defined */ if (window.luigiWCFn) { window.luigiWCFn(i18nViewUrl, wc_id, wcItemPlaceholder, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isSpecialWC); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isSpecialMf); }); } else if (node.webcomponent && node.webcomponent.selfRegistered) { this.includeSelfRegisteredWCFromUrl(node, i18nViewUrl, () => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isSpecialWC); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isSpecialMf); }); } else { this.registerWCFromUrl(i18nViewUrl, wc_id).then(() => { - this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isSpecialWC); + this.attachWC(wc_id, wcItemPlaceholder, wc_container, extendedContext, i18nViewUrl, nodeId, isSpecialMf); }); } } From 9b40a76263dd3a2e99b8fe2374f6c01ca9363769 Mon Sep 17 00:00:00 2001 From: "Doberer, Johannes" Date: Wed, 16 Aug 2023 08:00:58 +0200 Subject: [PATCH 15/15] container anchor --- container/test-app/helloWorldWC.js | 10 ++++++++++ container/test-app/index.html | 3 +++ 2 files changed, 13 insertions(+) diff --git a/container/test-app/helloWorldWC.js b/container/test-app/helloWorldWC.js index e1e22acbb5..2fb8b88d57 100644 --- a/container/test-app/helloWorldWC.js +++ b/container/test-app/helloWorldWC.js @@ -16,6 +16,9 @@ export default class extends HTMLElement { const getNodeParamsBtn = document.createElement('template'); getNodeParamsBtn.innerHTML = ''; + const setAnchorBtn = document.createElement('template'); + setAnchorBtn.innerHTML = ''; + const empty = document.createElement('template'); empty.innerHTML = `

Test!



`; @@ -27,6 +30,7 @@ export default class extends HTMLElement { this._shadowRoot.appendChild(templateBtn.content.cloneNode(true)); this._shadowRoot.appendChild(addNodeParamsBtn.content.cloneNode(true)); this._shadowRoot.appendChild(getNodeParamsBtn.content.cloneNode(true)); + this._shadowRoot.appendChild(setAnchorBtn.content.cloneNode(true)); for (let index = 0; index < 10; index++) { this._shadowRoot.appendChild(empty.content.cloneNode(true)); @@ -69,6 +73,12 @@ export default class extends HTMLElement { }); } }); + this.$setAnchorBtn = this._shadowRoot.querySelector('#setAnchor'); + this.$setAnchorBtn.addEventListener('click', () => { + if (this.LuigiClient) { + this.LuigiClient.setAnchor('#myAnchor'); + } + }); } set context(ctx) { diff --git a/container/test-app/index.html b/container/test-app/index.html index 9193f7fb7a..6bb80f282e 100644 --- a/container/test-app/index.html +++ b/container/test-app/index.html @@ -546,6 +546,9 @@ const params = event.detail.params; console.log('params', params); }); + luigiContainer.addEventListener(MFEventID.SET_ANCHOR_LINK_REQUEST, event => { + console.log('anhor', event.detail); + }); //... });