Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getUserSettings for webcomponents #3414

Merged
merged 21 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d75c78f
getUserSettings for luigi client wc
JohannesDoberer Aug 28, 2023
1d5dc17
Merge branch 'main' into getUserSettings-wc-luigi-client
JohannesDoberer Aug 28, 2023
e89c5b8
remove left over
JohannesDoberer Aug 28, 2023
eb98300
Merge branch 'main' into getUserSettings-wc-luigi-client
JohannesDoberer Aug 29, 2023
610dd1e
userSettings for container
JohannesDoberer Aug 29, 2023
9b2ca4e
e2e tests container
JohannesDoberer Aug 29, 2023
36c4b0d
fix test
JohannesDoberer Aug 29, 2023
1675468
fix runtime error
JohannesDoberer Aug 29, 2023
43fb14b
ts definition
JohannesDoberer Aug 29, 2023
4200f04
ts declaration CompoundContainer
JohannesDoberer Aug 30, 2023
881bb5e
web-components test
JohannesDoberer Aug 30, 2023
0d067fa
Merge branch 'main' into getUserSettings-wc-luigi-client
JohannesDoberer Aug 30, 2023
1329a2b
Merge branch 'main' into getUserSettings-wc-luigi-client
JohannesDoberer Aug 31, 2023
9a5239f
Merge branch 'main' into getUserSettings-wc-luigi-client
JohannesDoberer Sep 1, 2023
bf8939b
Merge branch 'main' into getUserSettings-wc-luigi-client
JohannesDoberer Sep 1, 2023
0137640
Merge branch 'main' into getUserSettings-wc-luigi-client
JohannesDoberer Sep 4, 2023
02d8a51
Merge branch 'main' into getUserSettings-wc-luigi-client
ndricimrr Sep 12, 2023
d839534
Update core/src/services/web-components.js
JohannesDoberer Sep 18, 2023
91a91c9
Merge branch 'main' into getUserSettings-wc-luigi-client
JohannesDoberer Sep 18, 2023
667b4a5
suggestions
JohannesDoberer Sep 18, 2023
106b6f7
return null on reject
JohannesDoberer Sep 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 52 additions & 14 deletions container/cypress/e2e/wc-container.cy.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,57 @@
describe('Web Container Test', () => {
it('LuigiClient API', () => {
cy.visit('http://localhost:8080');
cy.visit('http://localhost:8080/#hello-world-wc');
describe('LuigiClient API LuigiContainer', () => {
let stub;
beforeEach(() => {
cy.visit('http://localhost:8080');
cy.visit('http://localhost:8080/#hello-world-wc');
stub = cy.stub();
});

const stub = cy.stub();
cy.on('window:alert', stub);
it('getCurrentLocale, getActiveFeatureToggles, getCurrentTheme', () => {
cy.on('window:alert', stub);

cy.get('[data-test-id="luigi-client-api-test-01"]')
.shadow()
.contains('Click me')
.click()
.then(() => {
expect(stub.getCall(0)).to.be.calledWith('LuigiClient.getCurrentLocale()=en');
expect(stub.getCall(1)).to.be.calledWith('LuigiClient.getActiveFeatureToggles()=["ft1","ft2"]');
expect(stub.getCall(2)).to.be.calledWith('LuigiClient.uxManager().getCurrentTheme()=sap_fiori_3');
});
cy.get('[data-test-id="luigi-client-api-test-01"]')
.shadow()
.contains('Click me')
.click()
.then(() => {
expect(stub.getCall(0)).to.be.calledWith('LuigiClient.getCurrentLocale()=en');
expect(stub.getCall(1)).to.be.calledWith('LuigiClient.getActiveFeatureToggles()=["ft1","ft2"]');
expect(stub.getCall(2)).to.be.calledWith('LuigiClient.uxManager().getCurrentTheme()=sap_fiori_3');
});
});

it('LuigiClient API getUserSettings for LuigiContainer', () => {
const stub = cy.stub();
cy.on('window:alert', stub);

cy.get('[data-test-id="luigi-client-api-test-01"]')
.shadow()
.contains('getUserSettings')
.click()
.then(() => {
expect(stub.getCall(0)).to.be.calledWith('LuigiClient.getUserSettings()={"language":"de","date":""}');
});
});
});

describe('LuigiClient API LuigiContainer', () => {
let stub;
beforeEach(() => {
cy.visit('http://localhost:8080/#dashboard');
stub = cy.stub();
});

it('LuigiClient API getUserSettings for LuigiCompoundContainer', () => {
const stub = cy.stub();
cy.on('window:alert', stub);
cy.get('[data-test-id="luigi-client-api-test-compound-01"]')
.shadow()
.contains('getUserSettings')
.click()
.then(() => {
expect(stub.getCall(0)).to.be.calledWith('LuigiClient.getUserSettings()={"language":"it","date":""}');
});
});
});
});
34 changes: 23 additions & 11 deletions container/src/LuigiCompoundContainer.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
<svelte:options customElement={{
tag: null,
props: {
viewurl: { type: 'String', reflect: false, attribute: 'viewurl' },
deferInit: { type: 'Boolean', attribute: 'defer-init' },
context: { type: 'String', reflect: false, attribute: 'context' },
compoundConfig: { type: 'Object', reflect: false, attribute: 'compound-config' },
nodeParams: { type: 'Object', reflect: false, attribute: 'node-params' }
}
}} />
<svelte:options
customElement={{
tag: null,
props: {
viewurl: { type: 'String', reflect: false, attribute: 'viewurl' },
deferInit: { type: 'Boolean', attribute: 'defer-init' },
context: { type: 'String', reflect: false, attribute: 'context' },
compoundConfig: {
type: 'Object',
reflect: false,
attribute: 'compound-config'
},
nodeParams: { type: 'Object', reflect: false, attribute: 'node-params' },
userSettings: {
type: 'Object',
reflect: false,
attribute: 'user-settings'
}
}
}}
/>

<script lang="ts">
import { onMount } from 'svelte';
Expand All @@ -20,6 +31,7 @@
export let deferInit: boolean;
export let compoundConfig: any;
export let nodeParams: any;
export let userSettings: any;

let containerInitialized = false;
let mainComponent: HTMLElement;
Expand All @@ -30,7 +42,7 @@

// Only needed for get rid of "unused export property" svelte compiler warnings
export const unwarn = () => {
return nodeParams;
return nodeParams && userSettings;
};

const initialize = (thisComponent: any) => {
Expand Down
64 changes: 45 additions & 19 deletions container/src/LuigiContainer.svelte
Original file line number Diff line number Diff line change
@@ -1,28 +1,51 @@
<svelte:options customElement={{
tag: null,
props: {
viewurl: { type: 'String', reflect: false, attribute: 'viewurl' },
deferInit: { type: 'Boolean', attribute: 'defer-init' },
context: { type: 'String', reflect: false, attribute: 'context' },
label: { type: 'String', reflect: false, attribute: 'label' },
webcomponent: { type: 'String', reflect: false, attribute: 'webcomponent' },
locale: { type: 'String', reflect: false, attribute: 'locale' },
theme: { type: 'String', reflect: false, attribute: 'theme' },
activeFeatureToggleList: { type: 'Array', reflect: false, attribute: 'active-feature-toggle-list' },
skipInitCheck: { type: 'Boolean', reflect: false, attribute: 'skip-init-check' },
nodeParams: { type: 'Object', reflect: false, attribute: 'node-params' }
},
extend: (customElementConstructor) => {
const notInitFn = (name) => {
return () => console.warn(name + ' can\'t be called on luigi-container before its micro frontend is attached to the DOM.');
<svelte:options
customElement={{
tag: null,
props: {
viewurl: { type: 'String', reflect: false, attribute: 'viewurl' },
deferInit: { type: 'Boolean', attribute: 'defer-init' },
context: { type: 'String', reflect: false, attribute: 'context' },
label: { type: 'String', reflect: false, attribute: 'label' },
webcomponent: {
type: 'String',
reflect: false,
attribute: 'webcomponent'
},
locale: { type: 'String', reflect: false, attribute: 'locale' },
theme: { type: 'String', reflect: false, attribute: 'theme' },
activeFeatureToggleList: {
type: 'Array',
reflect: false,
attribute: 'active-feature-toggle-list'
},
skipInitCheck: {
type: 'Boolean',
reflect: false,
attribute: 'skip-init-check'
},
nodeParams: { type: 'Object', reflect: false, attribute: 'node-params' },
userSettings: {
type: 'Object',
reflect: false,
attribute: 'user-settings'
}
},
extend: customElementConstructor => {
let notInitFn = name => {
return () =>
console.warn(
name +
" can't be called on luigi-container before its micro frontend is attached to the DOM."
);
};
return class extends customElementConstructor {
sendCustomMessage = notInitFn('sendCustomMessage');
updateContext = notInitFn('updateContext');
closeAlert = notInitFn('closeAlert');
};
}
}} />
}}
/>

<script lang="ts">
import { onMount, onDestroy } from 'svelte';
Expand All @@ -41,6 +64,7 @@
export let activeFeatureToggleList: string[];
export let skipInitCheck: boolean;
export let nodeParams: any;
export let userSettings: any;

const iframeHandle:
| {
Expand All @@ -55,7 +79,9 @@

// Only needed for get rid of "unused export property" svelte compiler warnings
export const unwarn = () => {
return locale && theme && activeFeatureToggleList && nodeParams;
return (
locale && theme && activeFeatureToggleList && nodeParams && userSettings
);
};

const initialize = (thisComponent: any) => {
Expand Down
3 changes: 3 additions & 0 deletions container/src/services/webcomponents.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ export class WebComponentService {
return;
}
this.dispatchLuigiEvent(Events.SET_ANCHOR_LINK_REQUEST, anchor);
},
getUserSettings: () => {
return JSON.parse(this.thisComponent.getAttribute('user-settings')) || {};
}
};
}
Expand Down
14 changes: 14 additions & 0 deletions container/test-app/helloWorldWC.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export default class extends HTMLElement {
const empty = document.createElement('template');
empty.innerHTML = `<section><p>Test!</p><br/><br/></section>`;

const getUserSettingsBtn = document.createElement('template');
getUserSettingsBtn.innerHTML = '<button id="getUserSettings">getUserSettings</button>';

this._shadowRoot = this.attachShadow({
mode: 'open',
delegatesFocus: false
Expand All @@ -35,6 +38,7 @@ export default class extends HTMLElement {
this._shadowRoot.appendChild(addNodeParamsBtn.content.cloneNode(true));
this._shadowRoot.appendChild(getNodeParamsBtn.content.cloneNode(true));
this._shadowRoot.appendChild(setAnchorBtn.content.cloneNode(true));
this._shadowRoot.appendChild(getUserSettingsBtn.content.cloneNode(true));

for (let index = 0; index < 10; index++) {
this._shadowRoot.appendChild(empty.content.cloneNode(true));
Expand Down Expand Up @@ -88,6 +92,16 @@ export default class extends HTMLElement {
this.LuigiClient.setAnchor('#myAnchor');
}
});
this.$getUserSettingsBtn = this._shadowRoot.querySelector('#getUserSettings');
this.$getUserSettingsBtn.addEventListener('click', () => {
if (this.LuigiClient) {
let userSettings = this.LuigiClient.getUserSettings();
this.LuigiClient.uxManager().showAlert({
text: 'LuigiClient.getUserSettings()=' + JSON.stringify(userSettings),
type: 'info'
});
}
});
}

set context(ctx) {
Expand Down
3 changes: 3 additions & 0 deletions container/test-app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,11 @@
<!-- LUIGI Micro Frontends -->
<div class="lui-wrapper" route="dashboard">
<luigi-compound-container
data-test-id="luigi-client-api-test-compound-01"
id="dashboard"
context='{"label": "Dashboard"}'
node-params='{"Luigi":"rocks"}'
user-settings='{"language":"it", "date":""}'
></luigi-compound-container>
</div>
<div class="lui-wrapper" route="dashboard2">
Expand Down Expand Up @@ -358,6 +360,7 @@
theme="sap_fiori_3"
active-feature-toggle-list='["ft1","ft2"]'
node-params='{"Luigi":"rocks from attri &<strong>asdf<strong> bute"}'
user-settings='{"language": "de", "date":""}'
></luigi-container>
</div>
<div class="lui-wrapper" route="error-test">
Expand Down
7 changes: 7 additions & 0 deletions container/typings/LuigiCompoundContainer.svelte.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
export declare interface UserSettings {
[key: string]: number | string | boolean;
}
export default class LuigiCompoundContainer extends HTMLElement {
compoundConfig: any;

/**
* Manually triggers the micro frontend rendering process when using defer-init attribute
*/
init(): Function;
/**
* The user settings to be passed to the web-component-based micro frontend
*/
userSettings: UserSettings;
}
8 changes: 8 additions & 0 deletions container/typings/LuigiContainer.svelte.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
export declare interface NodeParams {
[key: string]: string;
}
export declare interface UserSettings {
[key: string]: number | string | boolean;
}
export default class LuigiContainer extends HTMLElement {
/**
* The URL of the microfrontend to be rendered
Expand Down Expand Up @@ -60,4 +63,9 @@ export default class LuigiContainer extends HTMLElement {
* Manually triggers the micro frontend rendering process when using defer-init attribute
*/
init(): Function;

/**
* The user settings to be passed to the web-component-based micro frontend
*/
userSettings: UserSettings;
}
2 changes: 2 additions & 0 deletions core/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@
};
},
set: (obj) => {
const wc_container = document.querySelector('.wcContainer');
if(wc_container)wc_container.configChangedRequest = true;
JohannesDoberer marked this conversation as resolved.
Show resolved Hide resolved
if (obj) {
noAnimation = false;
Object.getOwnPropertyNames(obj).forEach((prop) => {
Expand Down
18 changes: 13 additions & 5 deletions core/src/services/routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ class RoutingClass {
}
this.navigateWebComponent(component, nodeObject);
} else {
const wc_container = document.querySelector('.wcContainer');
if (wc_container) wc_container.configChangedRequest = false;
if (iContainer) {
iContainer.classList.remove('lui-webComponent');
this.removeLastChildFromWCContainer();
Expand Down Expand Up @@ -644,21 +646,22 @@ class RoutingClass {
}

navigateWebComponent(component, navNode) {
let wc_containerNode = document.querySelector('.wcContainer')._luigi_node;
let wc_container = document.querySelector('.wcContainer');
let wc_containerNode = wc_container._luigi_node;
const wc_id = this.getGeneratedWCId(navNode);

const componentData = component.get();
// if true, do only a context update and not rerender the wc
if (navNode === wc_containerNode) {
if (navNode === wc_containerNode && !wc_container.configChangedRequest) {
const wc = document.querySelector(wc_id);
wc.context = componentData.context;
if (wc.extendedContext) {
wc.extendedContext.nodeParams = componentData.nodeParams;
}
return;
}

const wc_container = this.removeLastChildFromWCContainer();
wc_container.configChangedRequest = false;
wc_container = this.removeLastChildFromWCContainer();
if (!wc_container) return;

WebComponentService.renderWebComponent(componentData.viewUrl, wc_container, componentData, navNode);
Expand All @@ -670,9 +673,14 @@ class RoutingClass {

const componentData = component.get();

if (wc_container._luigi_node === navNode && isEqual(wc_container._luigi_pathParams, componentData.pathParams)) {
if (
wc_container._luigi_node === navNode &&
isEqual(wc_container._luigi_pathParams, componentData.pathParams) &&
!wc_container.configChangedRequest
) {
return;
}
wc_container.configChangedRequest = false;
const { compound } = navNode;
this.removeLastChildFromWCContainer();

Expand Down
Loading