Skip to content

Commit

Permalink
feat(model): make deferred private
Browse files Browse the repository at this point in the history
  • Loading branch information
next-joserepresa authored and josex2r committed Oct 5, 2020
1 parent ca50ad3 commit ba54c09
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 71 deletions.
6 changes: 3 additions & 3 deletions addon/components/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,14 @@ export default class ModalComponent extends Component.extend({
return;
}

this.modal.content.removeObject(this.model);
this.modal.closeByModel(this.model);
}

resolve(data, label = `Component '${this.model.fullname}': fulfillment`) {
this.model.deferred.resolve(data, label);
this.model.resolve(data, label);
}

reject(data, label = `Component '${this.model.fullname}': rejection`) {
this.model.deferred.reject(data, label);
this.model.reject(data, label);
}
}
37 changes: 32 additions & 5 deletions addon/models/modal.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import { reads } from '@ember/object/computed';
import { oneWay } from '@ember/object/computed';
import EmberObject, { computed } from '@ember/object';
import { dasherize } from '@ember/string';
import { defer } from 'rsvp';
import { isBlank } from '@ember/utils';
import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
import { tracked } from '@glimmer/tracking';

export default class ModalModel extends EmberObject {
export default class ModalModel extends EmberObject.extend(PromiseProxyMixin) {
name = null;
options = null;
deferred = null;

@reads('deferred.promise') promise;
// Do not use "PromiseProxyMixin", "@oneWay" does not tracks the state.
@tracked isPending = true;
@tracked isSettled = false;
@tracked isFulfilled = false;
@tracked isRejected = false;

@tracked _deferred;

@oneWay('_deferred.promise') promise;

@computed('name')
get fullname() {
Expand All @@ -25,6 +34,24 @@ export default class ModalModel extends EmberObject {
init() {
super.init(...arguments);

this.set('deferred', defer(`Modal: open '${this.fullname}'`));
this._deferred = defer(`Modal: open '${this.fullname}'`);
}

resolve() {
this.isPending = false;
this.isSettled = true;
this.isFulfilled = true;
this.isRejected = false;

return this._deferred.resolve(...arguments);
}

reject() {
this.isPending = false;
this.isSettled = true;
this.isFulfilled = false;
this.isRejected = true;

return this._deferred.reject(...arguments);
}
}
23 changes: 11 additions & 12 deletions addon/services/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,24 @@ export default class ModalService extends Service.extend(Evented) {

open(name, options = {}) {
const ModalModel = getOwner(this).factoryFor('model:modal');
const modal = ModalModel.create({ name, options });
const model = ModalModel.create({ name, options });

// If the modal is already opened, reject it
if (this.isOpened(name)) {
modal.get('deferred').reject(null, `Modal: '${modal.fullname}' is already opened`);
model.reject(null, `Modal: '${model.fullname}' is already opened`);
} else {
// Add new modal.
this.content.addObject(modal);
this.content.addObject(model);
}

this.trigger('open', modal);
this.trigger('open', model);

return modal.get('promise');
return model.get('promise');
}

closeByModel(model) {
this.trigger('close', model);
this.content.removeObject(model);
}

close(key, value) {
Expand All @@ -34,14 +39,8 @@ export default class ModalService extends Service.extend(Evented) {
}

filter.forEach((modal) => {
const deferred = modal.get('deferred');

if (isEmpty(deferred.promise._state)) {
deferred.reject(null, `Modal: closing '${modal.fullname}'`);
}
modal.reject(null, `Modal: closing '${modal.fullname}'`);
});

this.trigger('close', key);
}

isOpened(name) {
Expand Down
30 changes: 10 additions & 20 deletions tests/integration/components/modal-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import EmberObject from '@ember/object';
import RSVP from 'rsvp';
import sinon from 'sinon';
import { waitUntil } from '@ember/test-helpers';
Expand All @@ -10,14 +9,13 @@ import { run } from '@ember/runloop';
import { click, render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import ModalModel from 'ember-modal-service/models/modal';
import ModalService from 'ember-modal-service/services/modal';
import SchedulerService from 'ember-task-scheduler/services/scheduler';
import ModalComponent from 'ember-modal-service/components/modal';

const { spy } = sinon;
const ANIMATION_DELAY = 300;

let component, deferred, service, content, didOpenSpy;
let component, service, content, didOpenSpy, model;

function waitForTransitionEnd(element) {
return new RSVP.Promise((resolve) => {
Expand Down Expand Up @@ -49,13 +47,12 @@ module('Integration | Component | modal', (hooks) => {
setupRenderingTest(hooks);

hooks.beforeEach(async function() {
this.owner.register('service:modal', ModalService);
this.owner.register('service:scheduler', SchedulerService);
this.owner.register('model:modal', ModalModel);

deferred = RSVP.defer();
content = A();
didOpenSpy = spy();
model = ModalModel.create({ name: 'foo' });

const layout = hbs`
<button data-id="resolve" {{on 'click' (action 'resolve' 'foo')}}>Resolve</button>
Expand All @@ -65,15 +62,8 @@ module('Integration | Component | modal', (hooks) => {
class MyComponent extends ModalComponent {
layout = layout;
target = null;
model = EmberObject.create({
fullname: 'modal-foo',
deferred,
promise: deferred.promise
});
model = model;
didOpen = didOpenSpy;
modal = {
content
};
}

this.owner.register('component:my-modal', MyComponent);
Expand Down Expand Up @@ -117,7 +107,7 @@ module('Integration | Component | modal', (hooks) => {
assert.ok(isVisible(component));

run(() => {
deferred.resolve();
model.resolve();
});

assert.notOk(isVisible(component));
Expand All @@ -134,7 +124,7 @@ module('Integration | Component | modal', (hooks) => {
assert.ok(isVisible(component));

run(() => {
deferred.reject();
model.reject();
});

assert.notOk(isVisible(component));
Expand Down Expand Up @@ -190,7 +180,7 @@ module('Integration | Component | modal', (hooks) => {
await waitForScheduler();

run(() => {
deferred.resolve();
model.resolve();
});

assert.notOk(isVisible(component));
Expand All @@ -211,7 +201,7 @@ module('Integration | Component | modal', (hooks) => {

instance.resolve('foo');

assert.equal(await deferred.promise, 'foo');
assert.equal(await model.promise, 'foo');
});

test('it rejects promise with arguments', async function(assert) {
Expand All @@ -225,7 +215,7 @@ module('Integration | Component | modal', (hooks) => {

await waitForScheduler();

deferred.promise.catch((foo) => {
model.promise.catch((foo) => {
assert.equal(foo, 'foo');
});

Expand All @@ -241,7 +231,7 @@ module('Integration | Component | modal', (hooks) => {

await click('[data-id="resolve"]');

assert.equal(await deferred.promise, 'foo');
assert.equal(await model.promise, 'foo');
});

test('it rejects promise with action', async(assert) => {
Expand All @@ -253,7 +243,7 @@ module('Integration | Component | modal', (hooks) => {

await waitForScheduler();

deferred.promise.catch((foo) => {
model.promise.catch((foo) => {
assert.equal(foo, 'foo');
});

Expand Down
31 changes: 27 additions & 4 deletions tests/integration/services/modal-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ module('Integration | Service | modal', (hooks) => {

assert.equal($element.length, 1, 'Modal is displayed');

run(service.get('content.0.deferred'), 'resolve', 'bar');
run(service.get('content.0'), 'resolve', 'bar');

$element = find('[data-id="modalBar"]');

Expand All @@ -101,7 +101,7 @@ module('Integration | Service | modal', (hooks) => {

await waitForTimeout(ANIMATION_DELAY);

run(service.get('content.0.deferred'), 'resolve', 'foo');
run(service.get('content.0'), 'resolve', 'foo');

$element = find('[data-id="modalFoo"]:not([data-modal-show="true"])');

Expand Down Expand Up @@ -131,7 +131,7 @@ module('Integration | Service | modal', (hooks) => {

assert.equal($element.length, 1, 'Modal is displayed');

run(service.get('content.0.deferred'), 'reject', 'bar');
run(service.get('content.0'), 'reject', 'bar');

$element = find('[data-id="modalBar"]');

Expand Down Expand Up @@ -159,7 +159,7 @@ module('Integration | Service | modal', (hooks) => {

await waitForTimeout(ANIMATION_DELAY);

run(service.get('content.0.deferred'), 'reject', 'foo');
run(service.get('content.0'), 'reject', 'foo');

$element = find('[data-id="modalFoo"]:not([data-modal-show="true"])');

Expand Down Expand Up @@ -229,4 +229,27 @@ module('Integration | Service | modal', (hooks) => {

assert.equal($element.length, 0, 'Modal is removed from DOM');
});

test('it triggers events when a modal is open/closed', (assert) => {
const done = assert.async();

service.one('open', (modal) => {
assert.ok(1, 'modal is open');
assert.equal(modal.name, 'foo', 'modal exists as first argument');
});

run(() => {
service.open('foo');
});

service.one('close', (modal) => {
assert.ok(1, 'modal is closed');
assert.equal(modal.name, 'foo', 'key exists as first argument');
done();
});

run(() => {
service.close('foo');
});
});
});
36 changes: 34 additions & 2 deletions tests/unit/models/modal-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,41 @@ module('Unit | Model | modal', (hooks) => {
}, Error, 'Modal must have a name.');
});

test('it setups the deferred, promise and fullname objects on init', (assert) => {
assert.ok(model.get('deferred'));
test('it setups the promise and fullname objects on init', (assert) => {
assert.ok(model.get('promise'));
assert.ok(model.get('fullname'));
});

test('it tracks the pending promise state', (assert) => {
assert.equal(model.isPending, true, 'isPending');
assert.equal(model.isSettled, false, 'isSettled');
assert.equal(model.isFulfilled, false, 'isFulfilled');
assert.equal(model.isRejected, false, 'isPisRejected');
});

test('it tracks the resolved promise state', async(assert) => {
model.resolve();

await model.promise;

assert.equal(model.isPending, false, 'isPending');
assert.equal(model.isSettled, true, 'isSettled');
assert.equal(model.isFulfilled, true, 'isFulfilled');
assert.equal(model.isRejected, false, 'isPisRejected');
});

test('it tracks the rejected promise state', async(assert) => {
model.reject();

try {
await model.promise;
} catch (e) {
// Nope
}

assert.equal(model.isPending, false, 'isPending');
assert.equal(model.isSettled, true, 'isSettled');
assert.equal(model.isFulfilled, false, 'isFulfilled');
assert.equal(model.isRejected, true, 'isPisRejected');
});
});
26 changes: 1 addition & 25 deletions tests/unit/services/modal-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ module('Unit | Service | modal', (hooks) => {
assert.equal(content.objectAt(0).get('name'), 'foo');
assert.equal(content.objectAt(1).get('name'), 'bar');
assert.equal(content.objectAt(1).get('options.bar'), 'bar');
assert.ok(content.objectAt(1).get('deferred'));
assert.ok(content.objectAt(1).get('promise'));
});

Expand All @@ -67,7 +66,7 @@ module('Unit | Service | modal', (hooks) => {

service.get('content').addObjects(modals);

service.get('content').objectAt(0).get('deferred').resolve();
service.get('content').objectAt(0).resolve();

service.close();

Expand Down Expand Up @@ -99,27 +98,4 @@ module('Unit | Service | modal', (hooks) => {
assert.equal(service.get('content').findBy('name', 'foo').get('promise._state'), PENDING);
assert.equal(service.get('content').findBy('name', 'bar').get('promise._state'), REJECTED);
});

test('it triggers events when a modal is open/closed', (assert) => {
const done = assert.async();

service.one('open', (modal) => {
assert.ok(1, 'modal is open');
assert.equal(modal.name, 'foo', 'modal exists as first argument');
});

run(() => {
service.open('foo');
});

service.one('close', (key) => {
assert.ok(1, 'modal is closed');
assert.equal(key, 'foo', 'key exists as first argument');
done();
});

run(() => {
service.close('foo');
});
});
});

0 comments on commit ba54c09

Please sign in to comment.