[FilesBanner] Update the "unrecognized file system" panel to banners
The format-panel was previously shown when a invalid file system is identified on a USB device. This doesn't really align with our UI treatment, so migrate this to the banners framework. This adds a new handler for commands to be used from buttons of StateBanners as well as removing all the previous code around the format-panel. Bug: 1295992 Test: browser_tests --gtest_filter=FileManagerJsTest.StateBanner Test: Deploy to device and plug in corrupt USB Change-Id: Idaabb44f078241f08be1cb017d0179e8b40ca37e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3450061 Reviewed-by: Luciano Pacheco <lucmult@chromium.org> Commit-Queue: Ben Reich <benreich@chromium.org> Cr-Commit-Position: refs/heads/main@{#969713}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
b83943fc63
commit
d0ef37ee10
@ -375,6 +375,7 @@ preprocess_if_expr("preprocess_generated") {
|
||||
"file_manager/foreground/js/ui/banners/drive_welcome_banner.js",
|
||||
"file_manager/foreground/js/ui/banners/educational_banner.js",
|
||||
"file_manager/foreground/js/ui/banners/holding_space_welcome_banner.js",
|
||||
"file_manager/foreground/js/ui/banners/invalid_usb_filesystem_banner.js",
|
||||
"file_manager/foreground/js/ui/banners/local_disk_low_space_banner.js",
|
||||
"file_manager/foreground/js/ui/banners/photos_welcome_banner.js",
|
||||
"file_manager/foreground/js/ui/banners/shared_with_crostini_pluginvm_banner.js",
|
||||
|
@ -2149,47 +2149,13 @@ cr-menu.chrome-menu hr {
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
/* Panel shown when USB device with no readable partitions
|
||||
is plugged in. */
|
||||
#format-panel {
|
||||
bottom: 0;
|
||||
color: var(--cros-text-color-primary);
|
||||
display: none;
|
||||
left: 0;
|
||||
padding-inline-start: 50px;
|
||||
padding-top: 20px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
body[unformatted] .dialog-container #format-panel {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body[drive='unmounted'] .dialog-container .filelist-panel,
|
||||
body[drive='mounting'] .dialog-container .filelist-panel,
|
||||
body[drive='error'] .dialog-container .filelist-panel,
|
||||
body[unformatted] .dialog-container .filelist-panel {
|
||||
/* Hide file list when Drive is not mounted.
|
||||
Use opacity to avoid manual resizing.*/
|
||||
body[drive='unmounted'] .dialog-container #list-container,
|
||||
body[drive='mounting'] .dialog-container #list-container,
|
||||
body[drive='error'] .dialog-container #list-container,
|
||||
body[unformatted] .dialog-container #list-container {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#format-panel > * {
|
||||
align-items: center;
|
||||
display: none;
|
||||
flex-direction: row;
|
||||
height: 22px;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
body[unformatted] #format-panel > .error,
|
||||
#format-panel > #format-button {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.buttonbar > * {
|
||||
position: relative;
|
||||
}
|
||||
|
@ -200,6 +200,7 @@ js_library("banner_controller") {
|
||||
"ui/banners:drive_low_space_banner",
|
||||
"ui/banners:drive_welcome_banner",
|
||||
"ui/banners:holding_space_welcome_banner",
|
||||
"ui/banners:invalid_usb_filesystem_banner",
|
||||
"ui/banners:local_disk_low_space_banner",
|
||||
"ui/banners:shared_with_crostini_pluginvm_banner",
|
||||
"ui/banners:trash_banner",
|
||||
|
@ -20,6 +20,7 @@ import {TAG_NAME as DriveLowSpaceBanner} from './ui/banners/drive_low_space_bann
|
||||
import {TAG_NAME as DriveOfflinePinningBannerTagName} from './ui/banners/drive_offline_pinning_banner.js';
|
||||
import {TAG_NAME as DriveWelcomeBannerTagName} from './ui/banners/drive_welcome_banner.js';
|
||||
import {TAG_NAME as HoldingSpaceWelcomeBannerTagName} from './ui/banners/holding_space_welcome_banner.js';
|
||||
import {TAG_NAME as InvalidUSBFileSystemBanner} from './ui/banners/invalid_usb_filesystem_banner.js';
|
||||
import {TAG_NAME as LocalDiskLowSpaceBannerTagName} from './ui/banners/local_disk_low_space_banner.js';
|
||||
import {TAG_NAME as PhotosWelcomeBannerTagName} from './ui/banners/photos_welcome_banner.js';
|
||||
import {TAG_NAME as SharedWithCrostiniPluginVmBanner} from './ui/banners/shared_with_crostini_pluginvm_banner.js';
|
||||
@ -269,6 +270,7 @@ export class BannerController extends EventTarget {
|
||||
PhotosWelcomeBannerTagName,
|
||||
]);
|
||||
this.setStateBannersInOrder([
|
||||
InvalidUSBFileSystemBanner,
|
||||
SharedWithCrostiniPluginVmBanner,
|
||||
TrashBannerTagName,
|
||||
]);
|
||||
@ -307,6 +309,13 @@ export class BannerController extends EventTarget {
|
||||
this.volumeSizeStats_[this.currentVolume_.volumeId].remainingSize
|
||||
})
|
||||
});
|
||||
|
||||
// Register a custom filter that checks if the removable device has an
|
||||
// error and show the invalid USB file system banner.
|
||||
this.registerCustomBannerFilter_(InvalidUSBFileSystemBanner, {
|
||||
shouldShow: () => !!(this.currentVolume_ && this.currentVolume_.error),
|
||||
context: () => ({error: this.currentVolume_.error}),
|
||||
});
|
||||
}
|
||||
|
||||
for (const banner of this.warningBanners_) {
|
||||
|
@ -445,20 +445,8 @@ export class MainWindowComponent {
|
||||
null;
|
||||
|
||||
// Update unformatted volume status.
|
||||
if (newVolumeInfo && newVolumeInfo.error) {
|
||||
this.ui_.element.setAttribute('unformatted', '');
|
||||
|
||||
if (newVolumeInfo.error ===
|
||||
VolumeManagerCommon.VolumeError.UNSUPPORTED_FILESYSTEM) {
|
||||
this.ui_.formatPanelError.textContent =
|
||||
str('UNSUPPORTED_FILESYSTEM_WARNING');
|
||||
} else {
|
||||
this.ui_.formatPanelError.textContent =
|
||||
str('UNKNOWN_FILESYSTEM_WARNING');
|
||||
}
|
||||
} else {
|
||||
this.ui_.element.removeAttribute('unformatted');
|
||||
}
|
||||
const unformatted = !!(newVolumeInfo && newVolumeInfo.error);
|
||||
this.ui_.element.toggleAttribute('unformatted', /*force=*/ unformatted);
|
||||
|
||||
if (event.newDirEntry) {
|
||||
this.ui_.locationLine.show(event.newDirEntry);
|
||||
|
@ -26,6 +26,7 @@ html_to_js("web_components") {
|
||||
"drive_welcome_banner.js",
|
||||
"educational_banner.js",
|
||||
"holding_space_welcome_banner.js",
|
||||
"invalid_usb_filesystem_banner.js",
|
||||
"local_disk_low_space_banner.js",
|
||||
"photos_welcome_banner.js",
|
||||
"shared_with_crostini_pluginvm_banner.js",
|
||||
@ -50,6 +51,7 @@ js_type_check("closure_compile_jsmodules") {
|
||||
":drive_welcome_banner",
|
||||
":educational_banner",
|
||||
":holding_space_welcome_banner",
|
||||
":invalid_usb_filesystem_banner",
|
||||
":local_disk_low_space_banner",
|
||||
":photos_welcome_banner",
|
||||
":shared_with_crostini_pluginvm_banner",
|
||||
@ -68,6 +70,15 @@ js_library("holding_space_welcome_banner") {
|
||||
]
|
||||
}
|
||||
|
||||
js_library("invalid_usb_filesystem_banner") {
|
||||
deps = [
|
||||
":state_banner",
|
||||
"//ui/file_manager/file_manager/common/js:util",
|
||||
"//ui/file_manager/file_manager/common/js:volume_manager_types",
|
||||
"//ui/file_manager/file_manager/externs:banner",
|
||||
]
|
||||
}
|
||||
|
||||
js_library("local_disk_low_space_banner") {
|
||||
deps = [
|
||||
":warning_banner",
|
||||
|
6
ui/file_manager/file_manager/foreground/js/ui/banners/invalid_usb_filesystem_banner.html
Normal file
6
ui/file_manager/file_manager/foreground/js/ui/banners/invalid_usb_filesystem_banner.html
Normal file
@ -0,0 +1,6 @@
|
||||
<state-banner>
|
||||
<span slot="text">$i18n{UNKNOWN_FILESYSTEM_WARNING}</span>
|
||||
<cr-button slot="extra-button" command="#format">
|
||||
$i18n{FORMAT_DEVICE_BUTTON_LABEL}
|
||||
</cr-button>
|
||||
</state-banner>
|
67
ui/file_manager/file_manager/foreground/js/ui/banners/invalid_usb_filesystem_banner.js
Normal file
67
ui/file_manager/file_manager/foreground/js/ui/banners/invalid_usb_filesystem_banner.js
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
||||
|
||||
import {str} from '../../../../common/js/util.js';
|
||||
import {VolumeManagerCommon} from '../../../../common/js/volume_manager_types.js';
|
||||
import {Banner} from '../../../../externs/banner.js';
|
||||
import {StateBanner} from './state_banner.js';
|
||||
|
||||
/**
|
||||
* The custom element tag name.
|
||||
* @type {string}
|
||||
*/
|
||||
export const TAG_NAME = 'invalid-usb-filesystem-banner';
|
||||
|
||||
/** @const {!HTMLTemplateElement} */
|
||||
const htmlTemplate = html`{__html_template__}`;
|
||||
|
||||
/**
|
||||
* A banner that shows is a removable device is plugged in and it has a
|
||||
* filesystem that is either unknown or unsupported. It includes an action
|
||||
* button for the user to format the device.
|
||||
*/
|
||||
export class InvalidUSBFileSystemBanner extends StateBanner {
|
||||
/**
|
||||
* Returns the HTML template for this banner.
|
||||
* @returns {!Node}
|
||||
*/
|
||||
getTemplate() {
|
||||
return htmlTemplate.content.cloneNode(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only show the banner when the user has navigated to the Removable root type
|
||||
* this is used in conjunction with a custom filter to ensure only removable
|
||||
* roots with errors are shown the banner.
|
||||
* @returns {!Array<!Banner.AllowedVolume>}
|
||||
*/
|
||||
allowedVolumes() {
|
||||
return [{root: VolumeManagerCommon.RootType.REMOVABLE}];
|
||||
}
|
||||
|
||||
/**
|
||||
* When the custom filter shows this banner in the controller, it passes the
|
||||
* context to the banner. This is used to identify if the device has an
|
||||
* unsupported OR unknown file system.
|
||||
* @param {!Object} context The device error of the removable device.
|
||||
*/
|
||||
onFilteredContext(context) {
|
||||
if (!context || !context.error) {
|
||||
console.warn('Context not supplied or error key missing');
|
||||
return;
|
||||
}
|
||||
const text = this.shadowRoot.querySelector('span[slot="text"]');
|
||||
if (context.error ===
|
||||
VolumeManagerCommon.VolumeError.UNSUPPORTED_FILESYSTEM) {
|
||||
text.innerText = str('UNSUPPORTED_FILESYSTEM_WARNING');
|
||||
return;
|
||||
}
|
||||
|
||||
text.innerText = str('UNKNOWN_FILESYSTEM_WARNING');
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(TAG_NAME, InvalidUSBFileSystemBanner);
|
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import {assertInstanceof} from 'chrome://resources/js/assert.m.js';
|
||||
import {Command} from 'chrome://resources/js/cr/ui/command.m.js';
|
||||
import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
|
||||
|
||||
import {util} from '../../../../common/js/util.js';
|
||||
@ -68,18 +70,30 @@ export class StateBanner extends Banner {
|
||||
if (extraButton) {
|
||||
extraButton.addEventListener('click', (e) => {
|
||||
const href = extraButton.getAttribute('href');
|
||||
if (!href) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
const chromeOsSettingsSubpage =
|
||||
href.replace('chrome://os-settings/', '');
|
||||
href && href.replace('chrome://os-settings/', '');
|
||||
if (chromeOsSettingsSubpage && chromeOsSettingsSubpage !== href) {
|
||||
chrome.fileManagerPrivate.openSettingsSubpage(
|
||||
chromeOsSettingsSubpage);
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
const commandName = extraButton.getAttribute('command');
|
||||
if (commandName) {
|
||||
const command =
|
||||
assertInstanceof(document.querySelector(commandName), Command);
|
||||
// Unit tests don't enclose a StateBanner inside a concrete banner,
|
||||
// so we want to ensure the event is appropriately dispatched from the
|
||||
// outer scope otherwise it won't bubble up to the commands.
|
||||
let bannerInstance = this;
|
||||
const parentBanner = this.getRootNode() && this.getRootNode().host;
|
||||
if (parentBanner && parentBanner instanceof StateBanner) {
|
||||
bannerInstance = parentBanner;
|
||||
}
|
||||
command.execute(bannerInstance);
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
util.visitURL(extraButton.getAttribute('href'));
|
||||
e.preventDefault();
|
||||
});
|
||||
|
@ -2,8 +2,12 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import {decorate} from 'chrome://resources/js/cr/ui.m.js';
|
||||
import {Command} from 'chrome://resources/js/cr/ui/command.m.js';
|
||||
import {assertEquals} from 'chrome://test/chai_assert.js';
|
||||
import {mockUtilVisitURL} from '../../../../common/js/mock_util.js';
|
||||
import {waitUntil} from '../../../../common/js/test_error_reporting.js';
|
||||
|
||||
|
||||
import {StateBanner} from './state_banner.js';
|
||||
|
||||
@ -116,3 +120,44 @@ export async function testChromeOsSettingsNoSubpageLink() {
|
||||
assertEquals(mockVisitURL.getURL(), osSettingsLink);
|
||||
mockVisitURL.restoreVisitURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an extra-button with a command triggers an Event of the correct
|
||||
* type.
|
||||
*/
|
||||
export async function testCommandsCanBeUsedForExtraButtons(done) {
|
||||
const html = `<command id="format">
|
||||
<state-banner>
|
||||
<span slot="text">Banner title</span>
|
||||
<button slot="extra-button" command="#format">
|
||||
Test Button
|
||||
</button>
|
||||
</state-banner>
|
||||
`;
|
||||
document.body.innerHTML = html;
|
||||
decorate('command', Command);
|
||||
|
||||
// Add a listener to wait for the #format command to be received and keep
|
||||
// track of the event it received. Given the actual command is not properly
|
||||
// setup in the unittest environment, the event bubbles up to the body and
|
||||
// we can listen for it there.
|
||||
let commandReceived = false;
|
||||
let commandEvent = null;
|
||||
document.body.addEventListener('command', (e) => {
|
||||
commandReceived = true;
|
||||
commandEvent = e;
|
||||
});
|
||||
|
||||
// Click the extra button with a command associated with it.
|
||||
stateBanner =
|
||||
/** @type{!StateBanner} */ (document.body.querySelector('state-banner'));
|
||||
stateBanner.querySelector('[slot="extra-button"]').click();
|
||||
|
||||
// Wait until the command has been received.
|
||||
await waitUntil(() => commandReceived == true);
|
||||
|
||||
// Assert the event type received is a command.
|
||||
assertEquals(commandEvent.type, 'command');
|
||||
|
||||
done();
|
||||
}
|
||||
|
@ -289,12 +289,6 @@ export class FileManagerUI {
|
||||
*/
|
||||
this.listContainer;
|
||||
|
||||
/**
|
||||
* @type {!HTMLElement}
|
||||
*/
|
||||
this.formatPanelError =
|
||||
queryRequiredElement('#format-panel > .error', this.element);
|
||||
|
||||
/**
|
||||
* @type {!MultiMenu}
|
||||
* @const
|
||||
|
@ -513,10 +513,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="format-panel">
|
||||
<div class="error"></div>
|
||||
<button id="format-button" command="#format"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="files-feedback-panels" visibleif="full-page">
|
||||
<xf-display-panel id="progress-panel" role="complementary"
|
||||
|
Reference in New Issue
Block a user