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 goBack support for normal navigation #595

Merged
merged 22 commits into from
Jun 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c172f62
Merge remote-tracking branch 'upstream/master'
maxmarkus May 15, 2019
c09690a
Merge remote-tracking branch 'upstream/master'
maxmarkus May 16, 2019
76fa8db
Merge remote-tracking branch 'upstream/master'
maxmarkus May 20, 2019
9c86254
Merge remote-tracking branch 'upstream/master'
maxmarkus May 23, 2019
640e287
Merge remote-tracking branch 'upstream/master'
maxmarkus May 23, 2019
2397ede
Merge remote-tracking branch 'upstream/master'
maxmarkus Jun 3, 2019
c052644
Merge remote-tracking branch 'upstream/master'
maxmarkus Jun 13, 2019
37b1b60
Merge remote-tracking branch 'upstream/master'
maxmarkus Jun 18, 2019
2b4d1b5
send doHistoryBack
maxmarkus Jun 18, 2019
f63bf45
history listener not required in client
maxmarkus Jun 18, 2019
2d47924
e2e tets for goback
maxmarkus Jun 19, 2019
895b84a
documented that goBackContext is only available when using preserved …
maxmarkus Jun 19, 2019
1f147cb
added warning when using goBackContext if it not available
maxmarkus Jun 19, 2019
20ed7dc
Merge branch 'master' into 265-goback-internal-routes
pekura Jun 24, 2019
bcfd55c
Merge branch 'master' into 265-goback-internal-routes
maxmarkus Jun 24, 2019
35dcac4
Merge branch '265-goback-internal-routes' of github.com:maxmarkus/lui…
maxmarkus Jun 24, 2019
5ef5da0
some iframe tets to make coverage happy
maxmarkus Jun 24, 2019
4111db3
Merge branch 'master' into 265-goback-internal-routes
maxmarkus Jun 26, 2019
a2593c0
Merge branch '265-goback-internal-routes' of github.com:maxmarkus/lui…
maxmarkus Jun 26, 2019
f6e4255
built client docu
maxmarkus Jun 26, 2019
d471ec9
docu updates thanks to basia
maxmarkus Jun 27, 2019
9945f6a
Apply suggestions from code review
maxmarkus Jun 27, 2019
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
4 changes: 2 additions & 2 deletions client/luigi-client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ export declare interface LinkManager {
fromContext: (navigationContext: string) => this;

/**
* Discards the active view and navigates back to the last visited view (preserved view), if a preserved view was set before.
* @param {any} goBackValue data that is passed in the `goBackContext` field to the last visited view
* Discards the active view and navigates back to the last visited view. Works with preserved views, and also acts as the substitute of the browser **back** button. **goBackContext** is only available when using preserved views.
* @param {any} goBackValue data that is passed in the **goBackContext** field to the last visited view when using preserved views.
* @example
* LuigiClient.linkManager().goBack({ foo: 'bar' });
* LuigiClient.linkManager().goBack(true);
Expand Down
20 changes: 9 additions & 11 deletions client/src/linkManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,22 +206,20 @@ export class linkManager extends LuigiClientBase {
}

/**
* Discards the active view and navigates back to the last visited view (preserved view), if a preserved view was set before.
* Discards the active view and navigates back to the last visited view. Works with preserved views, and also acts as the substitute of the browser **back** button. **goBackContext** is only available when using preserved views.
* @memberof linkManager
* @param {any} goBackValue data that is passed in the `goBackContext` field to the last visited view
* @param {any} goBackValue data that is passed in the **goBackContext** field to the last visited view when using preserved views.
* @example
* LuigiClient.linkManager().goBack({ foo: 'bar' });
* LuigiClient.linkManager().goBack(true);
*/
goBack(goBackValue) {
if (this.hasBack()) {
window.parent.postMessage(
{
msg: 'luigi.navigation.back',
goBackContext: goBackValue && JSON.stringify(goBackValue)
},
'*'
);
}
window.parent.postMessage(
{
msg: 'luigi.navigation.back',
goBackContext: goBackValue && JSON.stringify(goBackValue)
},
'*'
);
}
}
88 changes: 88 additions & 0 deletions core/examples/luigi-sample-angular/e2e/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,91 @@ Cypress.Commands.add('expectSearchToBe', (searchString, a) => {
}
});
});

/**
* iframe
* Basically what it is doing is checking if the iframe has loaded, if
* the iframe has loaded it will do $iframe.contents().find("body");.
* If it has not loaded it will hook that same code into the load event
* so it will run as soon as the iframe loads.
*
usage:
cy.get('iframe').iframe().then(modal => {
cy.wrap(modal)
.contains('Go back')
.click();
});
*/

Cypress.Commands.add('iframe', { prevSubject: 'element' }, $iframe => {
Cypress.log({
name: 'iframe',
consoleProps() {
return {
iframe: $iframe
};
}
});
return new Cypress.Promise(resolve => {
onIframeReady(
$iframe,
() => {
resolve($iframe.contents().find('body'));
},
() => {
$iframe.on('load', () => {
resolve($iframe.contents().find('body'));
});
}
);
});
});

function onIframeReady($iframe, successFn, errorFn) {
try {
const iCon = $iframe.first()[0].contentWindow,
bl = 'about:blank',
compl = 'complete';
const callCallback = () => {
try {
const $con = $iframe.contents();
if ($con.length === 0) {
// https://git.io/vV8yU
throw new Error('iframe inaccessible');
}
successFn($con);
} catch (e) {
// accessing contents failed
errorFn();
}
};
const observeOnload = () => {
$iframe.on('load.jqueryMark', () => {
try {
const src = $iframe.attr('src').trim(),
href = iCon.location.href;
if (href !== bl || src === bl || src === '') {
$iframe.off('load.jqueryMark');
callCallback();
}
} catch (e) {
errorFn();
}
});
};
if (iCon.document.readyState === compl) {
const src = $iframe.attr('src').trim(),
href = iCon.location.href;
if (href === bl && src !== bl && src !== '') {
observeOnload();
} else {
callCallback();
}
} else {
observeOnload();
}
} catch (e) {
// accessing contentWindow failed
errorFn();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@ describe('Luigi client features', () => {
.find(checkPathSelector + ' .check-path-result')
.contains(msgExpected);
});

// go back
cy.goToOverviewPage();
cy.goToLinkManagerMethods($iframeBody);
cy.expectPathToBe('/projects/pr2');
cy.wrap($iframeBody)
.contains('go back: single iframe')
.click();
cy.expectPathToBe('/overview');
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,71 +1,83 @@
describe('Modal Microfrontend', () => {
let iframeBody;
beforeEach(() => {
cy.visit('/');
cy.login('tets', 'tets');

let iframeBody;
//wait for the iFrame to be loaded
cy.get('iframe', { timeout: 1000 }).then(ifr => {
iframeBody = ifr.contents().find('body');
});
});

describe('Behaviour when used in LinkManager', () => {
beforeEach(() => {
cy.visit('/');
cy.login('tets', 'tets');
cy.goToLinkManagerMethods(iframeBody);
});

//wait for the iFrame to be loaded
cy.get('iframe', { timeout: 1000 }).then(ifr => {
iframeBody = ifr.contents().find('body');
});

it(`doesn't change URL when opened`, () => {
cy.expectPathToBe('/projects/pr2');

cy.wrap(iframeBody)
.contains('rendered in a modal')
.click();

cy.expectPathToBe('/projects/pr2');
});

describe('Behaviour when used in LinkManager', () => {
beforeEach(() => {
cy.goToLinkManagerMethods(iframeBody);
});

it(`doesn't change URL when opened`, () => {
cy.expectPathToBe('/projects/pr2');

cy.wrap(iframeBody)
.contains('rendered in a modal')
.click();

cy.expectPathToBe('/projects/pr2');
});

it(`can be closed by Close Button`, () => {

cy.wrap(iframeBody)
.contains('rendered in a modal')
.click();

cy.wrap(iframeBody)
.get('[data-e2e=modal-mf]')
.should('be.visible');

cy.wrap(iframeBody)
.get('[data-e2e=modal-mf] [aria-label=close]')
.click();

cy.wrap(iframeBody)
.get('[data-e2e=modal-mf]')
.should('not.be.visible');
});
it(`can be closed by Close Button`, () => {
cy.wrap(iframeBody)
.contains('rendered in a modal')
.click();

it(`sets proper URL inside iframe`, () => {
cy.wrap(iframeBody)
.contains('rendered in a modal')
.click();
cy.wrap(iframeBody)
.get('[data-e2e=modal-mf]')
.should('be.visible');

cy.get('[data-e2e=modal-mf] iframe').then(ifr => {
expect(ifr.attr('src')).to.equal('/sampleapp.html#/settings');
});
});
cy.wrap(iframeBody)
.get('[data-e2e=modal-mf] [aria-label=close]')
.click();

it(`has the size set directly`, () => {
cy.wrap(iframeBody)
.contains('rendered in a modal')
.click();
cy.wrap(iframeBody)
.get('[data-e2e=modal-mf]')
.should('not.be.visible');
});

it(`sets proper URL inside iframe`, () => {
cy.wrap(iframeBody)
.contains('rendered in a modal')
.click();

cy.get('[data-e2e=modal-mf] iframe').then(ifr => {
expect(ifr.attr('src')).to.equal('/sampleapp.html#/settings');
});
});

it(`has the size set directly`, () => {
cy.wrap(iframeBody)
.contains('rendered in a modal')
.click();

cy.get('[data-e2e=modal-mf]').then(modal => {
expect(modal.attr('style')).to.contain('width:');
expect(modal.attr('style')).to.contain('height:');
});
cy.get('[data-e2e=modal-mf]').then(modal => {
expect(modal.attr('style')).to.contain('width:');
expect(modal.attr('style')).to.contain('height:');
});
});

it(`go back closes the modal`, () => {
cy.wrap(iframeBody)
.contains('rendered in a modal')
.click();

cy.get('[data-e2e=modal-mf] iframe')
.iframe()
.then(modal => {
cy.wrap(modal)
.contains('Go back')
.click();
});

cy.expectPathToBe('/projects/pr2');
});
});
});

Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,12 @@ <h1 class="fd-section__title">LuigiClient linkManager methods</h1>
>
</app-code-snippet>
</li>
<li class="fd-list-group__item">
<a href="javascript:void(0)" (click)="linkManager().goBack()">
go back: single iframe, standard history back</a
>
<app-code-snippet data="linkManager().goBack()"> </app-code-snippet>
</li>
</ul>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
"
>Go to /environments/env1 with preserveView</a
>
<br /><br />
<a href="javascript:void(0)" (click)="linkManager().goBack()"
>Go back: use default history method</a
>
</div>
</section>

Expand Down
12 changes: 8 additions & 4 deletions core/src/App.html
Original file line number Diff line number Diff line change
Expand Up @@ -410,15 +410,19 @@
e.data.goBackContext && JSON.parse(e.data.goBackContext)
});
// TODO: check if getNavigationPath or history pop to update hash / path
const path = this.handleNavigation(
this.handleNavigation(
{ params: { link: previousActiveIframeData.path } },
config
);
});
} else {
console.error(
'goBack() not possible, no preserved views found.'
);
if (e.data.goBackContext) {
console.warn(
`Warning: goBack() does not support goBackContext value. This is available only when using preserved views feature. Documentation: https://github.com/SAP/luigi/blob/master/docs/luigi-client-api.md#navigate`
);
}
// TODO: does not work with default child node behavior, fixed by #216
window.history.back();
}
}
}
Expand Down
31 changes: 31 additions & 0 deletions core/test/services/iframe.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,43 @@ describe('Iframe', () => {
});
});

it('getIframeContainer', () => {
sinon
.stub(document, 'querySelectorAll')
.onFirstCall()
.returns([])
.onSecondCall()
.returns(['firstIframe', 'secondIframe']);

// first
assert.equal(Iframe.getIframeContainer(), undefined, 'no iframe found');
// second
assert.equal(
Iframe.getIframeContainer(),
'firstIframe',
'returns first iframe'
);
});

it('removeInactiveIframes', () => {
node.removeChild = sinon.spy();
Iframe.removeInactiveIframes(node);
assert.equal(node.removeChild.callCount, 1);
});

it('removeIframe', () => {
const testNode = {
children: ['one', 'two', 'three', 'four'],
removeChild: sinon.spy()
};
Iframe.removeIframe('two', testNode);
assert.equal(testNode.removeChild.callCount, 1, 'removeChild call count');
assert(
testNode.removeChild.calledWith('two'),
'correct node child was deleted'
);
});

describe('create new iframe with different viewgroup and dont delete the previous one (cache)', () => {
it('navigate', () => {
sinon.stub(document, 'querySelectorAll').callsFake(() => [
Expand Down
4 changes: 2 additions & 2 deletions docs/luigi-client-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,11 @@ Returns **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/

### goBack

Discards the active view and navigates back to the last visited view (preserved view), if a preserved view was set before.
Discards the active view and navigates back to the last visited view. Works with preserved views, and also acts as the substitute of the browser **back** button. **goBackContext** is only available when using preserved views.

#### Parameters

- `goBackValue` **any** data that is passed in the `goBackContext` field to the last visited view
- `goBackValue` **any** data that is passed in the **goBackContext** field to the last visited view when using preserved views.

#### Examples

Expand Down