Commit e08766ea authored by Edward Jung's avatar Edward Jung Committed by Commit Bot

Redesign of the chrome://flags page

+ Improve responsive design for mobile devices. Larger touch targets.
+ Simplified the warning blurb.
+ Add in page flag search for better filtering, significantly better
search experience on mobile devices.
+ Add tab interface separating Available and Unavailable flags.
+ Uses updated modern colour scheme and typography.
+ Display Chrome version number on the page.

Bug: 622640
Change-Id: I55f1739ab12c4d28c7ace0f234fabb30577abf21
Reviewed-on: https://chromium-review.googlesource.com/693256Reviewed-by: 's avatarAlexei Svitkine <asvitkine@chromium.org>
Reviewed-by: 's avatarColin Blundell <blundell@chromium.org>
Reviewed-by: 's avatarBernhard Bauer <bauerb@chromium.org>
Reviewed-by: 's avatarEugene But <eugenebut@chromium.org>
Commit-Queue: Edward Jung <edwardjung@chromium.org>
Cr-Commit-Position: refs/heads/master@{#507085}
parent 95962589
......@@ -61,10 +61,9 @@ content::WebUIDataSource* CreateFlagsUIHTMLSource() {
content::WebUIDataSource* source =
content::WebUIDataSource::Create(chrome::kChromeUIFlagsHost);
source->AddLocalizedString(flags_ui::kFlagsLongTitle,
IDS_FLAGS_UI_LONG_TITLE);
source->AddLocalizedString(flags_ui::kFlagsTableTitle,
IDS_FLAGS_UI_TABLE_TITLE);
source->AddLocalizedString(flags_ui::kFlagsSearchPlaceholder,
IDS_FLAGS_UI_SEARCH_PLACEHOLDER);
source->AddLocalizedString(flags_ui::kFlagsTitle, IDS_FLAGS_UI_TITLE);
source->AddLocalizedString(flags_ui::kFlagsWarningHeader,
IDS_FLAGS_UI_WARNING_HEADER);
source->AddLocalizedString(flags_ui::kFlagsBlurb, IDS_FLAGS_UI_WARNING_TEXT);
......@@ -72,8 +71,10 @@ content::WebUIDataSource* CreateFlagsUIHTMLSource() {
IDS_FLAGS_UI_PROMOTE_BETA_CHANNEL);
source->AddLocalizedString(flags_ui::kChannelPromoDev,
IDS_FLAGS_UI_PROMOTE_DEV_CHANNEL);
source->AddLocalizedString(flags_ui::kFlagsUnsupportedTableTitle,
IDS_FLAGS_UI_UNSUPPORTED_TABLE_TITLE);
source->AddLocalizedString(flags_ui::kFlagsSupportedTitle,
IDS_FLAGS_UI_SUPPORTED_TITLE);
source->AddLocalizedString(flags_ui::kFlagsUnsupportedTitle,
IDS_FLAGS_UI_UNSUPPORTED_TITLE);
source->AddLocalizedString(flags_ui::kFlagsNotSupported,
IDS_FLAGS_UI_NOT_AVAILABLE);
source->AddLocalizedString(flags_ui::kFlagsRestartNotice,
......@@ -82,8 +83,11 @@ content::WebUIDataSource* CreateFlagsUIHTMLSource() {
IDS_FLAGS_UI_RELAUNCH_BUTTON);
source->AddLocalizedString(flags_ui::kResetAllButton,
IDS_FLAGS_UI_RESET_ALL_BUTTON);
source->AddLocalizedString(flags_ui::kFlagsNoMatches,
IDS_FLAGS_UI_NO_MATCHES);
source->AddLocalizedString(flags_ui::kDisable, IDS_FLAGS_UI_DISABLE);
source->AddLocalizedString(flags_ui::kEnable, IDS_FLAGS_UI_ENABLE);
source->AddString(flags_ui::kVersion, version_info::GetVersionNumber());
#if defined(OS_CHROMEOS)
if (!user_manager::UserManager::Get()->IsCurrentUserOwner() &&
......
......@@ -21,13 +21,15 @@ const char kChannelPromoDev[] = "channelPromoDev";
const char kDisable[] = "disable";
const char kEnable[] = "enable";
const char kFlagsBlurb[] = "flagsBlurb";
const char kFlagsLongTitle[] = "flagsLongTitle";
const char kFlagsTitle[] = "flagsTitle";
const char kFlagsNoExperimentsAvailable[] = "flagsNoExperimentsAvailable";
const char kFlagsNoMatches[] = "flagsNoMatches";
const char kFlagsNotSupported[] = "flagsNotSupported";
const char kFlagsRestartButton[] = "flagsRestartButton";
const char kFlagsRestartNotice[] = "flagsRestartNotice";
const char kFlagsTableTitle[] = "flagsTableTitle";
const char kFlagsUnsupportedTableTitle[] = "flagsUnsupportedTableTitle";
const char kFlagsSearchPlaceholder[] = "flagsSearchPlaceholder";
const char kFlagsSupportedTitle[] = "flagsSupportedTitle";
const char kFlagsUnsupportedTitle[] = "flagsUnsupportedTitle";
const char kFlagsWarningHeader[] = "flagsWarningHeader";
const char kNeedsRestart[] = "needsRestart";
const char kOwnerWarning[] = "ownerWarning";
......@@ -38,5 +40,6 @@ const char kShowDevChannelPromotion[] = "showDevChannelPromotion";
const char kShowOwnerWarning[] = "showOwnerWarning";
const char kSupportedFeatures[] = "supportedFeatures";
const char kUnsupportedFeatures[] = "unsupportedFeatures";
const char kVersion[] = "version";
} // namespace flags_ui
......@@ -25,13 +25,15 @@ extern const char kChannelPromoDev[];
extern const char kDisable[];
extern const char kEnable[];
extern const char kFlagsBlurb[];
extern const char kFlagsLongTitle[];
extern const char kFlagsTitle[];
extern const char kFlagsNoExperimentsAvailable[];
extern const char kFlagsNoMatches[];
extern const char kFlagsNotSupported[];
extern const char kFlagsRestartButton[];
extern const char kFlagsRestartNotice[];
extern const char kFlagsTableTitle[];
extern const char kFlagsUnsupportedTableTitle[];
extern const char kFlagsSearchPlaceholder[];
extern const char kFlagsSupportedTitle[];
extern const char kFlagsUnsupportedTitle[];
extern const char kFlagsWarningHeader[];
extern const char kNeedsRestart[];
extern const char kOwnerWarning[];
......@@ -42,6 +44,7 @@ extern const char kShowDevChannelPromotion[];
extern const char kShowOwnerWarning[];
extern const char kSupportedFeatures[];
extern const char kUnsupportedFeatures[];
extern const char kVersion[];
} // namespace flags_ui
......
This diff is collapsed.
This diff is collapsed.
......@@ -48,9 +48,30 @@ function renderTemplate(experimentalFeaturesData) {
elements[i].onclick = restartBrowser;
}
// Toggling of experiment description overflow content.
elements = document.querySelectorAll('.experiment .flex:first-child');
for (var i = 0; i < elements.length; ++i) {
elements[i].addEventListener('click', function(e) {
this.classList.toggle('expand');
});
}
// Tab panel selection.
var tabEls = document.getElementsByClassName('tab');
for (var i = 0; i < tabEls.length; ++i) {
tabEls[i].addEventListener('click', function(e) {
e.preventDefault();
for (var j= 0; j < tabEls.length; ++j) {
tabEls[j].parentNode.classList.toggle('selected', tabEls[j] == this);
}
});
}
$('experiment-reset-all').onclick = resetAllFlags;
highlightReferencedFlag();
var search = new FlagSearch();
search.init();
}
/**
......@@ -68,6 +89,12 @@ function highlightReferencedFlag() {
document.querySelector('.referenced').classList.remove('referenced');
// Highlight the referenced element.
el.classList.add('referenced');
// Switch to unavailable tab if the flag is in this section.
if ($('tab-content-unavailable').contains(el)) {
$('tab-available').parentNode.classList.remove('selected');
$('tab-unavailable').parentNode.classList.add('selected');
}
el.scrollIntoView();
}
}
......@@ -177,7 +204,156 @@ function handleSelectExperimentalFeatureChoice(node, index) {
requestExperimentalFeaturesData();
}
// Get data and have it displayed upon loading.
/**
* Handles in page searching. Matches against the experiment flag name.
*/
var FlagSearch = function() {
this.experiments_ = [];
this.unavailableExperiments_ = [];
this.searchBox_ = $('search');
this.noMatchMsg_ = document.querySelectorAll('.no-match');
this.searchIntervalId_ = null;
};
// Delay in ms following a keypress, before a search is made.
FlagSearch.SEARCH_DEBOUNCE_TIME_MS = 150;
FlagSearch.prototype = {
/**
* Initialises the in page search. Addings searchbox listeners and
* collates the permalinks used for string matching.
*/
init: function() {
this.experiments_ =
document.querySelectorAll('#tab-content-available .permalink');
this.unavailableExperiments_ =
document.querySelectorAll('#tab-content-unavailable .permalink');
this.searchBox_.addEventListener('keyup', this.debounceSearch.bind(this));
document.querySelector('.clear-search').addEventListener('click',
this.clearSearch.bind(this));
this.searchBox_.focus();
},
/**
* Clears a search showing all experiments.
*/
clearSearch: function() {
this.searchBox_.value = '';
this.doSearch();
},
/**
* Reset existing highlights on an element.
* @param {HTMLElement} el The element to remove all highlighted mark up on.
* @param {string} flag The flag name to reset the element's textContent to.
*/
resetHighlights: function(el, flag) {
if (el.children) {
el.textContent = flag;
}
},
/**
* Highlights the search term within the permalink flag name.
* @param {string} searchTerm Search term user entered.
* @param {HTMLElement} el The permalink node where the search tern occurs.
* @return {boolean} Whether there was a match.
*/
highlightMatches: function(searchTerm, el) {
// Experiment container.
var parentEl = el.parentNode.parentNode.parentNode;
var flag = el.textContent.toLowerCase();
var match = flag.indexOf(searchTerm);
parentEl.classList.toggle('hidden', match == -1);
if (match == -1) {
this.resetHighlights(el, flag);
return false;
}
if (searchTerm != '') {
// Clear all nodes.
el.textContent = '';
if (match > 0) {
var textNodePrefix =
document.createTextNode(flag.substring(0, match));
el.appendChild(textNodePrefix);
}
var matchEl = document.createElement('mark');
matchEl.textContent = flag.substr(match, searchTerm.length);
el.appendChild(matchEl);
var matchSuffix = flag.substring(match + searchTerm.length);
if (matchSuffix) {
var textNodeSuffix = document.createTextNode(matchSuffix);
el.appendChild(textNodeSuffix);
}
} else {
this.resetHighlights(el, flag);
}
return true;
},
/**
* Performs a search against the permalinks.
* @param {Event} e
*/
doSearch: function(e) {
// Replace spaces with hyphens as flag names don't have spaces.
var searchTerm =
this.searchBox_.value.trim().toLowerCase().replace(/\s/, '-');
var matches = 0;
var unavailableMatches = 0;
if (searchTerm || searchTerm == '') {
document.body.classList.add('searching');
// Available experiments
for (var i = 0, j = this.experiments_.length; i < j; i++) {
if (this.highlightMatches(searchTerm, this.experiments_[i])) {
matches++;
}
}
this.noMatchMsg_[0].classList.toggle('hidden', matches);
// Unavailable experiments
for (var i = 0, j = this.unavailableExperiments_.length; i < j; i++) {
if (this.highlightMatches(searchTerm,
this.unavailableExperiments_[i])) {
unavailableMatches++;
}
}
this.noMatchMsg_[1].classList.toggle('hidden', unavailableMatches);
}
this.searchIntervalId_ = null;
},
/**
* Debounces the search to improve performance and prevent too many searches
* from being initiated.
* @param {Event} e
*/
debounceSearch: function(e) {
// Don't search if the search term did not change.
if (this.searchValue_ == this.searchBox_.value) {
return;
}
if (this.searchIntervalId_) {
clearTimeout(this.searchIntervalId_);
}
this.searchIntervalId_ = setTimeout(this.doSearch.bind(this),
FlagSearch.SEARCH_DEBOUNCE_TIME_MS);
}
};
// Get and display the data upon loading.
document.addEventListener('DOMContentLoaded', requestExperimentalFeaturesData);
// Update the highlighted flag when the hash changes.
......
<?xml version="1.0" encoding="utf-8"?>
<grit-part>
<message name="IDS_FLAGS_UI_LONG_TITLE" desc="Long version of the title for the about:flags page.">
Careful, these experiments may bite
</message>
<message name="IDS_FLAGS_UI_TABLE_TITLE" desc="Title for the experiments table on the about:flags page.">
<message name="IDS_FLAGS_UI_TITLE" desc="Main title for the about:flags page.">
Experiments
</message>
<message name="IDS_FLAGS_UI_WARNING_HEADER" desc="Text right before the warning body text.">
WARNING
Warning: Experimental features ahead!
</message>
<message name="IDS_FLAGS_UI_WARNING_TEXT" desc="Text at the top of the about:flags page, warning users that the about:flags contents are volatile.">
These experimental features may change, break, or disappear at any time. We make absolutely no guarantees about what may happen if you turn one of these experiments on, and your browser may even spontaneously combust. Jokes aside, your browser may delete all your data, or your security and privacy could be compromised in unexpected ways. Any experiments you enable will be enabled for all users of this browser. Please proceed with caution.
By enabling these features, you could lose browser data or compromise your security or privacy. Enabled features apply to all users of this browser.
</message>
<message name="IDS_FLAGS_UI_PROMOTE_BETA_CHANNEL" desc="Shown on the about:flags page when we want to promote the Chrome beta channel">
Interested in cool new Chrome features? Try our beta channel at chrome.com/beta.
Interested in cool new Chrome features? Try our <ph name="BEGIN_LINK">&lt;a href="https://chrome.com/beta"&gt;</ph>beta channel<ph name="END_LINK">&lt;/a&gt;</ph>.
</message>
<message name="IDS_FLAGS_UI_PROMOTE_DEV_CHANNEL" desc="Shown on the about:flags page when we want to promote the Chrome developer channel">
Interested in cool new Chrome features? Try our dev channel at chrome.com/dev.
Interested in cool new Chrome features? Try our <ph name="BEGIN_LINK">&lt;a href="https://chrome.com/dev"&gt;</ph>dev channel<ph name="END_LINK">&lt;/a&gt;</ph>.
</message>
<message name="IDS_FLAGS_UI_SEARCH_PLACEHOLDER" desc="Search flags text box placeholder text on the about:flags page.">
Search flags
</message>
<message name="IDS_FLAGS_UI_DISABLE" desc="The link for disabling a labs experiment.">
Disable
......@@ -27,11 +27,17 @@
<message name="IDS_FLAGS_UI_RESET_ALL_BUTTON" desc="Text on a button that resets all experiments to their default state when clicked.">
Reset all to default
</message>
<message name="IDS_FLAGS_UI_UNSUPPORTED_TABLE_TITLE" desc="Title for the unsupported experiments table on the about:flags page. It lists all experiments that are not available on the current platform.">
Unavailable Experiments
<message name="IDS_FLAGS_UI_SUPPORTED_TITLE" desc="Title for the supported experiments on the about:flags page. It lists all experiments that are not available on the current platform.">
Available
</message>
<message name="IDS_FLAGS_UI_UNSUPPORTED_TITLE" desc="Title for the unsupported experiments on the about:flags page. It lists all experiments that are not available on the current platform.">
Unavailable
</message>
<message name="IDS_FLAGS_UI_NO_MATCHES" desc="Message shown when there are no search matches on the about:flags page">
No matching experiments
</message>
<message name="IDS_FLAGS_UI_NOT_AVAILABLE" desc="Shown instead of the Enable/Disable link, to let the user know that an experiment is not available on the current platform.">
Sorry, this experiment is not available on your platform.
Not available on your platform.
</message>
<if expr="not chromeos">
<message name="IDS_FLAGS_UI_RELAUNCH_BUTTON" desc="Text on a button that relaunches Chrome when clicked. ">
......
......@@ -38,10 +38,9 @@ web::WebUIIOSDataSource* CreateFlagsUIHTMLSource() {
web::WebUIIOSDataSource* source =
web::WebUIIOSDataSource::Create(kChromeUIFlagsHost);
source->AddLocalizedString(flags_ui::kFlagsLongTitle,
IDS_FLAGS_UI_LONG_TITLE);
source->AddLocalizedString(flags_ui::kFlagsTableTitle,
IDS_FLAGS_UI_TABLE_TITLE);
source->AddLocalizedString(flags_ui::kFlagsSearchPlaceholder,
IDS_FLAGS_UI_SEARCH_PLACEHOLDER);
source->AddLocalizedString(flags_ui::kFlagsTitle, IDS_FLAGS_UI_TITLE);
source->AddLocalizedString(flags_ui::kFlagsWarningHeader,
IDS_FLAGS_UI_WARNING_HEADER);
source->AddLocalizedString(flags_ui::kFlagsBlurb, IDS_FLAGS_UI_WARNING_TEXT);
......@@ -49,8 +48,10 @@ web::WebUIIOSDataSource* CreateFlagsUIHTMLSource() {
IDS_FLAGS_UI_PROMOTE_BETA_CHANNEL);
source->AddLocalizedString(flags_ui::kChannelPromoDev,
IDS_FLAGS_UI_PROMOTE_DEV_CHANNEL);
source->AddLocalizedString(flags_ui::kFlagsUnsupportedTableTitle,
IDS_FLAGS_UI_UNSUPPORTED_TABLE_TITLE);
source->AddLocalizedString(flags_ui::kFlagsSupportedTitle,
IDS_FLAGS_UI_SUPPORTED_TITLE);
source->AddLocalizedString(flags_ui::kFlagsUnsupportedTitle,
IDS_FLAGS_UI_UNSUPPORTED_TITLE);
source->AddLocalizedString(flags_ui::kFlagsNotSupported,
IDS_FLAGS_UI_NOT_AVAILABLE);
source->AddLocalizedString(flags_ui::kFlagsRestartNotice,
......@@ -59,8 +60,11 @@ web::WebUIIOSDataSource* CreateFlagsUIHTMLSource() {
IDS_FLAGS_UI_RELAUNCH_BUTTON);
source->AddLocalizedString(flags_ui::kResetAllButton,
IDS_FLAGS_UI_RESET_ALL_BUTTON);
source->AddLocalizedString(flags_ui::kFlagsNoMatches,
IDS_FLAGS_UI_NO_MATCHES);
source->AddLocalizedString(flags_ui::kDisable, IDS_FLAGS_UI_DISABLE);
source->AddLocalizedString(flags_ui::kEnable, IDS_FLAGS_UI_ENABLE);
source->AddString(flags_ui::kVersion, version_info::GetVersionNumber());
source->SetJsonPath("strings.js");
source->AddResourcePath(flags_ui::kFlagsJS, IDR_FLAGS_UI_FLAGS_JS);
......
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