Skip to content

Commit

Permalink
Add getPathParams, getCoreSearchParams, getClientPermissions (#3390)
Browse files Browse the repository at this point in the history
  • Loading branch information
ndricimrr authored Oct 6, 2023
1 parent 186b213 commit 25e36cb
Show file tree
Hide file tree
Showing 17 changed files with 286 additions and 24 deletions.
22 changes: 22 additions & 0 deletions client/luigi-element.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,4 +443,26 @@ export interface LuigiClient {
* @memberof LuigiClient
*/
setAnchor: (anchor: string) => void;
/**
* Retrieves the search params from the active URL
* @returns {Object} containing the search params
* @memberof LuigiClient
*/
getCoreSearchParams: () => Object;
/**
* Returns the dynamic path parameters of the active URL.
* Path parameters are defined by navigation nodes with a dynamic **pathSegment** value starting with **:**, such as **productId**.
* All path parameters in the current navigation path (as defined by the active URL) are returned.
* <!-- add-attribute:class:warning -->
* > **NOTE:** some special characters (`<`, `>`, `"`, `'`, `/`) in path parameters are HTML-encoded.
* @returns {Object} path parameters, where the object property name is the path parameter name without the prefix, and its value is the actual value of the path parameter. For example ` {productId: 1234, ...}`
* @memberof LuigiClient
*/
getPathParams: () => Object;
/**
* Returns the current client permissions as specified in the navigation node or an empty object. For details, see [Node parameters](navigation-parameters-reference.md).
* @returns {Object} client permissions as specified in the navigation node
* @memberof LuigiClient
*/
getClientPermissions(): () => Object;
}
41 changes: 40 additions & 1 deletion container/cypress/e2e/wc-container.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,45 @@ describe('Web Container Test', () => {
});
});

it('getCoreSearchParams', () => {
const stub = cy.stub();

cy.on('window:alert', stub);

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

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

cy.get('[data-test-id="luigi-client-api-test-01"]')
.shadow()
.contains('getPathParams')
.click()
.then(() => {
expect(stub.getCall(0)).to.be.calledWith('{"path":"param"}');
});
});

it('getClientPermissions', () => {
const stub = cy.stub();
cy.on('window:alert', stub);
cy.get('[data-test-id="luigi-client-api-test-01"]')
.shadow()
.contains('getClientPermissions')
.click()
.then(() => {
expect(stub.getCall(0)).to.be.calledWith('{"permission":"testPermission"}');
});
});

it('LuigiClient API getUserSettings for LuigiContainer', () => {
const stub = cy.stub();
cy.on('window:alert', stub);
Expand All @@ -35,7 +74,7 @@ describe('Web Container Test', () => {
});
});

describe('LuigiClient API LuigiContainer', () => {
describe('LuigiClient API LuigiCompoundContainer', () => {
let stub;
beforeEach(() => {
cy.visit('http://localhost:8080/#dashboard');
Expand Down
6 changes: 3 additions & 3 deletions container/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
"serve": "npm run build && npm run copyLuigiElement && npm run copyBundle && sirv -D test-app --no-clear",
"bundle:watch": "chokidar \"src/**/*.*\" -c \"npm run build && npm run copyBundle\"",
"start": "concurrently -k \"npm run serve\" \"npm run bundle:watch\"",
"cypress-headless": "cypress run",
"cypress-browser": "cypress open --e2e --browser chrome"
"cypress-headless": "cypress run -c video=false",
"cypress-browser": "cypress open --e2e --browser chrome -c video=false"
},
"devDependencies": {
"@babel/node": "7.22.10",
Expand Down Expand Up @@ -58,4 +58,4 @@
"engines": {
"node": ">=18"
}
}
}
10 changes: 8 additions & 2 deletions container/src/LuigiCompoundContainer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
type: 'Object',
reflect: false,
attribute: 'user-settings'
}
},
searchParams: { type: 'Object', reflect: false, attribute: 'search-params' },
pathParams: { type: 'Object', reflect: false, attribute: 'path-params' },
clientPermissions: { type: 'Object', reflect: false, attribute: 'client-permissions' }
}
}}
/>
Expand All @@ -31,6 +34,9 @@
export let deferInit: boolean;
export let compoundConfig: any;
export let nodeParams: any;
export let searchParams: any;
export let pathParams: any;
export let clientPermissions: any;
export let userSettings: any;
let containerInitialized = false;
Expand All @@ -42,7 +48,7 @@
// Only needed for get rid of "unused export property" svelte compiler warnings
export const unwarn = () => {
return nodeParams && userSettings;
return nodeParams && searchParams && pathParams && clientPermissions && userSettings;
};
const initialize = (thisComponent: any) => {
Expand Down
14 changes: 10 additions & 4 deletions container/src/LuigiContainer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
type: 'Object',
reflect: false,
attribute: 'user-settings'
}
},
searchParams: { type: 'Object', reflect: false, attribute: 'search-params' },
pathParams: { type: 'Object', reflect: false, attribute: 'path-params' },
clientPermissions: { type: 'Object', reflect: false, attribute: 'client-permissions' }
},
extend: customElementConstructor => {
let notInitFn = name => {
Expand Down Expand Up @@ -64,8 +67,13 @@
export let activeFeatureToggleList: string[];
export let skipInitCheck: boolean;
export let nodeParams: any;
export let searchParams: any;
export let pathParams: any;
export let clientPermissions: any;
export let userSettings: any;
const iframeHandle:
| {
iframe: HTMLIFrameElement;
Expand All @@ -79,9 +87,7 @@
// Only needed for get rid of "unused export property" svelte compiler warnings
export const unwarn = () => {
return (
locale && theme && activeFeatureToggleList && nodeParams && userSettings
);
return locale && theme && activeFeatureToggleList && nodeParams && searchParams && pathParams && clientPermissions && userSettings;
};
const initialize = (thisComponent: any) => {
Expand Down
15 changes: 15 additions & 0 deletions container/src/services/webcomponents.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,21 @@ export class WebComponentService {
}
this.dispatchLuigiEvent(Events.SET_ANCHOR_LINK_REQUEST, anchor);
},
getCoreSearchParams: () => {
let result = this.thisComponent.getAttribute('search-params') || {};
result = JSON.parse(result);
return result;
},
getPathParams: () => {
let result = this.thisComponent.getAttribute('path-params') || {};
result = JSON.parse(result);
return result;
},
getClientPermissions: () => {
let result = this.thisComponent.getAttribute('client-permissions') || {};
result = JSON.parse(result);
return result;
},
getUserSettings: () => {
return JSON.parse(this.thisComponent.getAttribute('user-settings')) || {};
}
Expand Down
34 changes: 34 additions & 0 deletions container/test-app/helloWorldWC.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ export default class extends HTMLElement {
const setAnchorBtn = document.createElement('template');
setAnchorBtn.innerHTML = '<button id="setAnchor">setAnchor</button>';

const getCoreSearchParamsBtn = document.createElement('template');
getCoreSearchParamsBtn.innerHTML = '<button id="coreSearchParams">getCoreSearchParams</button>';

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

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

const empty = document.createElement('template');
empty.innerHTML = `<section><p>Test!</p><br/><br/></section>`;

Expand All @@ -38,6 +47,9 @@ 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(getCoreSearchParamsBtn.content.cloneNode(true));
this._shadowRoot.appendChild(getPathParamsBtn.content.cloneNode(true));
this._shadowRoot.appendChild(getClientPermissionsBtn.content.cloneNode(true));
this._shadowRoot.appendChild(getUserSettingsBtn.content.cloneNode(true));

for (let index = 0; index < 10; index++) {
Expand Down Expand Up @@ -92,6 +104,28 @@ export default class extends HTMLElement {
this.LuigiClient.setAnchor('#myAnchor');
}
});

this.$coreSearchParamsBtn = this._shadowRoot.querySelector('#coreSearchParams');
this.$coreSearchParamsBtn.addEventListener('click', () => {
if (this.LuigiClient) {
alert(JSON.stringify(this.LuigiClient.getCoreSearchParams()));
}
});

this.$getPathParamsBtn = this._shadowRoot.querySelector('#getPathParams');
this.$getPathParamsBtn.addEventListener('click', () => {
if (this.LuigiClient) {
alert(JSON.stringify(this.LuigiClient.getPathParams()));
}
});

this.$getClientPermissionsBtn = this._shadowRoot.querySelector('#getClientPermissions');
this.$getClientPermissionsBtn.addEventListener('click', () => {
if (this.LuigiClient) {
alert(JSON.stringify(this.LuigiClient.getClientPermissions()));
}
});

this.$getUserSettingsBtn = this._shadowRoot.querySelector('#getUserSettings');
this.$getUserSettingsBtn.addEventListener('click', () => {
if (this.LuigiClient) {
Expand Down
8 changes: 7 additions & 1 deletion container/test-app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,9 @@
id="dashboard"
context='{"label": "Dashboard"}'
node-params='{"Luigi":"rocks"}'
search-params='{"test":"searchParam1"}'
path-params='{"path":"param"}'
client-permissions='{"permission": "testPermission"}'
user-settings='{"language":"it", "date":""}'
></luigi-compound-container>
</div>
Expand Down Expand Up @@ -360,6 +363,9 @@
theme="sap_fiori_3"
active-feature-toggle-list='["ft1","ft2"]'
node-params='{"Luigi":"rocks from attri &<strong>asdf<strong> bute"}'
search-params='{"test":"searchParam1"}'
path-params='{"path":"param"}'
client-permissions='{"permission": "testPermission"}'
user-settings='{"language": "de", "date":""}'
></luigi-container>
</div>
Expand Down Expand Up @@ -413,7 +419,7 @@
{
viewUrl: layoutMFEDomain + '/panelHeader.js',
context: {
title: 'My Awesome Grid',
title: 'My Awesome Grid 000',
description: 'Really awesome'
},
layoutConfig: {
Expand Down
18 changes: 17 additions & 1 deletion container/typings/LuigiCompoundContainer.svelte.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,24 @@ export default class LuigiCompoundContainer extends HTMLElement {
* Manually triggers the micro frontend rendering process when using defer-init attribute
*/
init(): Function;

/**
* The search parameters to be passed to the web-component-based micro frontend.
*/
searchParams: any;

/**
* The path parameters to be passed to the web-component-based micro frontend.
*/
pathParams: any;

/**
* The clientPermissions to be passed to the web-component-based micro frontend.
*/
clientPermissions: any;

/**
* The user settings to be passed to the web-component-based micro frontend
*/
userSettings: UserSettings;
}
}
17 changes: 17 additions & 0 deletions container/typings/LuigiContainer.svelte.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
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 @@ -45,6 +47,21 @@ export default class LuigiContainer extends HTMLElement {
*/
nodeParams: NodeParams;

/**
* The search parameters to be passed to the web-component-based micro frontend.
*/
searchParams: any;

/**
* The path parameters to be passed to the web-component-based micro frontend.
*/
pathParams: any;

/**
* The clientPermissions to be passed to the web-component-based micro frontend.
*/
clientPermissions: any;

/**
* Updates the context of the microfrontend
* @param contextObj The context object to be updated
Expand Down
2 changes: 1 addition & 1 deletion core/src/Modal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
WebComponentService.renderWebComponent(
nodeObject.viewUrl,
document.querySelector(modalElementClassSelector),
{context: pathData.context},
{ context: pathData.context, clientPermissions: nodeObject.clientPermissions || {}, pathParams: pathData.pathParams || {} },
nodeObject,
undefined, true
);
Expand Down
20 changes: 14 additions & 6 deletions core/src/navigation/TabHeader.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,33 @@
export let node;
async function getCurrentContext() {
/**
* Retrieves the current node data by mapping it accordingly with the corresponding path
* Data returned consists in the current context, pathParams, clientPermissions, etc.
*/
async function getCurrentNodeData() {
const route = RoutingHelpers.mapPathToNode(Routing.getCurrentPath(), node);
const data = await Navigation.extractDataFromPath(route);
return data?.pathData?.context || node.context;
return {
context: data?.pathData?.context || node.context,
pathParams: data.pathData?.pathParams || {},
clientPermissions: data.nodeObject.clientPermissions || {}
};
}
onMount(() => {
document.querySelector('.lui-tab-header').innerHTML = '';
setTimeout(async ()=>{
// 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) {
if (node.webcomponent && node.tabNav.showAsTabHeader) {
const nodeData = await getCurrentNodeData();
const tabHeaderCnt = document.querySelector('.lui-tab-header');
WebComponentService.renderWebComponent(node.viewUrl, tabHeaderCnt, {context: await getCurrentContext()}, node);
WebComponentService.renderWebComponent(node.viewUrl, tabHeaderCnt, nodeData , node);
tabHeaderCnt.addEventListener('lui_ctx_update', async () => {
const wc = document.querySelector('.lui-tab-header [lui_web_component]');
if (wc) {
wc.context = await getCurrentContext();
wc.context = nodeData.context;
}
});
} else {
Expand Down
Loading

0 comments on commit 25e36cb

Please sign in to comment.