Commit 1fcd56bd authored by scottchen's avatar scottchen Committed by Commit Bot

MD Extensions: Show messages for no extensions, and for no search results

This CL adds messages to tell users when there are no extensions installed, as well as when their search filtering yields no results.

BUG=729863
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:closure_compilation

Review-Url: https://codereview.chromium.org/2974583003
Cr-Commit-Position: refs/heads/master@{#486166}
parent 5cddc222
...@@ -12,6 +12,7 @@ per-file chromeos_strings.grdp=* ...@@ -12,6 +12,7 @@ per-file chromeos_strings.grdp=*
per-file chromium_strings.grd=* per-file chromium_strings.grd=*
per-file generated_resources.grd=* per-file generated_resources.grd=*
per-file google_chrome_strings.grd=* per-file google_chrome_strings.grd=*
per-file md_extensions_strings.grdp=*
per-file media_router_strings.grdp*=apacible@chromium.org per-file media_router_strings.grdp*=apacible@chromium.org
per-file media_router_strings.grdp*=imcheng@chromium.org per-file media_router_strings.grdp*=imcheng@chromium.org
......
...@@ -94,6 +94,12 @@ ...@@ -94,6 +94,12 @@
<message name="IDS_MD_EXTENSIONS_LOAD_ERROR_RETRY" desc="The text on the button to retry loading an unpacked extension after a failed load."> <message name="IDS_MD_EXTENSIONS_LOAD_ERROR_RETRY" desc="The text on the button to retry loading an unpacked extension after a failed load.">
Retry Retry
</message> </message>
<message name="IDS_MD_EXTENSIONS_NO_INSTALLED_ITEMS" desc="The message shown to the user on the Extensions settings page when there are no extensions or apps installed.">
Find extensions and apps in the <ph name="BEGIN_LINK">&lt;a target="_blank" href="https://chrome.google.com/webstore/category/extensions"&gt;</ph>Chrome Web Store<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
</message>
<message name="IDS_MD_EXTENSIONS_NO_SEARCH_RESULTS" desc="The message shown to the user when a search on the Extensions settings page has no matching results.">
No search results found
</message>
<message name="IDS_MD_EXTENSIONS_PACK_DIALOG_TITLE" desc="The title of the dialog to pack an extension."> <message name="IDS_MD_EXTENSIONS_PACK_DIALOG_TITLE" desc="The title of the dialog to pack an extension.">
Pack extension Pack extension
</message> </message>
......
<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
<link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/cr.html">
<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
...@@ -8,7 +9,7 @@ ...@@ -8,7 +9,7 @@
<dom-module id="extensions-item-list"> <dom-module id="extensions-item-list">
<template> <template>
<style> <style include="cr-hidden-style">
:host { :host {
@apply(--layout-vertical); @apply(--layout-vertical);
} }
...@@ -18,11 +19,29 @@ ...@@ -18,11 +19,29 @@
margin-top: 18px; margin-top: 18px;
} }
.empty-list-message {
align-items: center;
color: #6e6e6e;
display: flex;
flex: 1;
font-weight: 500;
justify-content: center;
}
.wrapper { .wrapper {
padding: 6px; padding: 6px;
} }
</style> </style>
<iron-list id="list" items="[[computeShownItems_(items.*, filter)]]" <div id="no-items" class="empty-list-message"
hidden$="[[!shouldShowEmptyItemsMessage_(items.length)]]">
<span>$i18nRaw{noExtensionsOrApps}</span>
</div>
<div id="no-search-results" class="empty-list-message"
hidden$="[[!shouldShowEmptySearchMessage_(shownItems_.length)]]">
<span>$i18n{noSearchResults}</span>
</div>
<iron-list id="list" items="[[shownItems_]]"
as="item" grid> as="item" grid>
<template> <template>
<div class="wrapper"> <div class="wrapper">
......
...@@ -21,6 +21,12 @@ cr.define('extensions', function() { ...@@ -21,6 +21,12 @@ cr.define('extensions', function() {
}, },
filter: String, filter: String,
/** @private {Array<!chrome.developerPrivate.ExtensionInfo>} */
shownItems_: {
type: Array,
computed: 'computeShownItems_(items.*, filter)',
}
}, },
listeners: { listeners: {
...@@ -73,6 +79,15 @@ cr.define('extensions', function() { ...@@ -73,6 +79,15 @@ cr.define('extensions', function() {
return item.name.toLowerCase().includes(this.filter.toLowerCase()); return item.name.toLowerCase().includes(this.filter.toLowerCase());
}, this); }, this);
}, },
shouldShowEmptyItemsMessage_: function() {
return this.items.length === 0;
},
shouldShowEmptySearchMessage_: function() {
return !this.shouldShowEmptyItemsMessage_() &&
this.shownItems_.length === 0;
},
}); });
return { return {
......
...@@ -86,8 +86,7 @@ ...@@ -86,8 +86,7 @@
selected="items-list"> selected="items-list">
<extensions-item-list id="items-list" items="[[extensions]]" <extensions-item-list id="items-list" items="[[extensions]]"
delegate="[[itemDelegate]]" in-dev-mode="[[inDevMode]]" delegate="[[itemDelegate]]" in-dev-mode="[[inDevMode]]"
filter="[[filter]]" filter="[[filter]]" hidden$="[[!didInitPage_]]">
hidden$="[[computeListHidden_(extensions, extensions.splices, apps, apps.splices)]]">
</extensions-item-list> </extensions-item-list>
<extensions-detail-view id="details-view" delegate="[[itemDelegate]]" <extensions-detail-view id="details-view" delegate="[[itemDelegate]]"
in-dev-mode="[[inDevMode]]" data="[[detailViewItem_]]" in-dev-mode="[[inDevMode]]" data="[[detailViewItem_]]"
......
...@@ -98,6 +98,15 @@ cr.define('extensions', function() { ...@@ -98,6 +98,15 @@ cr.define('extensions', function() {
return []; return [];
}, },
}, },
/**
* Prevents page content from showing before data is first loaded.
* @private
*/
didInitPage_: {
type: Boolean,
value: false,
},
}, },
listeners: { listeners: {
...@@ -171,6 +180,7 @@ cr.define('extensions', function() { ...@@ -171,6 +180,7 @@ cr.define('extensions', function() {
* the user visits chrome://extensions/?id=..., we land on the proper page. * the user visits chrome://extensions/?id=..., we land on the proper page.
*/ */
initPage: function() { initPage: function() {
this.didInitPage_ = true;
this.changePage(this.navigationHelper_.getCurrentPage(), true); this.changePage(this.navigationHelper_.getCurrentPage(), true);
}, },
...@@ -235,14 +245,6 @@ cr.define('extensions', function() { ...@@ -235,14 +245,6 @@ cr.define('extensions', function() {
this.apps[this.getIndexInList_('apps', id)]; this.apps[this.getIndexInList_('apps', id)];
}, },
/**
* @return {boolean} Whether the list should be visible.
* @private
*/
computeListHidden_: function() {
return this.$['items-list'].items.length == 0;
},
/** /**
* Creates and adds a new extensions-item element to the list, inserting it * Creates and adds a new extensions-item element to the list, inserting it
* into its sorted position in the relevant section. * into its sorted position in the relevant section.
......
...@@ -115,6 +115,10 @@ content::WebUIDataSource* CreateMdExtensionsSource() { ...@@ -115,6 +115,10 @@ content::WebUIDataSource* CreateMdExtensionsSource() {
source->AddLocalizedString("sidebarApps", IDS_MD_EXTENSIONS_SIDEBAR_APPS); source->AddLocalizedString("sidebarApps", IDS_MD_EXTENSIONS_SIDEBAR_APPS);
source->AddLocalizedString("sidebarExtensions", source->AddLocalizedString("sidebarExtensions",
IDS_MD_EXTENSIONS_SIDEBAR_EXTENSIONS); IDS_MD_EXTENSIONS_SIDEBAR_EXTENSIONS);
source->AddLocalizedString("noExtensionsOrApps",
IDS_MD_EXTENSIONS_NO_INSTALLED_ITEMS);
source->AddLocalizedString("noSearchResults",
IDS_MD_EXTENSIONS_NO_SEARCH_RESULTS);
source->AddLocalizedString("dropToInstall", source->AddLocalizedString("dropToInstall",
IDS_EXTENSIONS_INSTALL_DROP_TARGET); IDS_EXTENSIONS_INSTALL_DROP_TARGET);
source->AddLocalizedString("errorsPageHeading", source->AddLocalizedString("errorsPageHeading",
......
...@@ -194,6 +194,21 @@ TEST_F('CrExtensionsBrowserTest', 'ExtensionItemList', function() { ...@@ -194,6 +194,21 @@ TEST_F('CrExtensionsBrowserTest', 'ExtensionItemList', function() {
assert(extension_item_list_tests.TestNames.ItemListFiltering)).run(); assert(extension_item_list_tests.TestNames.ItemListFiltering)).run();
}); });
TEST_F('CrExtensionsBrowserTest', 'ExtensionItemListEmpty', function() {
extension_item_list_tests.registerTests();
mocha.grep(assert(extension_item_list_tests.TestNames.ItemListNoItemsMsg))
.run();
});
TEST_F(
'CrExtensionsBrowserTest', 'ExtensionItemListNoSearchResults', function() {
extension_item_list_tests.registerTests();
mocha
.grep(assert(
extension_item_list_tests.TestNames.ItemListNoSearchResultsMsg))
.run();
});
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Extension Load Error Tests // Extension Load Error Tests
......
...@@ -7,12 +7,15 @@ cr.define('extension_item_list_tests', function() { ...@@ -7,12 +7,15 @@ cr.define('extension_item_list_tests', function() {
/** @enum {string} */ /** @enum {string} */
var TestNames = { var TestNames = {
ItemListFiltering: 'item list filtering', ItemListFiltering: 'item list filtering',
ItemListNoItemsMsg: 'empty item list',
ItemListNoSearchResultsMsg: 'empty item list filtering results',
}; };
function registerTests() { function registerTests() {
suite('ExtensionItemListTest', function() { suite('ExtensionItemListTest', function() {
/** @type {extensions.ItemList} */ /** @type {extensions.ItemList} */
var itemList; var itemList;
var testVisible;
suiteSetup(function() { suiteSetup(function() {
return PolymerTest.importHtml('chrome://extensions/item-list.html'); return PolymerTest.importHtml('chrome://extensions/item-list.html');
...@@ -22,6 +25,8 @@ cr.define('extension_item_list_tests', function() { ...@@ -22,6 +25,8 @@ cr.define('extension_item_list_tests', function() {
setup(function() { setup(function() {
PolymerTest.clearBody(); PolymerTest.clearBody();
itemList = new extensions.ItemList(); itemList = new extensions.ItemList();
testVisible = extension_test_util.testVisible.bind(null, itemList);
var createExt = extension_test_util.createExtensionInfo; var createExt = extension_test_util.createExtensionInfo;
var items = var items =
[createExt({name: 'Alpha', id: 'a'.repeat(32)}), [createExt({name: 'Alpha', id: 'a'.repeat(32)}),
...@@ -60,6 +65,24 @@ cr.define('extension_item_list_tests', function() { ...@@ -60,6 +65,24 @@ cr.define('extension_item_list_tests', function() {
itemList.filter = ''; itemList.filter = '';
expectEquals(3, ironList.items.length); expectEquals(3, ironList.items.length);
}); });
test(assert(TestNames.ItemListNoItemsMsg), function() {
testVisible('#no-items', false);
testVisible('#no-search-results', false);
itemList.items = [];
testVisible('#no-items', true);
testVisible('#no-search-results', false);
});
test(assert(TestNames.ItemListNoSearchResultsMsg), function() {
testVisible('#no-items', false);
testVisible('#no-search-results', false);
itemList.filter = 'non-existent name';
testVisible('#no-items', false);
testVisible('#no-search-results', true);
});
}); });
} }
......
...@@ -78,7 +78,6 @@ cr.define('extension_manager_tests', function() { ...@@ -78,7 +78,6 @@ cr.define('extension_manager_tests', function() {
}); });
test(assert(TestNames.ItemListVisibility), function() { test(assert(TestNames.ItemListVisibility), function() {
var testVisible = extension_test_util.testVisible.bind(null, manager);
var extension = getDataByName(manager.extensions, 'My extension 1'); var extension = getDataByName(manager.extensions, 'My extension 1');
var listHasItemWithName = function(name) { var listHasItemWithName = function(name) {
...@@ -88,17 +87,14 @@ cr.define('extension_manager_tests', function() { ...@@ -88,17 +87,14 @@ cr.define('extension_manager_tests', function() {
}; };
expectEquals(manager.extensions, manager.$['items-list'].items); expectEquals(manager.extensions, manager.$['items-list'].items);
testVisible('#items-list', true);
expectTrue(listHasItemWithName('My extension 1')); expectTrue(listHasItemWithName('My extension 1'));
manager.removeItem(extension); manager.removeItem(extension);
Polymer.dom.flush(); Polymer.dom.flush();
testVisible('#items-list', false);
expectFalse(listHasItemWithName('My extension 1')); expectFalse(listHasItemWithName('My extension 1'));
manager.addItem(extension); manager.addItem(extension);
Polymer.dom.flush(); Polymer.dom.flush();
testVisible('#items-list', true);
expectTrue(listHasItemWithName('My extension 1')); expectTrue(listHasItemWithName('My extension 1'));
}); });
......
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