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 generic confirmation modal #389

Merged
merged 24 commits into from
Feb 5, 2019
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fe3eb92
First working implementation of confirmation modal
jesusreal Jan 28, 2019
240fcd9
Merge remote-tracking branch 'upstream/master' into generic-alerts-an…
jesusreal Jan 30, 2019
ac5e8b4
add e2e test and replace 'accept' with 'confirm' for modal button
jesusreal Jan 30, 2019
0998519
Fix couple of e2e tests and remove some e2e test code not needed
jesusreal Jan 30, 2019
7f5476e
refactor around promises for confirmation modal
jesusreal Jan 30, 2019
e9eca1c
text changes
jesusreal Jan 30, 2019
0c37cf7
Fix pathExists functionality broken in last refactoring commit
jesusreal Jan 30, 2019
748583b
Uncomment e2e tests
jesusreal Jan 30, 2019
9887bbd
Proper data on confirmationModal state object
jesusreal Jan 30, 2019
eabb55d
improve code for confirmation modal result being displayed
jesusreal Jan 31, 2019
88da0df
Improve API and docu for confirmation modal content
jesusreal Jan 31, 2019
40c9f82
Update luigi client readme file
jesusreal Jan 31, 2019
1ea9034
Update client/src/luigi-client.js
bszwarc Jan 31, 2019
761c132
Update client/src/luigi-client.js
bszwarc Jan 31, 2019
ecd0752
Update client/src/luigi-client.js
bszwarc Jan 31, 2019
080368c
Update client/src/luigi-client.js
bszwarc Jan 31, 2019
93a4931
Update luigi client readme file after feedback from technical writer
jesusreal Jan 31, 2019
ed3fc89
Very small improvement in docu
jesusreal Feb 1, 2019
5276171
Proper mechanism to use default values for modal
jesusreal Feb 4, 2019
ae183e8
small docu improvement
jesusreal Feb 4, 2019
d7deb89
Fix issue with ngIf preventing project detail view from working properly
jesusreal Feb 4, 2019
9f6f5cb
Center confirmation modal vertically by removing unnecessary styles
jesusreal Feb 4, 2019
cd6e607
Break long words in confirmation modal header and body
jesusreal Feb 4, 2019
89e4701
Confirmation modal now is viewport centered, not iframe centered
jesusreal Feb 4, 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
35 changes: 34 additions & 1 deletion client/src/luigi-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ var _onContextUpdatedFns = {};
var _onInitFns = {};
var authData = {};
var pathExistsPromises = {};

let promises = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need to make it an object inside an object? Why not just confirmationModalPromise object?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created it having in mind a future refactoring to include all promises in same object. Apart from this one, we have pathExists promise, and we will soon have generic alert promise. This solution scales better in my opinion

confirmationModal: {}
};
/**
* Creates a random Id
* @private
Expand Down Expand Up @@ -101,6 +103,13 @@ function luigiClientInit() {
pathExistsPromises[data.correlationId].resolveFn(data.pathExists);
delete pathExistsPromises[data.correlationId];
}

if ('luigi.ux.confirmationModal.hide' === e.data.msg) {
const data = e.data.data;
const promise = promises.confirmationModal;
data.confirmed ? promise.resolveFn() : promise.rejectFn();
delete promises.confirmationModal;
}
});

window.parent.postMessage({ msg: 'luigi.get-context' }, '*');
Expand Down Expand Up @@ -410,6 +419,30 @@ const LuigiClient = {
{ msg: 'luigi.set-page-dirty', dirty: isDirty },
'*'
);
},
/**
* Shows a confirmation modal.
* @param {Object} content the content of the confirmation modal
* @param {string} content.header text for modal header
jesusreal marked this conversation as resolved.
Show resolved Hide resolved
* @param {string} content.body text for modal body
jesusreal marked this conversation as resolved.
Show resolved Hide resolved
* @param {string} content.buttonConfirm text for modal confirm button
jesusreal marked this conversation as resolved.
Show resolved Hide resolved
* @param {string} content.buttonDismiss text for modal dismiss button
jesusreal marked this conversation as resolved.
Show resolved Hide resolved
* @returns {promise} A promise which is resolved when accepting confirmation modal and rejected when dismissing it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @returns {promise} A promise which is resolved when accepting confirmation modal and rejected when dismissing it.
* @returns a {promise} which is resolved when accepting the confirmation modal and rejected when dismissing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct regarding documentation. First comes the type of the returned value and the the additional info. I will just add the "the"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok :) I was just looking at the entire document, and under has Back I've found:
Returns boolean indicating if there is a preserved view you can return to. that's why I wanted to adjust the other part of the doc. Should we change this one as well, or is it a different case?

Also let's then write a promise - lower case, to make it consistent :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The piece of documentation you mentioned is:

       * @returns {boolean} indicating if there is a preserved view you can return to.

I will replace the line we are talking about with:

       * @returns {promise} which is resolved when accepting the confirmation modal and rejected when dismissing it.

This makes it consistent imho. Is that ok for you?

*/
showConfirmationModal: function showConfirmationModal(content) {
window.parent.postMessage(
{
msg: 'luigi.ux.confirmation-modal-show',
data: { content }
},
'*'
);
promises.confirmationModal = {};
promises.confirmationModal.promise = new Promise((resolve, reject) => {
promises.confirmationModal.resolveFn = resolve;
promises.confirmationModal.rejectFn = reject;
});
return promises.confirmationModal.promise;
}
};
}
Expand Down
14 changes: 12 additions & 2 deletions core/examples/luigi-sample-angular/e2e/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,23 @@ Cypress.Commands.add('login', (email, password) => {
cy.expectPathToBe('/overview');
});

Cypress.Commands.add('goToFeaturesPage', iframe => {
Cypress.Commands.add('goToUxManagerMethods', iframe => {
cy.wrap(iframe)
.contains('uxManager()')
.click();

cy.expectPathToBe('/projects/pr1');

cy.wrap(iframe).should('contain', 'LuigiClient uxManager methods:');
});

Cypress.Commands.add('goToLinkManagerMethods', iframe => {
cy.wrap(iframe)
.contains('linkManager()')
.click();

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

cy.wrap(iframe).should('contain', 'LuigiClient uxManager methods:');
cy.wrap(iframe).should('contain', 'LuigiClient linkManager methods:');
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe('Context switcher', () => {
beforeEach(() => {
cy.visit('http://localhost:4200');
cy.visit('/');
cy.login('tets@email.com', 'tets');
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe('Login Flow', () => {
beforeEach(() => {
cy.visit('http://localhost:4200');
cy.visit('/');
});

it('Login', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ describe('Luigi client features', () => {
it('linkManager features', () => {
cy.get('iframe').then($iframe => {
const $iframeBody = $iframe.contents().find('body');
cy.goToFeaturesPage($iframeBody);
cy.goToLinkManagerMethods($iframeBody);

//navigate using absolute path
cy.wrap($iframeBody)
.contains('absolute: to overview')
.click();
cy.expectPathToBe('/overview');

cy.goToFeaturesPage($iframeBody);
cy.goToLinkManagerMethods($iframeBody);

//navigate using relative path
cy.wrap($iframeBody)
Expand All @@ -28,7 +28,7 @@ describe('Luigi client features', () => {
cy.expectPathToBe('/projects/pr2/users/groups/stakeholders');

cy.goToOverviewPage();
cy.goToFeaturesPage($iframeBody);
cy.goToLinkManagerMethods($iframeBody);

//navigate using closest context
cy.wrap($iframeBody)
Expand All @@ -37,7 +37,7 @@ describe('Luigi client features', () => {
cy.expectPathToBe('/projects/pr2/users/groups/stakeholders');

cy.goToOverviewPage();
cy.goToFeaturesPage($iframeBody);
cy.goToLinkManagerMethods($iframeBody);

//navigate using context
cy.wrap($iframeBody)
Expand Down Expand Up @@ -96,7 +96,7 @@ describe('Luigi client features', () => {
});

// check if path exists
cy.goToFeaturesPage($iframeBody);
cy.goToLinkManagerMethods($iframeBody);
[
// non-existent relative path
{ path: 'projects/pr2/', successExpected: false },
Expand Down Expand Up @@ -137,7 +137,7 @@ describe('Luigi client features', () => {
beforeEach(() => {
cy.get('iframe').then($iframe => {
$iframeBody = $iframe.contents().find('body');
cy.goToFeaturesPage($iframeBody);
cy.goToLinkManagerMethods($iframeBody);
});
});
it('navigate to a partly wrong link', () => {
Expand Down Expand Up @@ -184,7 +184,7 @@ describe('Luigi client features', () => {
cy.wait(500);
cy.get('iframe').then($iframe => {
const $iframeBody = $iframe.contents().find('body');
cy.goToFeaturesPage($iframeBody);
cy.goToUxManagerMethods($iframeBody);
cy.wrap($iframeBody).should(
'not.contain',
'Lorem tipsum dolor sit amet'
Expand All @@ -195,13 +195,15 @@ describe('Luigi client features', () => {
cy.wrap($iframeBody)
.contains('Add backdrop')
.click();

cy.wrap($iframeBody).should('contain', 'Lorem tipsum dolor sit amet');
cy.get('.fd-ui__overlay').should('exist');

//close modal
cy.wrap($iframeBody)
.find('.fd-modal__footer')
.contains('Confirm')
.click();

cy.wrap($iframeBody).should(
'not.contain',
'Lorem tipsum dolor sit amet'
Expand All @@ -210,6 +212,38 @@ describe('Luigi client features', () => {
});
});

it('confirmation modal', () => {
cy.get('iframe').then($iframe => {
const $iframeBody = $iframe.contents().find('body');

cy.goToUxManagerMethods($iframeBody);

cy.get('[data-cy=luigi-confirmation-modal]').should('not.be.visible');

cy.wrap($iframeBody)
.find('[data-cy=show-luigi-confirmation-modal]')
.click();
cy.get('[data-cy=luigi-confirmation-modal]').should('be.visible');

cy.get('[data-cy=luigi-modal-dismiss]').click();
cy.get('[data-cy=luigi-confirmation-modal]').should('not.be.visible');
cy.wrap($iframeBody)
.find('[data-cy=luigi-confirmation-modal-result]')
.contains('Confirmation modal has been dismissed');

cy.wrap($iframeBody)
.find('[data-cy=show-luigi-confirmation-modal]')
.click();
cy.get('[data-cy=luigi-confirmation-modal]').should('be.visible');

cy.get('[data-cy=luigi-modal-confirm]').click();
cy.get('[data-cy=luigi-confirmation-modal]').should('not.be.visible');
cy.wrap($iframeBody)
.find('[data-cy=luigi-confirmation-modal-result]')
.contains('Confirmation modal has been confirmed');
});
});

it('loading indicator', () => {
cy.get('.fd-shellbar')
.contains('External Page')
Expand All @@ -234,6 +268,7 @@ describe('Luigi client features', () => {
cy.get('.spinnerContainer .fd-spinner').should('not.exist');
});
});

it("Unsaved changes - shouldn't proceed when 'No' was pressed in modal", () => {
cy.get('iframe').then($iframe => {
const $iframeBody = $iframe.contents().find('body');
Expand All @@ -246,17 +281,18 @@ describe('Luigi client features', () => {
.contains('Projects')
.click();

cy.get('[data-cy=confirmation-modal]').should('be.visible');
cy.get('[data-cy=luigi-confirmation-modal]').should('be.visible');

cy.expectPathToBe('/overview'); //the location is unchanged

cy.get('[data-cy=modal-no]').click();
cy.get('[data-cy=luigi-modal-dismiss]').click();

cy.get('[data-cy=confirmation-modal]').should('not.be.visible');
cy.get('[data-cy=luigi-confirmation-modal]').should('not.be.visible');

cy.expectPathToBe('/overview'); //the location is still unchanged after "No" clicked
});
});

it("Unsaved changes - should proceed when 'Yes' was pressed in modal", () => {
cy.get('iframe').then($iframe => {
const $iframeBody = $iframe.contents().find('body');
Expand All @@ -269,13 +305,13 @@ describe('Luigi client features', () => {
.contains('Projects')
.click();

cy.get('[data-cy=confirmation-modal]').should('be.visible');
cy.get('[data-cy=luigi-confirmation-modal]').should('be.visible');

cy.expectPathToBe('/overview'); //the location is unchanged

cy.get('[data-cy=modal-yes]').click();
cy.get('[data-cy=luigi-modal-confirm]').click();

cy.get('[data-cy=confirmation-modal]').should('not.be.visible');
cy.get('[data-cy=luigi-confirmation-modal]').should('not.be.visible');

cy.expectPathToBe('/projects'); //the location is changed after "Yes" clicked
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe('ProductSwitcher', () => {
beforeEach(() => {
cy.visit('http://localhost:4200');
cy.visit('/');
cy.login('tets@email.com', 'tets');
});

Expand All @@ -15,8 +15,6 @@ describe('ProductSwitcher', () => {
.contains('Project 1')
.click();

cy.location().should(loc => {
expect(loc.pathname).to.eq('/projects/pr1');
});
cy.expectPathToBe('/projects/pr1');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ export class OverviewComponent {
public luigiClient: LuigiClient = LuigiClient;
public luigiClientLinks: any[] = [
{
link: '/projects/pr2',
link: '/projects/pr1',
text: 'uxManager()',
description: 'backdrop methods'
},
{
link: '/projects/pr1',
text: 'uxManager()',
description: 'confirmation modal methods'
},
{
link: '/ext',
text: 'uxManager()',
Expand Down
Loading