Commit 16d63c94 authored by Joel Hockey's avatar Joel Hockey Committed by Commit Bot

Modify files toast to queue toasts rather than taking over

Currently files toast will hide any existing toast
and display the new one if it is called whilst an existing
toast is being displayed.

This change creates a queue for toasts, and all are displayed
in order.

Bug: 949356
Change-Id: I44d531295eef5845fe5fb1fc7f313ebce38ad8f2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1617044Reviewed-by: default avatarNoel Gordon <noel@chromium.org>
Reviewed-by: default avatarLuciano Pacheco <lucmult@chromium.org>
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Auto-Submit: Joel Hockey <joelhockey@chromium.org>
Cr-Commit-Position: refs/heads/master@{#662465}
parent 85a01e17
......@@ -163,6 +163,10 @@ IN_PROC_BROWSER_TEST_F(FileManagerJsTest, ProvidersModel) {
RunGeneratedTest("/foreground/js/providers_model_unittest.html");
}
IN_PROC_BROWSER_TEST_F(FileManagerJsTest, FilesToast) {
RunGeneratedTest("/foreground/elements/files_toast_unittest.html");
}
IN_PROC_BROWSER_TEST_F(FileManagerJsTest, SpinnerController) {
RunGeneratedTest("/foreground/js/spinner_controller_unittest.html");
}
......
......@@ -59,6 +59,7 @@ group("unit_test_data") {
"base/js:unit_tests",
"file_manager/background/js:unit_tests",
"file_manager/common/js:unit_tests",
"file_manager/foreground/elements:unit_tests",
"file_manager/foreground/js:unit_tests",
"file_manager/foreground/js/metadata:unit_tests",
"file_manager/foreground/js/ui:unit_tests",
......
......@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//third_party/closure_compiler/compile_js.gni")
import("//third_party/closure_compiler/js_unit_tests.gni")
visibility = [ "//ui/file_manager/file_manager/foreground/*" ]
......@@ -11,6 +12,7 @@ group("closure_compile") {
visibility += [ "//ui/file_manager:closure_compile" ]
deps = [
":closure_compile_module",
":unit_tests_type_check",
]
}
......@@ -79,3 +81,16 @@ js_library("files_toggle_ripple") {
js_library("files_tooltip") {
visibility += [ "//ui/file_manager/gallery/*" ]
}
js_unittest("files_toast_unittest") {
deps = [
":files_toast",
"//ui/webui/resources/js:webui_resource_test",
]
}
js_unit_tests("unit_tests") {
deps = [
":files_toast_unittest",
]
}
......@@ -2,6 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @typedef {{text:string, callback:(function()|undefined)}}
*/
var FilesToastAction;
/**
* @typedef {{text:string, action:(FilesToastAction|undefined)}}
*/
var FilesToastData;
/**
* Files Toast.
*
......@@ -32,14 +42,14 @@ var FilesToast = Polymer({
*/
created: function() {
/**
* @private {?{text: string, callback: function()}}
* @private {?FilesToastAction}
*/
this.action_ = null;
/**
* @private {number}
* @private {!Array<!FilesToastData>}
*/
this.generationId_ = 0;
this.queue_ = [];
/**
* @private {Animation}
......@@ -53,39 +63,24 @@ var FilesToast = Polymer({
},
/**
* Shows toast. If a toast is already shown, hide the current toast first and
* show next toast after the previous one disappears.
* Shows toast. If a toast is already shown, this toast will be added to the
* queue and shown when others have completed.
*
* @param {string} text Text of toast.
* @param {{text: string, callback:function()}=} opt_action Action. Callback
* @param {FilesToastAction=} opt_action Action. Callback
* is invoked when user taps an action.
*/
show: function(text, opt_action) {
this.generationId_++;
this.hide().then(this.showInternal_.bind(
this, text, !!opt_action ? opt_action : null, this.generationId_));
},
/**
* Internal method to show toast.
*
* @param {string} text Text of toast.
* @param {?{text: string, callback:function()}} action Action.
* @param {number} generationId Generation id.
* @private
*/
showInternal_: function(text, action, generationId) {
if (this.generationId_ !== generationId) {
if (this.visible) {
this.queue_.push({text: text, action: opt_action});
return;
}
this._setVisible(true);
// Update UI.
this.$.container.hidden = false;
this.$.text.innerText = text;
this.action_ = action;
this.action_ = opt_action || null;
if (this.action_) {
this.$.action.hidden = false;
......@@ -105,19 +100,7 @@ var FilesToast = Polymer({
});
// Set timeout.
setTimeout(this.timeout_.bind(this, this.generationId_), this.duration);
},
/**
* Handles timeout of a toast.
* @param {number} generationId Generation id.
*/
timeout_: function(generationId) {
if (this.generationId_ !== generationId) {
return;
}
this.hide();
setTimeout(this.hide.bind(this), this.duration);
},
/**
......@@ -170,6 +153,11 @@ var FilesToast = Polymer({
this.$.container.hidden = true;
this.hideAnimationPlayer_ = null;
this._setVisible(false);
// Show next in the queue, if any.
if (this.queue_.length > 0) {
const next = this.queue_.shift();
this.show(next.text, next.action);
}
});
}
});
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
function setUpPage() {
const importElements = (src) => {
const link = document.createElement('link');
link.rel = 'import';
link.onload = onLinkLoaded;
document.head.appendChild(link);
const sourceRoot = '../../../../../../../../';
link.href = sourceRoot + src;
};
let linksLoaded = 0;
const onLinkLoaded = () => {
if (++linksLoaded < 2) {
return;
}
document.body.innerHTML += '<files-toast></files-toast>';
window.waitUser = false;
};
importElements(
'third_party/polymer/v1_0/components-chromium/polymer/polymer.html');
importElements(
'ui/file_manager/file_manager/foreground/elements/files_toast.html');
// Make the test harness pause until the test page is fully loaded.
window.waitUser = true;
}
async function testToast(done) {
/** @type {FilesToast|Element} */
const toast = document.querySelector('files-toast');
const text = document.querySelector('files-toast #text');
const action = document.querySelector('files-toast #action');
const waitFor = async f => {
while (!f()) {
await new Promise(r => setTimeout(r, 0));
}
};
// Toast is hidden to start.
assertFalse(toast.visible);
// Show toast1, verify visible, text and action text.
let a1Called = false;
toast.show('t1', {
text: 'a1',
callback: () => {
a1Called = true;
}
});
assertTrue(toast.visible);
assertEquals('t1', text.innerText);
assertFalse(action.hidden);
assertEquals('a1', action.innerText);
// Queue up toast2 and toast3, should still be showing toast1.
let a2Called = false;
toast.show('t2', {
text: 'a2',
callback: () => {
a2Called = true;
}
});
toast.show('t3');
assertEquals('t1', text.innerText);
// Invoke toast1 action, callback will be called,
// and toast2 will show after animation.
action.dispatchEvent(new MouseEvent('click'));
assertTrue(a1Called);
await waitFor(() => text.innerText === 't2');
assertTrue(toast.visible);
assertEquals('t2', text.innerText);
assertFalse(action.hidden);
assertEquals('a2', action.innerText);
// Invoke toast2 action, callback will be called,
// and toast3 will show after animation with no action.
action.dispatchEvent(new MouseEvent('click'));
assertTrue(a2Called);
await waitFor(() => text.innerText === 't3');
assertTrue(toast.visible);
assertEquals('t3', text.innerText);
assertTrue(action.hidden);
// Call hide(), toast should no longer be visible, no more toasts shown.
toast.hide();
await waitFor(() => !toast.visible);
done();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment