Settings: Combine safetyCheck and privacy sections in re-design.
- Hide the "Safety check" menu entry
- Add a new |nest-under-section| attribute on <settings-section> and
leverage it in main_page_behavior.js to show multiple
<settings-section> instances at the same time.
- Update tests.
Bug: 1204457
Change-Id: Iddb6049343a58162ff1fcd3ca3999ebde7f5e101
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2893208
Reviewed-by: John Lee <johntlee@chromium.org>
Commit-Queue: dpapad <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#882916}
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
index 5fce87a..741d47b 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -98,7 +98,7 @@
<template is="dom-if" if="[[showPage_(pageVisibility.safetyCheck)]]"
restamp>
<settings-section page-title="$i18n{safetyCheckSectionTitle}"
- section="safetyCheck"
+ section="safetyCheck" nest-under-section="privacy"
id="safetyCheckSettingsSection">
<settings-safety-check-page prefs="{{prefs}}">
</settings-safety-check-page>
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js
index d1bb364b..fe6ea73 100644
--- a/chrome/browser/resources/settings/route.js
+++ b/chrome/browser/resources/settings/route.js
@@ -143,7 +143,11 @@
r.PRIVACY = r.BASIC.createSection('/privacy', 'privacy');
addPrivacyChildRoutes(r);
- r.SAFETY_CHECK = r.BASIC.createSection('/safetyCheck', 'safetyCheck');
+ if (loadTimeData.getBoolean('enableLandingPageRedesign')) {
+ r.SAFETY_CHECK = r.PRIVACY.createSection('/safetyCheck', 'safetyCheck');
+ } else {
+ r.SAFETY_CHECK = r.BASIC.createSection('/safetyCheck', 'safetyCheck');
+ }
}
// <if expr="not chromeos and not lacros">
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.html b/chrome/browser/resources/settings/settings_menu/settings_menu.html
index 2ecb987..eace8e27 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.html
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.html
@@ -111,12 +111,14 @@
<iron-icon icon="settings:assignment"></iron-icon>
$i18n{autofillPageTitle}
</a>
- <a role="menuitem" href="/safetyCheck"
- hidden="[[!pageVisibility.safetyCheck]]"
- id="safetyCheck">
- <iron-icon icon="settings20:safety-check"></iron-icon>
- $i18n{safetyCheckSectionTitle}
- </a>
+ <template is="dom-if" if="[[!enableLandingPageRedesign_]]">
+ <a role="menuitem" href="/safetyCheck"
+ hidden="[[!pageVisibility.safetyCheck]]"
+ id="safetyCheck">
+ <iron-icon icon="settings20:safety-check"></iron-icon>
+ $i18n{safetyCheckSectionTitle}
+ </a>
+ </template>
<a role="menuitem" href="/privacy"
hidden="[[!pageVisibility.privacy]]">
<iron-icon icon="cr:security"></iron-icon>
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.js b/chrome/browser/resources/settings/settings_menu/settings_menu.js
index a10c4d72..c1e430e 100644
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.js
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.js
@@ -13,13 +13,12 @@
import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
-import '../i18n_setup.js';
import '../icons.js';
import '../settings_shared_css.js';
import {assert} from 'chrome://resources/js/assert.m.js';
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
+import {loadTimeData} from '../i18n_setup.js';
import {PageVisibility} from '../page_visibility.js';
import {Route, RouteObserverBehavior, Router} from '../router.js';
@@ -42,6 +41,12 @@
* @type {!PageVisibility}
*/
pageVisibility: Object,
+
+ /** @private */
+ enableLandingPageRedesign_: {
+ type: Boolean,
+ value: () => loadTimeData.getBoolean('enableLandingPageRedesign'),
+ },
},
/** @param {!Route} newRoute */
diff --git a/chrome/browser/resources/settings/settings_page/main_page_behavior.js b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
index 746b2e7..333dccfc 100644
--- a/chrome/browser/resources/settings/settings_page/main_page_behavior.js
+++ b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
@@ -199,6 +199,41 @@
},
/**
+ * Finds the settings-section instances corresponding to the given route. If
+ * the section is lazily loaded it force-renders it.
+ * Note: If the section resides within "advanced" settings, a
+ * 'hide-container' event is fired (necessary to avoid flashing). Callers
+ * are responsible for firing a 'show-container' event.
+ * @param {!Route} route
+ * @return {!Promise<!Array<!SettingsSectionElement>>}
+ * @private
+ */
+ ensureSectionsForRoute_(route) {
+ const sections = this.querySettingsSections_(route.section);
+ if (sections.length > 0) {
+ return Promise.resolve(sections);
+ }
+
+ // The function to use to wait for <dom-if>s to render.
+ const waitFn = beforeNextRender.bind(null, this);
+
+ return new Promise(resolve => {
+ if (this.shouldExpandAdvanced_(route)) {
+ this.fire('hide-container');
+ waitFn(() => {
+ this.$$('#advancedPageTemplate').get().then(() => {
+ resolve(this.querySettingsSections_(route.section));
+ });
+ });
+ } else {
+ waitFn(() => {
+ resolve(this.querySettingsSections_(route.section));
+ });
+ }
+ });
+ },
+
+ /**
* @param {!Route} route
* @private
*/
@@ -255,19 +290,23 @@
},
/**
- * Shows the section corresponding to |newRoute| and hides the previously
- * |active| section (if any).
+ * Shows the section(s) corresponding to |newRoute| and hides the previously
+ * |active| section(s), if any.
* @param {!Route} newRoute
*/
- switchToSection_(newRoute) {
- this.ensureSectionForRoute_(newRoute).then(section => {
+ switchToSections_(newRoute) {
+ this.ensureSectionsForRoute_(newRoute).then(sections => {
// Clear any previously |active| section.
- const oldSection = this.$$(`settings-section[active]`);
- if (oldSection) {
- oldSection.toggleAttribute('active', false);
+ const oldSections =
+ this.shadowRoot.querySelectorAll(`settings-section[active]`);
+ for (const s of oldSections) {
+ s.toggleAttribute('active', false);
}
- section.toggleAttribute('active', true);
+ for (const s of sections) {
+ s.toggleAttribute('active', true);
+ }
+
this.fire('show-container');
});
},
@@ -431,13 +470,13 @@
processTransitionRedesign_(oldRoute, newRoute, oldState, newState) {
if (oldState === RouteState.TOP_LEVEL) {
if (newState === RouteState.SECTION) {
- this.switchToSection_(newRoute);
+ this.switchToSections_(newRoute);
} else if (newState === RouteState.SUBPAGE) {
this.enterSubpage_(newRoute);
} else if (newState === RouteState.TOP_LEVEL) {
// Case when navigating from '/?search=foo' to '/' (clearing search
// results).
- this.switchToSection_(TOP_LEVEL_EQUIVALENT_ROUTE);
+ this.switchToSections_(TOP_LEVEL_EQUIVALENT_ROUTE);
}
// Nothing to do here for the case of RouteState.DIALOG.
return;
@@ -445,12 +484,12 @@
if (oldState === RouteState.SECTION) {
if (newState === RouteState.SECTION) {
- this.switchToSection_(newRoute);
+ this.switchToSections_(newRoute);
} else if (newState === RouteState.SUBPAGE) {
- this.switchToSection_(newRoute);
+ this.switchToSections_(newRoute);
this.enterSubpage_(newRoute);
} else if (newState === RouteState.TOP_LEVEL) {
- this.switchToSection_(TOP_LEVEL_EQUIVALENT_ROUTE);
+ this.switchToSections_(TOP_LEVEL_EQUIVALENT_ROUTE);
this.scroller.scrollTop = 0;
}
// Nothing to do here for the case of RouteState.DIALOG.
@@ -460,7 +499,7 @@
if (oldState === RouteState.SUBPAGE) {
if (newState === RouteState.SECTION) {
this.enterMainPage_(/** @type {!Route} */ (oldRoute));
- this.switchToSection_(newRoute);
+ this.switchToSections_(newRoute);
} else if (newState === RouteState.SUBPAGE) {
// Handle case where the two subpages belong to
// different sections, but are linked to each other. For example
@@ -488,12 +527,12 @@
if (oldState === RouteState.INITIAL) {
if ([RouteState.SECTION, RouteState.DIALOG].includes(newState)) {
- this.switchToSection_(newRoute);
+ this.switchToSections_(newRoute);
} else if (newState === RouteState.SUBPAGE) {
- this.switchToSection_(newRoute);
+ this.switchToSections_(newRoute);
this.enterSubpage_(newRoute);
} else if (newState === RouteState.TOP_LEVEL) {
- this.switchToSection_(TOP_LEVEL_EQUIVALENT_ROUTE);
+ this.switchToSections_(TOP_LEVEL_EQUIVALENT_ROUTE);
}
return;
}
@@ -515,4 +554,24 @@
return /** @type {?SettingsSectionElement} */ (
this.$$(`settings-section[section="${section}"]`));
},
+
+ /*
+ * @param {string} sectionName Section name of the element to get.
+ * @return {!Array<!SettingsSectionElement>}
+ */
+ querySettingsSections_(sectionName) {
+ const result = [];
+ const section = this.getSection(sectionName);
+
+ if (section) {
+ result.push(section);
+ }
+
+ const extraSections = this.shadowRoot.querySelectorAll(
+ `settings-section[nest-under-section="${sectionName}"]`);
+ if (extraSections.length > 0) {
+ result.push(...extraSections);
+ }
+ return result;
+ }
};
diff --git a/chrome/test/data/webui/settings/basic_page_test.js b/chrome/test/data/webui/settings/basic_page_test.js
index 15c52119..7f57821c 100644
--- a/chrome/test/data/webui/settings/basic_page_test.js
+++ b/chrome/test/data/webui/settings/basic_page_test.js
@@ -11,7 +11,7 @@
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {loadTimeData, pageVisibility, Router, routes} from 'chrome://settings/settings.js';
-import {flushTasks, isVisible} from '../test_util.m.js';
+import {eventToPromise, flushTasks, isVisible} from '../test_util.m.js';
// clang-format on
// Register mocha tests.
@@ -60,11 +60,35 @@
suite('SettingsBasicPageRedesign', () => {
let page = null;
- setup(function() {
+ suiteSetup(function() {
+ assertTrue(loadTimeData.getBoolean('enableLandingPageRedesign'));
+ const attribute =
+ loadTimeData.getString('enableLandingPageRedesignAttribute');
+ assertEquals('enable-landing-page-redesign', attribute);
+
+ // Do this manually as it is normally part of settings.html, which is not
+ // part of this test.
+ document.documentElement.toggleAttribute(attribute, true);
+ });
+
+ setup(async function() {
PolymerTest.clearBody();
page = document.createElement('settings-basic-page');
document.body.appendChild(page);
page.scroller = document.body;
+
+ // Need to wait for the 'show-container' event to fire after every
+ // transition, to ensure no logic related to previous transitions is still
+ // running when later transitions are tested.
+ const whenDone = eventToPromise('show-container', page);
+
+ // Ensure that all settings-section instances are rendered.
+ flush();
+ await page.$$('#advancedPageTemplate').get();
+ const sections = page.shadowRoot.querySelectorAll('settings-section');
+ assertTrue(sections.length > 1);
+
+ await whenDone;
});
/** @param {string} section */
@@ -81,22 +105,6 @@
}
test('OnlyOneSectionShown', async () => {
- assertTrue(loadTimeData.getBoolean('enableLandingPageRedesign'));
- const attribute =
- loadTimeData.getString('enableLandingPageRedesignAttribute');
- assertEquals('enable-landing-page-redesign', attribute);
-
-
- // Do this manually as it is normally part of settings.html, which is not
- // part of this test.
- document.documentElement.toggleAttribute(attribute, true);
-
- // Ensure that all settings-section instances are rendered.
- flush();
- await page.$$('#advancedPageTemplate').get();
- const sections = page.shadowRoot.querySelectorAll('settings-section');
- assertTrue(sections.length > 1);
-
// RouteState.INITIAL -> RoutState.TOP_LEVEL
// Check that only one is marked as |active|.
assertActiveSection(routes.PEOPLE.section);
@@ -105,7 +113,9 @@
// RouteState.TOP_LEVEL -> RoutState.SECTION
// Check that navigating to a different route correctly updates the page.
+ let whenDone = eventToPromise('show-container', page);
Router.getInstance().navigateTo(routes.SEARCH);
+ await whenDone;
await flushTasks();
assertActiveSection(routes.SEARCH.section);
assertTrue(!!page.shadowRoot.querySelector(
@@ -128,7 +138,9 @@
}
// RouteState.SECTION -> RoutState.SECTION
+ whenDone = eventToPromise('show-container', page);
Router.getInstance().navigateTo(routes.APPEARANCE);
+ await whenDone;
await flushTasks();
assertActiveSection(routes.APPEARANCE.section);
assertTrue(!!getCardElement());
@@ -136,7 +148,9 @@
assertFalse(!!getSubpage());
// RouteState.SECTION -> RoutState.SUBPAGE
+ whenDone = eventToPromise('show-container', page);
Router.getInstance().navigateTo(routes.FONTS);
+ await whenDone;
await flushTasks();
assertActiveSection(routes.APPEARANCE.section);
assertTrue(!!getCardElement());
@@ -144,7 +158,9 @@
assertTrue(!!getSubpage());
// RouteState.SUBPAGE -> RoutState.SECTION
+ whenDone = eventToPromise('show-container', page);
Router.getInstance().navigateTo(routes.APPEARANCE);
+ await whenDone;
await flushTasks();
assertActiveSection(routes.APPEARANCE.section);
assertTrue(!!getCardElement());
@@ -152,8 +168,29 @@
assertFalse(!!getSubpage());
// RouteState.SECTION -> RoutState.TOP_LEVEL
+ whenDone = eventToPromise('show-container', page);
Router.getInstance().navigateTo(routes.BASIC);
+ await whenDone;
await flushTasks();
assertActiveSection(routes.PEOPLE.section);
});
+
+ // Test cases where a settings-section is appearing next to another section
+ // using the |nest-under-section| attribute. Only one such case currently
+ // exists.
+ test('SometimesMoreSectionsShown', async () => {
+ const whenDone = eventToPromise('show-container', page);
+ Router.getInstance().navigateTo(routes.PRIVACY);
+ await whenDone;
+ await flushTasks();
+
+ const activeSections =
+ page.shadowRoot.querySelectorAll('settings-section[active]');
+ assertEquals(2, activeSections.length);
+ assertEquals(routes.SAFETY_CHECK.section, activeSections[0].section);
+ assertEquals(
+ routes.PRIVACY.section,
+ activeSections[0].getAttribute('nest-under-section'));
+ assertEquals(routes.PRIVACY.section, activeSections[1].section);
+ });
});