Commit 5c9cfe3d authored by Yuheng Huang's avatar Yuheng Huang Committed by Commit Bot

Settings: Add mechanism for preserving subpage search across navigations.

In <settings-subpage>, if preserve-search-term boolean is set,
settings-subpage will save the subpage input term to the URL,
similar to what settings page does to the settings search input.
When user hits the back button, it will preserve the search input
from the URL.

At this moment only
chrome://settings/siteData
and
chrome://settings/content/all
have this flag turned on since they are the only places of search
results link to another subpage.

Bug: 1049887
Change-Id: I92782876f2e0ac3167fce6c4980914b4dab9b249
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2210731
Commit-Queue: Yuheng Huang <yuhengh@chromium.org>
Reviewed-by: default avatardpapad <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#775844}
parent f1d8608b
...@@ -161,7 +161,8 @@ ...@@ -161,7 +161,8 @@
<template is="dom-if" route-path="/content/all" no-search> <template is="dom-if" route-path="/content/all" no-search>
<settings-subpage page-title="$i18n{siteSettingsAllSites}" <settings-subpage page-title="$i18n{siteSettingsAllSites}"
search-label="$i18n{siteSettingsAllSitesSearch}" search-label="$i18n{siteSettingsAllSitesSearch}"
search-term="{{searchFilter_}}"> search-term="{{searchFilter_}}"
preserve-search-term>
<all-sites filter="[[searchFilter_]]"></all-sites> <all-sites filter="[[searchFilter_]]"></all-sites>
</settings-subpage> </settings-subpage>
</template> </template>
...@@ -263,7 +264,8 @@ ...@@ -263,7 +264,8 @@
<template is="dom-if" route-path="/siteData" no-search> <template is="dom-if" route-path="/siteData" no-search>
<settings-subpage page-title="$i18n{siteSettingsCookieHeader}" <settings-subpage page-title="$i18n{siteSettingsCookieHeader}"
search-label="$i18n{siteSettingsCookieSearch}" search-label="$i18n{siteSettingsCookieSearch}"
search-term="{{siteDataFilter_}}"> search-term="{{siteDataFilter_}}"
preserve-search-term>
<site-data filter="[[siteDataFilter_]]" <site-data filter="[[siteDataFilter_]]"
focus-config="[[focusConfig_]]"> focus-config="[[focusConfig_]]">
</site-data> </site-data>
......
...@@ -48,6 +48,7 @@ js_library("settings_subpage") { ...@@ -48,6 +48,7 @@ js_library("settings_subpage") {
"//ui/webui/resources/js:find_shortcut_behavior", "//ui/webui/resources/js:find_shortcut_behavior",
"//ui/webui/resources/js:i18n_behavior", "//ui/webui/resources/js:i18n_behavior",
"//ui/webui/resources/js:load_time_data", "//ui/webui/resources/js:load_time_data",
"//ui/webui/resources/js:util",
"//ui/webui/resources/js/cr/ui:focus_without_ink", "//ui/webui/resources/js/cr/ui:focus_without_ink",
] ]
} }
...@@ -105,6 +106,7 @@ js_library("settings_subpage.m") { ...@@ -105,6 +106,7 @@ js_library("settings_subpage.m") {
"//ui/webui/resources/js:find_shortcut_behavior.m", "//ui/webui/resources/js:find_shortcut_behavior.m",
"//ui/webui/resources/js:i18n_behavior.m", "//ui/webui/resources/js:i18n_behavior.m",
"//ui/webui/resources/js:load_time_data.m", "//ui/webui/resources/js:load_time_data.m",
"//ui/webui/resources/js:util.m",
"//ui/webui/resources/js/cr/ui:focus_without_ink.m", "//ui/webui/resources/js/cr/ui:focus_without_ink.m",
] ]
extra_deps = [ ":settings_subpage_module" ] extra_deps = [ ":settings_subpage_module" ]
...@@ -152,6 +154,7 @@ polymer_modulizer("settings_subpage") { ...@@ -152,6 +154,7 @@ polymer_modulizer("settings_subpage") {
"third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior.html|IronResizableBehavior", "third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior.html|IronResizableBehavior",
"ui/webui/resources/html/assert.html|assert", "ui/webui/resources/html/assert.html|assert",
"ui/webui/resources/html/polymer.html|html, Polymer, afterNextRender", "ui/webui/resources/html/polymer.html|html, Polymer, afterNextRender",
"ui/webui/resources/html/util.html|listenOnce",
] ]
namespace_rewrites = settings_namespace_rewrites + namespace_rewrites = settings_namespace_rewrites +
[ "Polymer.IronResizableBehavior|IronResizableBehavior" ] [ "Polymer.IronResizableBehavior|IronResizableBehavior" ]
......
...@@ -5,8 +5,10 @@ ...@@ -5,8 +5,10 @@
<link rel="import" href="chrome://resources/cr_elements/cr_search_field/cr_search_field.html"> <link rel="import" href="chrome://resources/cr_elements/cr_search_field/cr_search_field.html">
<link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html">
<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
<link rel="import" href="chrome://resources/html/assert.html">
<link rel="import" href="chrome://resources/html/find_shortcut_behavior.html"> <link rel="import" href="chrome://resources/html/find_shortcut_behavior.html">
<link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html">
<link rel="import" href="chrome://resources/html/util.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
<link rel="import" href="chrome://resources/polymer/v1_0/iron-resizable-behavior/iron-resizable-behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-resizable-behavior/iron-resizable-behavior.html">
<link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html">
......
...@@ -68,6 +68,14 @@ Polymer({ ...@@ -68,6 +68,14 @@ Polymer({
value: null, value: null,
}, },
/**
* Whether the subpage search term should be preserved across navigations.
*/
preserveSearchTerm: {
type: Boolean,
value: false,
},
/** @private */ /** @private */
active_: { active_: {
type: Boolean, type: Boolean,
...@@ -98,6 +106,53 @@ Polymer({ ...@@ -98,6 +106,53 @@ Polymer({
} }
}, },
/**
* @return {!Promise<!CrSearchFieldElement>}
* @private
*/
getSearchField_() {
let searchField = this.$$('cr-search-field');
if (searchField) {
return Promise.resolve(searchField);
}
return new Promise(resolve => {
listenOnce(this, 'dom-change', () => {
searchField = this.$$('cr-search-field');
resolve(assert(searchField));
});
});
},
/**
* Restore search field value from URL search param
* @private
*/
restoreSearchInput_() {
const searchField = this.$$('cr-search-field');
if (assert(searchField)) {
const urlSearchQuery =
settings.Router.getInstance().getQueryParameters().get(
'searchSubpage') ||
'';
this.searchTerm = urlSearchQuery;
searchField.setValue(urlSearchQuery);
}
},
/**
* Preserve search field value to URL search param
* @private
*/
preserveSearchInput_() {
const query = this.searchTerm;
const searchParams = query.length > 0 ?
new URLSearchParams('searchSubpage=' + encodeURIComponent(query)) :
undefined;
const currentRoute = settings.Router.getInstance().getCurrentRoute();
settings.Router.getInstance().navigateTo(currentRoute, searchParams);
},
/** Focuses the back button when page is loaded. */ /** Focuses the back button when page is loaded. */
initialFocus() { initialFocus() {
if (this.hideCloseButton) { if (this.hideCloseButton) {
...@@ -110,6 +165,9 @@ Polymer({ ...@@ -110,6 +165,9 @@ Polymer({
/** @protected */ /** @protected */
currentRouteChanged(route) { currentRouteChanged(route) {
this.active_ = this.getAttribute('route-path') == route.path; this.active_ = this.getAttribute('route-path') == route.path;
if (this.active_ && this.searchLabel && this.preserveSearchTerm) {
this.getSearchField_().then(() => this.restoreSearchInput_());
}
}, },
/** @private */ /** @private */
...@@ -156,7 +214,14 @@ Polymer({ ...@@ -156,7 +214,14 @@ Polymer({
/** @private */ /** @private */
onSearchChanged_(e) { onSearchChanged_(e) {
if (this.searchTerm === e.detail) {
return;
}
this.searchTerm = e.detail; this.searchTerm = e.detail;
if (this.preserveSearchTerm && this.active_) {
this.preserveSearchInput_();
}
}, },
/** @private */ /** @private */
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {Route, Router} from 'chrome://settings/settings.js'; import {Route, Router} from 'chrome://settings/settings.js';
import {setupPopstateListener} from 'chrome://test/settings/test_util.js'; import {setupPopstateListener} from 'chrome://test/settings/test_util.js';
import {flushTasks} from 'chrome://test/test_util.m.js'; import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js';
// clang-format on // clang-format on
...@@ -23,6 +23,9 @@ suite('SettingsSubpage', function() { ...@@ -23,6 +23,9 @@ suite('SettingsSubpage', function() {
testRoutes.SYNC = testRoutes.PEOPLE.createChild('/syncSetup'); testRoutes.SYNC = testRoutes.PEOPLE.createChild('/syncSetup');
testRoutes.PRIVACY = testRoutes.BASIC.createSection('/privacy', 'privacy'); testRoutes.PRIVACY = testRoutes.BASIC.createSection('/privacy', 'privacy');
testRoutes.CERTIFICATES = testRoutes.PRIVACY.createChild('/certificates'); testRoutes.CERTIFICATES = testRoutes.PRIVACY.createChild('/certificates');
testRoutes.SITE_DATA = testRoutes.BASIC.createChild('/siteData');
testRoutes.COOKIE_DETAILS =
testRoutes.SITE_DATA.createChild('/cookies/detail');
Router.resetInstanceForTesting(new Router(testRoutes)); Router.resetInstanceForTesting(new Router(testRoutes));
...@@ -31,6 +34,15 @@ suite('SettingsSubpage', function() { ...@@ -31,6 +34,15 @@ suite('SettingsSubpage', function() {
PolymerTest.clearBody(); PolymerTest.clearBody();
}); });
function createSettingsSubpageWithPreserveSearchTerm() {
const subpage = document.createElement('settings-subpage');
subpage.searchLabel = 'label';
subpage.preserveSearchTerm = true;
subpage.setAttribute('route-path', testRoutes.SITE_DATA.path);
document.body.appendChild(subpage);
return subpage;
}
test('clear search (event)', function() { test('clear search (event)', function() {
const subpage = document.createElement('settings-subpage'); const subpage = document.createElement('settings-subpage');
// Having a searchLabel will create the cr-search-field. // Having a searchLabel will create the cr-search-field.
...@@ -61,6 +73,50 @@ suite('SettingsSubpage', function() { ...@@ -61,6 +73,50 @@ suite('SettingsSubpage', function() {
assertEquals(search.$.searchInput, search.root.activeElement); assertEquals(search.$.searchInput, search.root.activeElement);
}); });
test('preserve search result when back button is clicked', async () => {
// Load settings subpage.
Router.getInstance().navigateTo(testRoutes.SITE_DATA);
let subpage = createSettingsSubpageWithPreserveSearchTerm();
flush();
// Set search field.
let search = subpage.$$('cr-search-field');
assertTrue(!!search);
search.setValue('test');
assertEquals('test', search.getValue());
// Navigate to another subpage.
Router.getInstance().navigateTo(testRoutes.COOKIE_DETAILS);
subpage.remove();
// Go back to settings subpage, verify search field is restored.
Router.getInstance().navigateToPreviousRoute();
subpage = createSettingsSubpageWithPreserveSearchTerm();
await eventToPromise('popstate', window);
search = subpage.$$('cr-search-field');
assertTrue(!!search);
assertEquals('test', search.getValue());
// Go back to settings subpage, verify search field is empty
Router.getInstance().navigateToPreviousRoute();
subpage = createSettingsSubpageWithPreserveSearchTerm();
await eventToPromise('popstate', window);
search = subpage.$$('cr-search-field');
assertTrue(!!search);
assertEquals('', search.getValue());
});
test('preserve search result from URL input', async function() {
const params = new URLSearchParams();
params.append('searchSubpage', 'test');
Router.getInstance().navigateTo(testRoutes.SITE_DATA, params);
const subpage = createSettingsSubpageWithPreserveSearchTerm();
await flushTasks();
const search = subpage.$$('cr-search-field');
assertTrue(!!search);
assertEquals('test', search.getValue());
});
test('navigates to parent when there is no history', function() { test('navigates to parent when there is no history', function() {
// Pretend that we initially started on the CERTIFICATES route. // Pretend that we initially started on the CERTIFICATES route.
window.history.replaceState(undefined, '', testRoutes.CERTIFICATES.path); window.history.replaceState(undefined, '', testRoutes.CERTIFICATES.path);
...@@ -75,7 +131,7 @@ suite('SettingsSubpage', function() { ...@@ -75,7 +131,7 @@ suite('SettingsSubpage', function() {
assertEquals(testRoutes.PRIVACY, Router.getInstance().getCurrentRoute()); assertEquals(testRoutes.PRIVACY, Router.getInstance().getCurrentRoute());
}); });
test('navigates to any route via window.back()', function(done) { test('navigates to any route via window.back()', async () => {
Router.getInstance().navigateTo(testRoutes.BASIC); Router.getInstance().navigateTo(testRoutes.BASIC);
Router.getInstance().navigateTo(testRoutes.SYNC); Router.getInstance().navigateTo(testRoutes.SYNC);
assertEquals(testRoutes.SYNC, Router.getInstance().getCurrentRoute()); assertEquals(testRoutes.SYNC, Router.getInstance().getCurrentRoute());
...@@ -85,12 +141,10 @@ suite('SettingsSubpage', function() { ...@@ -85,12 +141,10 @@ suite('SettingsSubpage', function() {
subpage.$$('cr-icon-button').click(); subpage.$$('cr-icon-button').click();
window.addEventListener('popstate', function(event) { await eventToPromise('popstate', window);
assertEquals( assertEquals(
Router.getInstance().getRoutes().BASIC, Router.getInstance().getRoutes().BASIC,
Router.getInstance().getCurrentRoute()); Router.getInstance().getCurrentRoute());
done();
});
}); });
test('updates the title of the document when active', function() { test('updates the title of the document when active', function() {
......
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