Skip to content

Commit b55b870

Browse files
committed
Add process modal
1 parent e8f63a3 commit b55b870

File tree

16 files changed

+261
-8
lines changed

16 files changed

+261
-8
lines changed

Diff for: addon/components/modals-container/process.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import Base from './base';
2+
import layout from '../../templates/components/modals-container/process';
3+
import {get} from '@ember/object';
4+
5+
/**
6+
* Here `process` means function thar return Promise
7+
*
8+
* @class Process
9+
* @namespace Components
10+
* @extends Components.BaseModal
11+
*/
12+
export default Base.extend({
13+
layout,
14+
15+
didInsertElement() {
16+
const process = get(this, 'options.process');
17+
if (process) {
18+
process()
19+
.then(v => this.send('confirm', v))
20+
.catch(e => this.send('decline', e));
21+
}
22+
}
23+
});

Diff for: addon/components/modals-container/progress.js

-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import layout from '../../templates/components/modals-container/progress';
22
import Base from './base';
33
import {computed, get, set} from '@ember/object';
44
import {alias} from '@ember/object/computed';
5-
import {inject as service} from '@ember/service';
65
import {later} from '@ember/runloop';
76
import {A} from '@ember/array';
87
import {run} from '@ember/runloop';
@@ -18,8 +17,6 @@ export default Base.extend({
1817

1918
layout,
2019

21-
modalsManager: service(),
22-
2320
/**
2421
* Number of fulfilled promises
2522
*

Diff for: addon/services/modals-manager.js

+88-3
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,44 @@ import {defer} from 'rsvp';
276276
*
277277
* **IMPORTANT** Here `options.promises` is a list of _FUNCTIONS_ that returns Promises!
278278
*
279+
* ### `process`
280+
*
281+
* This modal is used to show a "placeholder" while some process is running. This modal doesn't have any controls like confirm/decline-buttons in the footer or "×" in the header and can't be closed by pressing Esc or clicking somewhere outside a modal. Modal will be confirmed and self-closed after provided promise (`process`) is fulfilled or it will be declined (and self-closed) if it becomes rejected.
282+
*
283+
* ```js
284+
* import Controller from '@ember/controller';
285+
* import {inject as service} from '@ember/service';
286+
* import {get} from '@ember/object';
287+
* import {Promise} from 'rsvp';
288+
*
289+
* export default Controller.extend({
290+
* modalsManager: service(),
291+
*
292+
* actions: {
293+
* showProcessModal() {
294+
* get(this, 'modalsManager')
295+
* .process({
296+
* body: 'Some text goes here',
297+
* iconClass: 'text-center fa fa-spinner fa-spin fa-3x fa-fw',
298+
* title: '',
299+
* // this is required
300+
* process: () => new Promise(resolve => setTimeout(resolve(1), 100))
301+
* })
302+
* .then(result => {
303+
* // called after `process` is resolved
304+
* // here "result" is value of fulfilled Promise
305+
* })
306+
* .catch(error => {
307+
* // called after `process` is rejected
308+
* // here "error" is a reason why last promise was rejected
309+
* });
310+
* }
311+
* }
312+
* });
313+
* ```
314+
*
315+
* **IMPORTANT** Here `options.process` is a _FUNCTION_ that return Promise!
316+
*
279317
* ## Go Pro
280318
*
281319
* ### Custom components for `title`, `body` and `footer`
@@ -622,7 +660,7 @@ import {defer} from 'rsvp';
622660
*
623661
* #### Title Component
624662
*
625-
* It takes a single parameter options. Its value is an object passed to the modalsManager.progress.
663+
* It takes a single parameter `options`. Its value is an object passed to the `modalsManager.progress`.
626664
*
627665
* ```hbs
628666
* <h4 class="modal-title"><i class="glyphicon glyphicon-info-sign"></i> Custom Progress Title Component</h4>
@@ -653,6 +691,45 @@ import {defer} from 'rsvp';
653691
* <p>Custom Progress Footer Component</p>
654692
* ```
655693
*
694+
* ### `process`
695+
*
696+
* ```js
697+
* import Controller from '@ember/controller';
698+
* import {inject as service} from '@ember/service';
699+
* import {get} from '@ember/object';
700+
* import {Promise} from 'rsvp';
701+
*
702+
* export default Controller.extend({
703+
* modalsManager: service(),
704+
*
705+
* actions: {
706+
* showProcessModal() {
707+
* get(this, 'modalsManager')
708+
* .process({
709+
* bodyComponent: 'custom-process-body',
710+
* headerComponent: 'custom-process-footer',
711+
* footerComponent: 'custom-process-header',
712+
* process: () => new Promise(resolve => setTimeout(resolve(1), 100))
713+
* })
714+
* .then(result => {})
715+
* .catch(error => {});
716+
* }
717+
* }
718+
* });
719+
* ```
720+
*
721+
* #### Title Component
722+
*
723+
* It takes a single parameter `options`. Its value is an object passed to the `modalsManager.process`.
724+
*
725+
* #### Body Component
726+
*
727+
* It takes a single parameter `options`. Its value is an object passed to the `modalsManager.process`.
728+
*
729+
* #### Footer Component
730+
*
731+
* It takes a single parameter `options`. Its value is an object passed to the `modalsManager.process`.
732+
*
656733
* @module EmberBootstrapModalsManager
657734
* @main EmberBootstrapModalsManager
658735
*/
@@ -794,8 +871,6 @@ export default Service.extend({
794871
/**
795872
* Shows `progress`-modal. This modal doesn't have any controls and is auto-closed when progress is completed
796873
*
797-
* Alert-modal will be opened if some error-appears
798-
*
799874
* @method progress
800875
* @param {object} options
801876
* @returns {RSVP.Promise}
@@ -805,6 +880,16 @@ export default Service.extend({
805880
return this.show('modals-container/progress', options);
806881
},
807882

883+
/**
884+
* @method process
885+
* @param {object} options
886+
* @returns {RSVP.Promise}
887+
*/
888+
process(options) {
889+
assert('`options.process` must be defined', options && options.process);
890+
return this.show('modals-container/process', options);
891+
},
892+
808893
/**
809894
* @method onConfirmClick
810895
* @private
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{{#bs-modal open=modalIsOpened keyboard=false backdropClose=false onSubmit=(action "confirm") onHide=(action "decline") as |modal|}}
2+
{{#modal.header closeButton=false}}
3+
{{#if options.titleComponent}}
4+
{{component
5+
options.titleComponent
6+
options=options
7+
}}
8+
{{else}}
9+
{{#bs-modal/header/title}}{{options.title}}{{/bs-modal/header/title}}
10+
{{/if}}
11+
{{/modal.header}}
12+
{{#modal.body}}
13+
{{#if options.bodyComponent}}
14+
{{component
15+
options.bodyComponent
16+
options=options
17+
}}
18+
{{else}}
19+
<p>{{options.body}}</p>
20+
{{#if options.iconClass}}
21+
<p class="text-center"><i class={{options.iconClass}}></i></p>
22+
{{/if}}
23+
{{/if}}
24+
{{/modal.body}}
25+
{{#modal.footer}}
26+
{{#if options.footerComponent}}
27+
{{component
28+
options.footerComponent
29+
options=options
30+
}}
31+
{{/if}}
32+
{{/modal.footer}}
33+
{{/bs-modal}}

Diff for: app/components/modals-container/process.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from 'ember-bootstrap-modals-manager/components/modals-container/process';

Diff for: package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: tests/acceptance/modals-test.js

+49
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,52 @@ test('Custom progress-modal (success)', function (assert) {
269269
return done();
270270
}, 3000);
271271
});
272+
273+
test('process-modal (success)', function (assert) {
274+
openModal('process');
275+
const done = assert.async();
276+
modalIsOpened(assert, true);
277+
andThen(() => {
278+
customModalText(assert, 'body', 'Some long process (you must add font-awesome to your project to use `fa`-icons)');
279+
assert.equal(find('.modal-footer button').length, 0, 'Footer has not buttons');
280+
keyEvent('.modal', 'keydown', 27).then(() => { // click Esc
281+
assert.equal(find('.modal-body').length, 1, 'Modal exists');
282+
});
283+
triggerEvent('.modal', 'click');
284+
modalIsOpened(assert, true);
285+
});
286+
setTimeout(() => {
287+
modalIsOpened(assert, false);
288+
lastLogMessageAssert(assert, 'Process was confirmed (with some result)');
289+
return done();
290+
}, 3100);
291+
});
292+
293+
test('process-modal (error)', function (assert) {
294+
click('.process-will-fail input');
295+
openModal('process');
296+
const done = assert.async();
297+
modalIsOpened(assert, true);
298+
setTimeout(() => {
299+
modalIsOpened(assert, false);
300+
lastLogMessageAssert(assert, 'Process was declined (with some error)');
301+
return done();
302+
}, 3100);
303+
});
304+
305+
test('Custom process-modal', function (assert) {
306+
openModal('custom-process');
307+
const done = assert.async();
308+
modalIsOpened(assert, true);
309+
andThen(() => {
310+
customModalText(assert, 'header', 'Custom Process Title Component');
311+
customModalText(assert, 'body', 'Custom Process Body Component');
312+
customModalText(assert, 'body', 'Some long process (you must add font-awesome to your project to use `fa`-icons)');
313+
customModalText(assert, 'footer', 'Custom Process Footer Component');
314+
});
315+
setTimeout(() => {
316+
modalIsOpened(assert, false);
317+
lastLogMessageAssert(assert, 'Process was confirmed (with some result)');
318+
return done();
319+
}, 3100);
320+
});

Diff for: tests/dummy/app/components/custom-process-body.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import Component from '@ember/component';
2+
import layout from '../templates/components/custom-process-body';
3+
4+
export default Component.extend({
5+
layout
6+
});

Diff for: tests/dummy/app/components/custom-process-footer.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import Component from '@ember/component';
2+
import layout from '../templates/components/custom-process-footer';
3+
4+
export default Component.extend({
5+
layout
6+
});

Diff for: tests/dummy/app/components/custom-process-header.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import Component from '@ember/component';
2+
import layout from '../templates/components/custom-process-header';
3+
4+
export default Component.extend({
5+
layout
6+
});

Diff for: tests/dummy/app/controllers/index.js

+35-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export default Controller.extend({
3737
striped: false,
3838
notStriped: not('striped'),
3939
progressWillFail: false,
40+
processWillFail: false,
4041

4142
addMessage(msg) {
4243
get(this, 'messages').pushObject(msg);
@@ -126,7 +127,7 @@ export default Controller.extend({
126127
set(this, 'options', options);
127128
get(this, 'modalsManager')
128129
.progress(options)
129-
.then((v) => this.addMessage(`Progress was finished (with ${JSON.stringify(v)})`))
130+
.then(v => this.addMessage(`Progress was finished (with ${JSON.stringify(v)})`))
130131
.catch(([result, error]) => {
131132
this.addMessage(`Progress was failed (completed ${JSON.stringify(result)}). Error - "${error}"`);
132133
return get(this, 'modalsManager')
@@ -136,6 +137,21 @@ export default Controller.extend({
136137
});
137138
});
138139
},
140+
showProcessModal() {
141+
const options = {
142+
body: 'Some long process (you must add font-awesome to your project to use `fa`-icons)',
143+
iconClass: 'text-center fa fa-spinner fa-spin fa-3x fa-fw',
144+
title: '',
145+
process: () => new Promise((resolve, reject) => setTimeout(() => {
146+
get(this, 'processWillFail') ? reject('some error') : resolve('some result');
147+
}, 3000))
148+
};
149+
set(this, 'options', options);
150+
get(this, 'modalsManager')
151+
.process(options)
152+
.then(v => this.addMessage(`Process was confirmed (with ${v})`))
153+
.catch(e => this.addMessage(`Process was declined (with ${e})`));
154+
},
139155

140156
showCustomAlertModal() {
141157
const options = {
@@ -226,6 +242,24 @@ export default Controller.extend({
226242
});
227243
});
228244
},
245+
showCustomProcessModal() {
246+
const options = {
247+
title: 'Process Modal Title',
248+
body: 'Some long process (you must add font-awesome to your project to use `fa`-icons)',
249+
titleComponent: 'custom-process-header',
250+
bodyComponent: 'custom-process-body',
251+
footerComponent: 'custom-process-footer',
252+
iconClass: 'text-center fa fa-spinner fa-spin fa-3x fa-fw',
253+
process: () => new Promise((resolve, reject) => setTimeout(() => {
254+
get(this, 'processWillFail') ? reject('some error') : resolve('some result');
255+
}, 3000))
256+
};
257+
set(this, 'options', options);
258+
get(this, 'modalsManager')
259+
.process(options)
260+
.then(v => this.addMessage(`Process was confirmed (with ${v})`))
261+
.catch(e => this.addMessage(`Process was declined (with ${e})`));
262+
},
229263
cleanMessage() {
230264
set(this, 'messages', A([]));
231265
}

Diff for: tests/dummy/app/index.html

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
<meta name="description" content="">
88
<meta name="viewport" content="width=device-width, initial-scale=1">
99

10+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
11+
1012
{{content-for "head"}}
1113

1214
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<p class="alert alert-info">Custom Process Body Component</p>
2+
<p class="alert alert-info">{{options.body}}</p>
3+
{{#if options.iconClass}}
4+
<p class="text-center"><i class={{options.iconClass}}></i></p>
5+
{{/if}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Custom Process Footer Component
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Custom Process Title Component

Diff for: tests/dummy/app/templates/index.hbs

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
{{#bs-button class="prompt-confirm-modal" onClick=(action "showPromptConfirmModal")}}Prompt Confirm Modal{{/bs-button}}
88
{{#bs-button class="check-confirm-modal" onClick=(action "showCheckConfirmModal")}}Check Confirm Modal{{/bs-button}}
99
{{#bs-button class="progress-modal" onClick=(action "showProgressModal")}}Progress Modal{{/bs-button}}
10+
{{#bs-button class="process-modal" onClick=(action "showProcessModal")}}Process Modal{{/bs-button}}
1011

1112
<h3>Modals with custom components:</h3>
1213
{{#bs-button class="custom-alert-modal" onClick=(action "showCustomAlertModal")}}Alert Modal{{/bs-button}}
@@ -15,6 +16,7 @@
1516
{{#bs-button class="custom-prompt-confirm-modal" onClick=(action "showCustomPromptConfirmModal")}}Prompt Confirm Modal{{/bs-button}}
1617
{{#bs-button class="custom-check-confirm-modal" onClick=(action "showCustomCheckConfirmModal")}}Check Confirm Modal{{/bs-button}}
1718
{{#bs-button class="custom-progress-modal" onClick=(action "showCustomProgressModal")}}Progress Modal{{/bs-button}}
19+
{{#bs-button class="custom-process-modal" onClick=(action "showCustomProcessModal")}}Process Modal{{/bs-button}}
1820
<h3>Settings:</h3>
1921
{{#bs-form formLayout="vertical" model=this as |form|}}
2022
<h4>Prompt:</h4>
@@ -33,6 +35,8 @@
3335
{{form.element controlType="checkbox" class="progress-will-fail" label="Progress will fail" property="progressWillFail"}}
3436
</div>
3537
</div>
38+
<h4>Process</h4>
39+
{{form.element controlType="checkbox" class="process-will-fail" label="Process will fail" property="processWillFail"}}
3640
{{/bs-form}}
3741
{{outlet}}
3842
</div>

0 commit comments

Comments
 (0)