Skip to content

Commit e751ffb

Browse files
authored
Adds possibility to change route/viewUrl in iframe based mfe (#4031)
1 parent 216fd4b commit e751ffb

File tree

6 files changed

+147
-14
lines changed

6 files changed

+147
-14
lines changed

container/cypress/e2e/test-app/iframe/iframe-container.cy.js

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,13 @@ describe('Iframe Container Test', () => {
4040
cy.get(containerSelector)
4141
.shadow()
4242
.get('iframe')
43-
.then(iframe => {
43+
.then((iframe) => {
4444
const $body = iframe.contents().find('body');
4545
cy.wrap($body)
4646
.contains('test showAlert')
4747
.click()
4848
.then(() => {
49-
cy.wrap(stub).should(
50-
'have.been.calledWith',
51-
'show-alert-request message received: {\"isTrusted\":true}'
52-
);
49+
cy.wrap(stub).should('have.been.calledWith', 'show-alert-request message received: {"isTrusted":true}');
5350
});
5451
});
5552
});
@@ -60,17 +57,14 @@ describe('Iframe Container Test', () => {
6057
cy.get(containerSelector)
6158
.shadow()
6259
.get('iframe')
63-
.then(iframe => {
60+
.then((iframe) => {
6461
const $body = iframe.contents().find('body');
6562
cy.wrap($body)
6663
.contains('test goBack')
6764
.click()
6865
.then(() => {
6966
console.log(cy.wrap(stub));
70-
cy.wrap(stub).should(
71-
'have.been.calledWith',
72-
'navigate-back-request'
73-
);
67+
cy.wrap(stub).should('have.been.calledWith', 'navigate-back-request');
7468
});
7569
});
7670
});
@@ -131,6 +125,31 @@ describe('Iframe Container Test', () => {
131125
});
132126
});
133127

128+
it('update viewUrl', () => {
129+
cy.on('window:alert', stub);
130+
131+
cy.get('#update-view-url')
132+
.click()
133+
.then(() => {
134+
cy.get(containerSelector)
135+
.shadow()
136+
.get('iframe')
137+
.then((iframe) => {
138+
const $body = iframe.contents().find('body');
139+
140+
cy.wrap($body)
141+
.contains('test history state')
142+
.click()
143+
.then(() => {
144+
cy.wrap(stub).should(
145+
'have.been.calledWith',
146+
'Custom message received: {"id":"my.historyMessage","_metaData":{},"data":{"state":{"luigiInduced":true}}}'
147+
);
148+
});
149+
});
150+
});
151+
});
152+
134153
it('set auth token', () => {
135154
cy.on('window:alert', stub);
136155

container/src/LuigiContainer.svelte

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
return class extends customElementConstructor {
3737
sendCustomMessage = notInitFn('sendCustomMessage');
3838
updateContext = notInitFn('updateContext');
39+
updateViewUrl = notInitFn('updateViewUrl');
3940
closeAlert = notInitFn('closeAlert');
4041
attributeChangedCallback(name, oldValue, newValue) {
4142
if (this.containerInitialized) {
@@ -151,6 +152,13 @@
151152
webcomponentService.thisComponent = thisComponent;
152153
153154
const ctx = GenericHelperFunctions.resolveContext(context);
155+
156+
thisComponent.updateViewUrl = (viewUrl: string, internal?: object) => {
157+
if (viewUrl?.length) {
158+
ContainerAPI.updateViewUrl(viewUrl, GenericHelperFunctions.resolveContext(context), internal, iframeHandle);
159+
}
160+
};
161+
154162
if (webcomponent && webcomponent != 'false') {
155163
if (!thisComponent.getNoShadow()) {
156164
mainComponent.innerHTML = '';

container/src/api/container-api.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,31 @@ export class ContainerAPIFunctions {
2727
}
2828
};
2929

30+
/**
31+
* Updates route of the microfrontend by sending a message to the iframe that sets new view URL
32+
* @param viewUrl new view URL
33+
* @param context context data
34+
* @param internal internal luigi legacy data
35+
* @param iframeHandle a reference to the iframe that is needed to send a message to it internally
36+
*/
37+
updateViewUrl = (viewUrl: string, context: object, internal?: object, iframeHandle?: IframeHandle) => {
38+
if (iframeHandle) {
39+
const internalParameter = internal || {};
40+
containerService.sendCustomMessageToIframe(
41+
iframeHandle,
42+
{
43+
context,
44+
internal: internalParameter,
45+
withoutSync: false,
46+
viewUrl
47+
},
48+
LuigiInternalMessageID.SEND_CONTEXT_OBJECT
49+
);
50+
} else {
51+
console.warn('Attempting to update route on inexisting iframe');
52+
}
53+
};
54+
3055
/**
3156
* Updates the auth data of the microfrontend by sending a message to the iframe that sets the authData of the microfrontend
3257
* @param iframeHandle a reference to the iframe that is needed to send a message to it internally

container/test-app/iframe/iframeContainer.html

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ <h3>
1111
</h3>
1212
<button id="btn-1">Send Custom Message to iFrame Container</button>
1313
<button id="update-ctx">Update Ctx</button>
14+
<button id="update-view-url" type="button">Update viewUrl</button>
1415
<button id="update-token">Update Access Token</button>
1516

1617
<div style="border: solid 1px blue; height: 400px">
@@ -44,17 +45,19 @@ <h3>
4445
});
4546

4647
luigiContainer.addEventListener(Events.UPDATE_TOP_NAVIGATION_REQUEST, (event) => {
47-
alert("UPDATE_TOP_NAVIGATION_REQUEST event received");
48+
alert('UPDATE_TOP_NAVIGATION_REQUEST event received');
4849
});
4950

5051
luigiContainer.addEventListener(Events.CUSTOM_MESSAGE, (event) => {
5152
alert('Custom message received: ' + JSON.stringify(event.detail));
5253
});
5354

5455
var alertSettings;
56+
5557
// Some glue code
5658
const sendCustomMessageBtn = document.getElementById('btn-1');
5759
const updateContextButton = document.getElementById('update-ctx');
60+
const updateViewUrlButton = document.getElementById('update-view-url');
5861
const updateAccessTokenButton = document.getElementById('update-token');
5962

6063
// for testing send Custom message functionality
@@ -66,6 +69,10 @@ <h3>
6669
luigiContainer.updateContext({ myContext: 'some context data' });
6770
});
6871

72+
updateViewUrlButton.addEventListener('click', () => {
73+
luigiContainer.updateViewUrl('/');
74+
});
75+
6976
updateAccessTokenButton.addEventListener('click', () => {
7077
luigiContainer.setAttribute('auth-data', '{"accessToken": "updated token"}');
7178
});

container/test-app/iframe/microfrontend.html

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ <h1 id="title">Multi purpose demo page</h1>
4444
<button onclick="testShowConfirmationModal()">test show confirmation modal</button>
4545
<button onclick="testShowLoadingIndicator()">test show loading indicator</button>
4646
<button onclick="testHideLoadingIndicator()">test hide loading indicator</button>
47+
<button onclick="testGetHistoryState()">test history state</button>
4748
<button onclick="testGetContext()">Get Context</button>
4849

4950
<button onclick="testSetCurrentLocale()">test set current locale</button>
@@ -91,11 +92,11 @@ <h1 id="title">Multi purpose demo page</h1>
9192
});
9293
}
9394

94-
function goBack(){
95+
function goBack() {
9596
LuigiClient.linkManager().goBack();
9697
}
9798

98-
function updateTopNavigation(){
99+
function updateTopNavigation() {
99100
LuigiClient.LuigiClient.linkManager().updateTopNavigation();
100101
}
101102

@@ -134,6 +135,13 @@ <h1 id="title">Multi purpose demo page</h1>
134135
LuigiClient.uxManager().hideLoadingIndicator();
135136
}
136137

138+
function testGetHistoryState() {
139+
LuigiClient.sendCustomMessage({
140+
id: 'my.historyMessage',
141+
...{ state: window.history.state },
142+
});
143+
}
144+
137145
function testGetContext() {
138146
LuigiClient.sendCustomMessage({
139147
id: 'my.contextMessage',

container/test/api/container-api.spec.ts

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,72 @@ describe('Container Service', () => {
6767
});
6868
});
6969

70+
describe('updateViewUrl', () => {
71+
const containerAPI = new ContainerAPIFunctions();
72+
73+
it('iframeHandle exists, WITH internalParam', () => {
74+
// mock and spy
75+
const viewUrl = '/';
76+
const context = {};
77+
const internal = { empty: false };
78+
const iframeHandle = {
79+
data: 'test'
80+
} as unknown as IframeHandle;
81+
containerService.sendCustomMessageToIframe = jest.fn();
82+
const spy = jest.spyOn(containerService, 'sendCustomMessageToIframe');
83+
84+
// act
85+
containerAPI.updateViewUrl(viewUrl, context, internal, iframeHandle);
86+
87+
// assert
88+
expect(spy).toHaveBeenCalledWith(
89+
iframeHandle,
90+
{ viewUrl, context, internal, withoutSync: false },
91+
LuigiInternalMessageID.SEND_CONTEXT_OBJECT
92+
);
93+
});
94+
95+
it('iframeHandle exists, UNDEFINED internalParam ', () => {
96+
// mock and spy
97+
const viewUrl = '/';
98+
const context = {};
99+
const internal = undefined;
100+
const iframeHandle = {
101+
data: 'test'
102+
} as unknown as IframeHandle;
103+
containerService.sendCustomMessageToIframe = jest.fn();
104+
const spy = jest.spyOn(containerService, 'sendCustomMessageToIframe');
105+
106+
// act
107+
containerAPI.updateViewUrl(viewUrl, context, internal, iframeHandle);
108+
109+
// assert
110+
expect(spy).toHaveBeenCalledWith(
111+
iframeHandle,
112+
{ viewUrl, context, internal: {}, withoutSync: false },
113+
LuigiInternalMessageID.SEND_CONTEXT_OBJECT
114+
);
115+
});
116+
117+
it('iframeHandle NOT exists', () => {
118+
// mock and spy
119+
const viewUrl = '/';
120+
const context = {};
121+
const internal = {};
122+
const iframeHandle = undefined;
123+
containerService.sendCustomMessageToIframe = jest.fn();
124+
const sendCustomMSGSpy = jest.spyOn(containerService, 'sendCustomMessageToIframe');
125+
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(jest.fn());
126+
127+
// act
128+
containerAPI.updateViewUrl(viewUrl, context, internal, iframeHandle);
129+
130+
// assert
131+
expect(sendCustomMSGSpy).not.toHaveBeenCalled();
132+
expect(consoleWarnSpy).toHaveBeenCalledWith('Attempting to update route on inexisting iframe');
133+
});
134+
});
135+
70136
describe('updateAuthData', () => {
71137
const containerAPI = new ContainerAPIFunctions();
72138

@@ -88,7 +154,7 @@ describe('Container Service', () => {
88154

89155
it('iframeHandle undefined, authData exists', () => {
90156
// mock and spy
91-
const authData = undefined;
157+
const authData = { someData: 'mytoken' };
92158
const iframeHandle = undefined;
93159
containerService.sendCustomMessageToIframe = jest.fn();
94160
const sendCustomMSGSpy = jest.spyOn(containerService, 'sendCustomMessageToIframe');

0 commit comments

Comments
 (0)