Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 285ac72

Browse files
fix(toast): differentiate between hide action clicks and hide timeouts
All interim elements now resolve with 'true' for hide or timeout responses; unless overridden in the `.hide(value)` call. Updated toast docs and demos. Fixes #3745.
1 parent 0982c76 commit 285ac72

File tree

4 files changed

+148
-59
lines changed

4 files changed

+148
-59
lines changed

src/components/toast/demoBasicUsage/script.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ angular.module('toastDemo1', ['ngMaterial'])
4141
.highlightAction(false)
4242
.position($scope.getToastPosition());
4343

44-
$mdToast.show(toast).then(function() {
45-
alert('You clicked \'OK\'.');
44+
$mdToast.show(toast).then(function(response) {
45+
if ( response == 'ok' ) {
46+
alert('You clicked \'OK\'.');
47+
}
4648
});
4749
};
4850

src/components/toast/toast.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ function MdToastDirective() {
7474
* @returns {obj} a `$mdToastPreset` with the chainable configuration methods:
7575
*
7676
* - $mdToastPreset#content(string) - sets toast content to string
77-
* - $mdToastPreset#action(string) - adds an action button, which resolves the promise returned from `show()` if clicked.
77+
* - $mdToastPreset#action(string) - adds an action button. If clicked the promise (returned from `show()`) will resolve
78+
* with value 'ok'; otherwise it promise is resolved with 'true' after a hideDelay timeout.
7879
* - $mdToastPreset#highlightAction(boolean) - sets action button to be highlighted
7980
* - $mdToastPreset#capsule(boolean) - adds 'md-capsule' class to the toast (curved corners)
8081
* - $mdToastPreset#theme(string) - sets the theme on the toast to theme (default is `$mdThemingProvider`'s default theme)
@@ -136,7 +137,9 @@ function MdToastDirective() {
136137
* to the root element of the application.
137138
*
138139
* @returns {promise} A promise that can be resolved with `$mdToast.hide()` or
139-
* rejected with `$mdToast.cancel()`.
140+
* rejected with `$mdToast.cancel()`. `$mdToast.hide()` will resolve either with a Boolean
141+
* value == 'true' or the value passed as an argument to `$mdToast.hide()`.
142+
* And `$mdToast.cancel()` will resolve the promise with a Boolean value == 'false'
140143
*/
141144

142145
/**
@@ -148,7 +151,9 @@ function MdToastDirective() {
148151
*
149152
* @param {*=} response An argument for the resolved promise.
150153
*
151-
* @returns {promise} a promise that is called when the existing element is removed from the DOM
154+
* @returns {promise} a promise that is called when the existing element is removed from the DOM.
155+
* The promise is resolved with either a Boolean value == 'true' or the value passed as the
156+
* argument to `.hide()`.
152157
*
153158
*/
154159

@@ -163,10 +168,14 @@ function MdToastDirective() {
163168
* @param {*=} response An argument for the rejected promise.
164169
*
165170
* @returns {promise} a promise that is called when the existing element is removed from the DOM
171+
* The promise is resolved with a Boolean value == 'false'.
166172
*
167173
*/
168174

169175
function MdToastProvider($$interimElementProvider) {
176+
// Differentiate promise resolves: hide timeout (value == true) and hide action clicks (value == ok).
177+
var ACTION_RESOLVE = 'ok';
178+
170179
var activeToastContent;
171180
var $mdToast = $$interimElementProvider('$mdToast')
172181
.setDefaults({
@@ -192,7 +201,7 @@ function MdToastProvider($$interimElementProvider) {
192201
self.content = activeToastContent;
193202
});
194203
this.resolve = function() {
195-
$mdToast.hide();
204+
$mdToast.hide( ACTION_RESOLVE );
196205
};
197206
},
198207
theme: $mdTheming.defaultTheme(),

src/components/toast/toast.spec.js

Lines changed: 122 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
describe('$mdToast service', function() {
22
beforeEach(module('material.components.toast', function($provide) {
33
}));
4+
afterEach(inject(function($timeout, $animate) {
5+
$animate.triggerCallbacks();
6+
$timeout.flush();
7+
}));
48

59
function setup(options) {
10+
var promise;
611
inject(function($mdToast, $rootScope, $animate) {
7-
$animate.triggerCallbacks();
812
options = options || {};
9-
$mdToast.show(options);
13+
$animate.triggerCallbacks();
14+
15+
promise = $mdToast.show(options);
16+
1017
$rootScope.$apply();
1118
$animate.triggerCallbacks();
1219
});
20+
return promise;
1321
}
1422

1523
describe('simple()', function() {
1624
hasConfigMethods(['content', 'action', 'capsule', 'highlightAction', 'theme']);
1725

1826
it('supports a basic toast', inject(function($mdToast, $rootScope, $timeout, $animate) {
19-
var resolved = false;
27+
var openAndclosed = false;
2028
var parent = angular.element('<div>');
2129
$mdToast.show(
2230
$mdToast.simple({
@@ -26,7 +34,7 @@ describe('$mdToast service', function() {
2634
capsule: true
2735
})
2836
).then(function() {
29-
resolved = true;
37+
openAndclosed = true;
3038
});
3139
$rootScope.$digest();
3240
expect(parent.find('span').text()).toBe('Do something');
@@ -35,7 +43,7 @@ describe('$mdToast service', function() {
3543
$animate.triggerCallbacks();
3644
$timeout.flush();
3745
$animate.triggerCallbacks();
38-
expect(resolved).toBe(true);
46+
expect(openAndclosed).toBe(true);
3947
}));
4048

4149
it('supports dynamicly updating the content', inject(function($mdToast, $rootScope, $rootElement) {
@@ -125,41 +133,6 @@ describe('$mdToast service', function() {
125133

126134
describe('build()', function() {
127135
describe('options', function() {
128-
it('should hide current toast when showing new one', inject(function($rootElement) {
129-
setup({
130-
template: '<md-toast class="one">'
131-
});
132-
expect($rootElement[0].querySelector('md-toast.one')).toBeTruthy();
133-
expect($rootElement[0].querySelector('md-toast.two')).toBeFalsy();
134-
expect($rootElement[0].querySelector('md-toast.three')).toBeFalsy();
135-
136-
setup({
137-
template: '<md-toast class="two">'
138-
});
139-
expect($rootElement[0].querySelector('md-toast.one')).toBeFalsy();
140-
expect($rootElement[0].querySelector('md-toast.two')).toBeTruthy();
141-
expect($rootElement[0].querySelector('md-toast.three')).toBeFalsy();
142-
143-
setup({
144-
template: '<md-toast class="three">'
145-
});
146-
expect($rootElement[0].querySelector('md-toast.one')).toBeFalsy();
147-
expect($rootElement[0].querySelector('md-toast.two')).toBeFalsy();
148-
expect($rootElement[0].querySelector('md-toast.three')).toBeTruthy();
149-
}));
150-
151-
it('should hide after duration', inject(function($timeout, $animate, $rootElement) {
152-
var parent = angular.element('<div>');
153-
var hideDelay = 1234;
154-
setup({
155-
template: '<md-toast />',
156-
hideDelay: hideDelay
157-
});
158-
expect($rootElement.find('md-toast').length).toBe(1);
159-
$timeout.flush(hideDelay);
160-
expect($rootElement.find('md-toast').length).toBe(0);
161-
}));
162-
163136
it('should have template', inject(function($timeout, $rootScope, $rootElement) {
164137
var parent = angular.element('<div>');
165138
setup({
@@ -191,9 +164,11 @@ describe('$mdToast service', function() {
191164
expect(toast.hasClass('md-left')).toBe(true);
192165
}));
193166
});
167+
});
194168

195-
describe('lifecycle', function() {
196-
it('should hide current toast when showing new one', inject(function($rootElement) {
169+
describe('lifecycle', function() {
170+
describe('should hide',function() {
171+
it('current toast when showing new one', inject(function($rootElement) {
197172
setup({
198173
template: '<md-toast class="one">'
199174
});
@@ -216,18 +191,116 @@ describe('$mdToast service', function() {
216191
expect($rootElement[0].querySelector('md-toast.three')).toBeTruthy();
217192
}));
218193

219-
it('should add class to toastParent', inject(function($rootElement) {
194+
it('after duration', inject(function($timeout, $animate, $rootElement) {
195+
var parent = angular.element('<div>');
196+
var hideDelay = 1234;
220197
setup({
221-
template: '<md-toast>'
198+
template: '<md-toast />',
199+
hideDelay: hideDelay
222200
});
223-
expect($rootElement.hasClass('md-toast-open-bottom')).toBe(true);
201+
expect($rootElement.find('md-toast').length).toBe(1);
202+
$timeout.flush(hideDelay);
203+
expect($rootElement.find('md-toast').length).toBe(0);
204+
}));
224205

206+
it('and resolve with default `true`', inject(function($timeout, $animate, $mdToast) {
207+
var hideDelay = 1234, result, fault;
225208
setup({
226-
template: '<md-toast>',
227-
position: 'top'
228-
});
229-
expect($rootElement.hasClass('md-toast-open-top')).toBe(true);
209+
template: '<md-toast />',
210+
hideDelay: 1234
211+
}).then(
212+
function(response){ result = response; },
213+
function(error){ fault = error; }
214+
);
215+
216+
$mdToast.hide();
217+
218+
$timeout.flush();
219+
$animate.triggerCallbacks();
220+
221+
expect(result).toBe(true);
222+
expect(angular.isUndefined(fault)).toBe(true);
223+
224+
}));
225+
226+
it('and resolve with specified value', inject(function($timeout, $animate, $mdToast) {
227+
var hideDelay = 1234, result, fault;
228+
setup({
229+
template: '<md-toast />',
230+
hideDelay: 1234
231+
}).then(
232+
function(response){ result = response; },
233+
function(error){ fault = error; }
234+
);
235+
236+
$mdToast.hide("secret");
237+
238+
$timeout.flush();
239+
$animate.triggerCallbacks();
240+
241+
expect(result).toBe("secret");
242+
expect(angular.isUndefined(fault)).toBe(true);
243+
244+
}));
245+
246+
it('and resolve `true` after timeout', inject(function($timeout, $animate) {
247+
var hideDelay = 1234, result, fault;
248+
setup({
249+
template: '<md-toast />',
250+
hideDelay: 1234
251+
}).then(
252+
function(response){ result = response; },
253+
function(error){ fault = error; }
254+
);
255+
256+
$timeout.flush();
257+
$animate.triggerCallbacks();
258+
259+
expect(result).toBe(true);
260+
expect(angular.isUndefined(fault)).toBe(true);
261+
262+
}));
263+
264+
it('and resolve `ok` with click on OK button', inject(function($mdToast, $rootScope, $timeout, $animate) {
265+
var result, fault;
266+
var parent = angular.element('<div>');
267+
var toast = $mdToast.simple({
268+
parent: parent,
269+
content: 'Do something'
270+
}).action('Close with "ok" response');
271+
272+
$mdToast
273+
.show(toast)
274+
.then(
275+
function(response){ result = response; },
276+
function(error){ fault = error; }
277+
);
278+
$rootScope.$digest();
279+
$animate.triggerCallbacks();
280+
281+
parent.find('button').triggerHandler('click');
282+
283+
$timeout.flush();
284+
$animate.triggerCallbacks();
285+
286+
expect(result).toBe('ok');
287+
expect(angular.isUndefined(fault)).toBe(true);
288+
230289
}));
231290
});
291+
292+
it('should add class to toastParent', inject(function($rootElement) {
293+
setup({
294+
template: '<md-toast>'
295+
});
296+
expect($rootElement.hasClass('md-toast-open-bottom')).toBe(true);
297+
298+
setup({
299+
template: '<md-toast>',
300+
position: 'top'
301+
});
302+
expect($rootElement.hasClass('md-toast-open-top')).toBe(true);
303+
}));
232304
});
305+
233306
});

src/core/services/interimElement/interimElement.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,9 @@ function InterimElementProvider() {
193193
function showInterimElement(opts) {
194194
// opts is either a preset which stores its options on an _options field,
195195
// or just an object made up of options
196-
if (opts && opts._options) opts = opts._options;
196+
opts = opts || { };
197+
if (opts._options) opts = opts._options;
198+
197199
return interimElementService.show(
198200
angular.extend({}, defaultOptions, opts)
199201
);
@@ -281,12 +283,14 @@ function InterimElementProvider() {
281283
*
282284
*/
283285
function hide(response) {
286+
response = response || true;
284287
var interimElement = stack.shift();
285-
return !interimElement ? $q.when(true) :
288+
return !interimElement ? $q.when(response) :
286289
interimElement
287290
.remove()
288291
.then(function() {
289292
interimElement.deferred.resolve(response);
293+
return interimElement.deferred.promise;
290294
});
291295
}
292296

@@ -303,8 +307,9 @@ function InterimElementProvider() {
303307
*
304308
*/
305309
function cancel(reason) {
310+
reason = reason || false;
306311
var interimElement = stack.shift();
307-
return !interimElement ? $q.when(true) :
312+
return !interimElement ? $q.when(reason) :
308313
interimElement
309314
.remove()
310315
.then(function() {
@@ -396,7 +401,7 @@ function InterimElementProvider() {
396401
}
397402
}
398403
},
399-
function(reason) { showDone = true; self.deferred.reject(reason); })
404+
function(reason) { showDone = true; self.deferred.reject(reason || false); })
400405
},
401406
cancelTimeout: function() {
402407
if (hideTimeout) {

0 commit comments

Comments
 (0)