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

Dialog API buff-up. #325

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 5 additions & 4 deletions addon/components/paper-dialog-inner.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import Ember from 'ember';
import Translate3dMixin from '../mixins/translate3d-mixin';
import PaperDialog from './paper-dialog';

const { Component, computed, $ } = Ember;
const { Component } = Ember;

export default Component.extend(Translate3dMixin, {
tagName: 'md-dialog',
Expand All @@ -19,8 +18,10 @@ export default Component.extend(Translate3dMixin, {
}
},

onTranslateToEnd(origin) {
$(origin).focus();
onTranslateToEnd($origin) {
if ($origin) {
$origin.focus();
}
},

click(ev) {
Expand Down
49 changes: 35 additions & 14 deletions addon/components/paper-dialog.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,53 @@
import Ember from 'ember';
const { Component, computed, $, inject: { service } } = Ember;
import { toJQuery } from '../mixins/translate3d-mixin';

const {
Component,
computed,
inject: { service },
isEmpty
} = Ember;

export default Component.extend({
tagName: '',

escapeToClose: true,
focusOnOpen: true,

destination: computed('parent', function() {
return this.get('parent') ? this.get('parent') : 'paper-wormhole';
}),
// Calculate a default that is always valid for the parent of the backdrop.
wormholeSelector: '#paper-wormhole',
defaultedParent: computed.or('parent', 'wormholeSelector'),

defaultParent: 'body',
// Calculate a default that is always valid where the opening transition should originate.
defaultedOpenFrom: computed.or('openFrom', 'origin', 'parent'),

hashedParent: computed('destination', function() {
let parent = this.get('destination');
return parent ? `#${parent}` : null;
}),
// Calculate a default that is always valid where the closing transition should terminate.
defaultedCloseTo: computed.or('closeTo', 'origin', 'parent'),

parentElementSelector: computed.or('hashedParent', 'defaultParent'),
// Calculate the id of the wormhole destination, setting it if need be. The
// id is that of the 'parent', if provided, or 'paper-wormhole' if not.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why this change?

+this.get('defaultedParent')
-this.get('parentElementSelector')

defaultedParent is the new parentElementSelector?

Copy link
Contributor

Choose a reason for hiding this comment

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

+1, parentElement || parentElementSelector is a very clear descriptor.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not complaining about the names. Just trying to understand.

destinationId: computed('defaultedParent', function() {
let parent = this.get('defaultedParent');
let $parent = toJQuery(parent);
// If the parent isn't found, assume that it is an id, but that the DOM doesn't
// exist yet. This only happens during integration tests or if entire application
// route is a dialog.
if (isEmpty($parent) && parent.charAt(0) === '#') {
return parent.substring(1);
}
let id = $parent.attr('id');
if (!id) {
id = `${this.elementId}-parent`;
$parent.get(0).id = id;
}
return id;
}),

constants: service(),

didInsertElement() {
if (this.get('escapeToClose')) {
let parent = this.get('parentElementSelector');
$(parent).on(`keydown.${this.elementId}`, (e) => {
toJQuery(this.get('defaultedParent')).on(`keydown.${this.elementId}`, (e) => {
if (e.keyCode === this.get('constants.KEYCODE.ESCAPE') && this.get('onClose')) {
this.get('onClose')();
}
Expand All @@ -35,8 +57,7 @@ export default Component.extend({

willDestroyElement() {
if (this.get('escapeToClose')) {
let parent = this.get('parentElementSelector');
$(parent).off(`keydown.${this.elementId}`);
toJQuery(this.get('defaultedParent')).off(`keydown.${this.elementId}`);
}
},

Expand Down
39 changes: 18 additions & 21 deletions addon/mixins/translate3d-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,26 @@ const {
String: { htmlSafe },
RSVP: { Promise },
computed,
on,
inject: { service },
run,
K
K,
typeOf
} = Ember;

export function toJQuery(element) {
return typeOf(element) === 'string' ? $(element) : element;
}

export default Mixin.create({
constants: service(),

attributeBindings: ['translateStyle:style'],
classNameBindings: ['transformIn:md-transition-in'],

fromElement: computed.or('openFrom', 'origin'),

toElement: computed.or('closeTo', 'origin'),

fromStyle: computed('fromElement', function() {
return this.toTransformCss(this.calculateZoomToOrigin(this.element, this.get('fromElement')));
fromStyle: computed('defaultedOpenFrom', function() {
return this.toTransformCss(this.calculateZoomToOrigin(this.element, this.get('defaultedOpenFrom')));
}),

translateToParent: computed.or('closeTo', 'origin', 'parent', 'defaultParent'),

centerStyle: computed(function() {
return this.toTransformCss('');
}),
Expand Down Expand Up @@ -59,8 +57,10 @@ export default Mixin.create({
// Wait while CSS takes affect
// Set the `main` styles and run the transition-in styles
run.next(() => {
this.waitTransitionEnd(this.element).then(() => {
this.onTranslateFromEnd();
this.waitTransitionEnd($(this.element)).then(() => {
if (!this.get('isDestroying') && !this.get('isDestroyed')) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the reason for this guard?
For situations where you create and destroy very quickly?
What was the problem you were having?

this.onTranslateFromEnd();
}
});
this.set('transformStyleApply', 'main');
this.set('transformIn', true);
Expand All @@ -79,21 +79,18 @@ export default Mixin.create({
let containerClone = this.$().parent().clone();
let dialogClone = containerClone.find('md-dialog');

let toElement = this.get('toElement');
let toStyle = this.toTransformCss(this.calculateZoomToOrigin(this.element, toElement));

let origin = this.get('origin');
let toStyle = this.toTransformCss(this.calculateZoomToOrigin(this.element, this.get('defaultedCloseTo')));

run.schedule('afterRender', () => {
$(this.get('parentElement')).parent().append(containerClone);
toJQuery(this.get('defaultedParent')).parent().append(containerClone);
run.next(() => {
dialogClone.removeClass('md-transition-in');
dialogClone.addClass('md-transition-out');
dialogClone.attr('style', toStyle);
run.next(() => {
this.waitTransitionEnd(dialogClone).then(() => {
containerClone.remove();
this.onTranslateToEnd(origin);
this.onTranslateToEnd(toJQuery(this.get('origin')));
});
});
});
Expand All @@ -107,14 +104,14 @@ export default Mixin.create({
*
* @public
*/
waitTransitionEnd(element) {
waitTransitionEnd($element) {

// fallback is 3 secs
return new Promise((resolve/*, reject*/) => {

// Upon timeout or transitionEnd, reject or resolve (respectively) this promise.
// NOTE: Make sure this transitionEnd didn't bubble up from a child
$(element).on(this.TRANSITIONEND, function(ev) {
$element.on(this.TRANSITIONEND, function(ev) {
if (ev) {
resolve();
}
Expand All @@ -138,7 +135,7 @@ export default Mixin.create({
let zoomStyle;

if (originator) {
originator = typeof originator === 'string' ? $(originator).get(0) : originator;
originator = toJQuery(originator).get(0);
let originBnds = this.copyRect(originator.getBoundingClientRect());
let dialogRect = this.copyRect(element.getBoundingClientRect());
let dialogCenterPt = this.centerPointFor(dialogRect);
Expand Down
8 changes: 4 additions & 4 deletions app/templates/components/paper-dialog.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{#ember-wormhole to=destination}}
{{#ember-wormhole to=destinationId}}
{{paper-backdrop
locked-open=isLockedOpen
opaque=true
Expand All @@ -8,10 +8,10 @@
}}
{{#paper-dialog-container outsideClicked=(action "outsideClicked")}}
{{#paper-dialog-inner
parentElement=parentElementSelector
origin=origin
openFrom=openFrom
closeTo=closeTo
defaultedParent=defaultedParent
defaultedOpenFrom=defaultedOpenFrom
defaultedCloseTo=defaultedCloseTo
fullscreen=fullscreen
clickOutsideToClose=clickOutsideToClose
focusOnOpen=focusOnOpen
Expand Down
9 changes: 5 additions & 4 deletions tests/dummy/app/controllers/dialog.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import Ember from 'ember';
const { $ } = Ember;

export default Ember.Controller.extend({
actions: {

/* Dialog with parent */
openDialogWithParent(param, event) {
this.set('dialogOrigin', event.currentTarget);
this.set('dialogOrigin', $(event.currentTarget));
this.set('showDialogWithParent', true);
},

Expand All @@ -16,7 +17,7 @@ export default Ember.Controller.extend({

/* Dialog */
openDialog(param, event) {
this.set('dialogOrigin', event.currentTarget);
this.set('dialogOrigin', $(event.currentTarget));
this.set('showDialog', true);
},

Expand All @@ -27,8 +28,8 @@ export default Ember.Controller.extend({

/* Prompt dialog */
dogName: '',
openPromptDialog(param, event) {
this.set('dialogOrigin', event.currentTarget);
openPromptDialog(/* param, event */) {
this.set('dialogOrigin', null);
this.set('showPromptDialog', true);
},

Expand Down
22 changes: 11 additions & 11 deletions tests/dummy/app/templates/dialog.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
{{#code-block language="handlebars"}}\{{#if showDialogWithParent}}
\{{#paper-dialog
onClose=(action "closeDialogWithParent" "cancel")
parent="paper-dialog-demo"
parent="#paper-dialog-demo"
origin=dialogOrigin
clickOutsideToClose=true}}
<form>
Expand Down Expand Up @@ -136,32 +136,32 @@
</tr>
<tr>
<td><strong>parent</strong></td>
<td>id</td>
<td>jQuery object or selector</td>
<td>existing element where the modal and backdrop will be rendered</td>
</tr>
<tr>
<td><strong>fullscreen</strong></td>
<td>boolean</td>
<td>if true dialog becomes fullscreen at smaller breakpoints</td>
</tr>
<tr>
<td><strong>origin</strong></td>
<td>selector</td>
<td>jQuery object or selector</td>
<td>
if present, the dialog will use it as openFrom and closeTo. Also,
focus will be returned to this element once the dialog closes.
</td>
</tr>
<tr>
<td><strong>openFrom</strong></td>
<td>selector</td>
<td>jQuery object or selector</td>
<td>source for opening the dialog with a transition</td>
</tr>
<tr>
<td><strong>closeTo</strong></td>
<td>selector</td>
<td>jQuery object or selector</td>
<td>target for closing the dialog with a transition</td>
</tr>
<tr>
<td><strong>fullscreen</strong></td>
<td>boolean</td>
<td>if true dialog becomes fullscreen at smaller breakpoints</td>
</tr>
<tr>
<td><strong>clickOutsideToClose</strong></td>
<td>boolean</td>
Expand Down Expand Up @@ -196,7 +196,7 @@
{{/paper-content}}

{{#if showDialogWithParent}}
{{#paper-dialog onClose=(action "closeDialogWithParent" "cancel") parent="paper-dialog-demo" origin=dialogOrigin clickOutsideToClose=true}}
{{#paper-dialog onClose=(action "closeDialogWithParent" "cancel") parent="#paper-dialog-demo" origin=dialogOrigin clickOutsideToClose=true}}
<form>
{{#paper-toolbar}}
<div class="md-toolbar-tools">
Expand Down
17 changes: 9 additions & 8 deletions tests/integration/components/paper-dialog-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ test('should render in specific wormhole if parent is defined', function(assert)
this.render(hbs`
<div id="paper-wormhole"></div>
<div id="sagittarius-a"></div>
{{#paper-dialog parent="sagittarius-a"}}
{{#paper-dialog parent="#sagittarius-a"}}
So this is singularity, eh?
{{/paper-dialog}}
`);
Expand All @@ -69,7 +69,7 @@ test('should render in specific wormhole if parent is defined', function(assert)
test('should only prevent scrolling behind scoped modal', function(assert) {
this.render(hbs`
<div id="sagittarius-a"></div>
{{paper-dialog parent="sagittarius-a"}}
{{paper-dialog parent="#sagittarius-a"}}
`);

assert.equal(this.$('md-backdrop').css('position'), 'absolute', 'backdrop is absolute');
Expand Down Expand Up @@ -98,23 +98,24 @@ test('applies transitions when opening and closing', function(assert) {
this.set('dialogOpen', true);

let getDialogTransform = () => {
let dialogStyle = this.$('md-dialog').get(0).style;
return dialogStyle.webkitTransform || dialogStyle.transform;
let dialog = this.$('md-dialog').get(0);
assert.ok(dialog, 'dialog found');
return dialog && (dialog.style.webkitTransform || dialog.style.transform);
};

let dialogTransform = getDialogTransform();
assert.ok(dialogTransform.indexOf('translate3d') !== -1);
assert.ok(dialogTransform.indexOf('translate3d') !== -1, 'open translate was added');

return wait().then(() => {
let dialogTransform = getDialogTransform();
assert.ok(!dialogTransform, 'translate was removed');
assert.ok(!dialogTransform, 'open translate was removed');

this.set('dialogOpen', false);

return wait();
}).then(() => {
let dialogTransform = getDialogTransform();
assert.ok(dialogTransform.indexOf('translate3d') !== -1, 'translate was added');
assert.ok(dialogTransform.indexOf('translate3d') !== -1, 'close translate was added');
});
});

Expand Down Expand Up @@ -223,4 +224,4 @@ test('opening gives focus', function(assert) {
});
});

});
});