Commit 1e3e1639 authored by Esmael El-Moslimany's avatar Esmael El-Moslimany Committed by Commit Bot

WebUI NTP: customize-dialog, allow menu and page to be independently scrollable

Bug: 1048336
Change-Id: I49b88b1e39030f0bc990934bee9076bd52b5f35a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2037324
Commit-Queue: Esmael Elmoslimany <aee@chromium.org>
Reviewed-by: default avatarTibor Goldschwendt <tiborg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#741548}
parent 43cf9153
...@@ -3,18 +3,62 @@ ...@@ -3,18 +3,62 @@
min-width: 800px; min-width: 800px;
} }
:host {
--border-width: 1px;
}
div[slot=title] {
align-items: center;
display: flex;
flex-direction: row;
height: 80px;
padding: 0;
}
div[slot=body] { div[slot=body] {
color: var(--cr-primary-text-color); color: var(--cr-primary-text-color);
display: flex; display: flex;
flex-direction: row; flex-direction: row;
min-height: 423px; overflow: hidden;
padding-inline-start: 0; padding: 0;
}
#menuContainer,
#pagesContainer {
overflow: hidden;
}
#leftTitleSpacer,
#menuContainer {
flex-basis: 232px;
}
#title,
#pagesContainer {
flex-grow: 1;
}
#menu,
#pages {
height: calc(100% - 2 * var(--border-width));
overflow: auto;
}
#pages > iron-pages {
min-height: 389px;
}
div[scroll-border] {
border-bottom: var(--border-width) solid var(--ntp-border-color);
}
div[scroll-border]:not([show]) {
--ntp-border-color: transparent;
} }
#menu { #menu {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-inline-end: 40px;
} }
.menu-item { .menu-item {
...@@ -24,6 +68,7 @@ ...@@ -24,6 +68,7 @@
cursor: pointer; cursor: pointer;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-shrink: 0;
font-size: 14px; font-size: 14px;
height: 32px; height: 32px;
margin-bottom: 16px; margin-bottom: 16px;
...@@ -74,44 +119,49 @@ ...@@ -74,44 +119,49 @@
#themesIcon { #themesIcon {
-webkit-mask-image: url(icons/colors.svg); -webkit-mask-image: url(icons/colors.svg);
} }
#pages {
align-items: center;
display: flex;
flex-direction: column;
flex-grow: 1;
}
</style> </style>
<cr-dialog id="dialog" show-on-attach> <cr-dialog id="dialog" show-on-attach>
<div slot="title">
<div id="leftTitleSpacer"></div>
<div id="title">$i18n{customizeThisPage}</div>
</div>
<!-- TODO(crbug.com/1040256): Currently, the sidebar scrolls in sync with the <!-- TODO(crbug.com/1040256): Currently, the sidebar scrolls in sync with the
page content area. Fix, so that the page content can scroll page content area. Fix, so that the page content can scroll
separately. --> separately. -->
<div slot="body"> <div slot="body">
<iron-selector id="menu" selected-attribute="selected" <div id="menuContainer">
attr-for-selected="page-name" selected="{{selectedPage_}}" <div id="menu">
on-keydown="onMenuItemKeyDown_"> <iron-selector selected-attribute="selected"
<div class="menu-item" page-name="backgrounds" tabindex="0"> attr-for-selected="page-name" selected="{{selectedPage_}}"
<div id="backgroundsIcon" class="menu-item-icon"></div> on-keydown="onMenuItemKeyDown_">
$i18n{backgroundsMenuItem} <div class="menu-item" page-name="backgrounds" tabindex="0">
</div> <div id="backgroundsIcon" class="menu-item-icon"></div>
<div class="menu-item" page-name="shortcuts" tabindex="0"> $i18n{backgroundsMenuItem}
<div id="shortcutsIcon" class="menu-item-icon"></div> </div>
$i18n{shortcutsMenuItem} <div class="menu-item" page-name="shortcuts" tabindex="0">
<div id="shortcutsIcon" class="menu-item-icon"></div>
$i18n{shortcutsMenuItem}
</div>
<div class="menu-item" page-name="themes" tabindex="0">
<div id="themesIcon" class="menu-item-icon"></div>
$i18n{themesMenuItem}
</div>
</iron-selector>
</div> </div>
<div class="menu-item" page-name="themes" tabindex="0"> </div>
<div id="themesIcon" class="menu-item-icon"></div> <div id="pagesContainer">
$i18n{themesMenuItem} <div id="pages">
<iron-pages selected="[[selectedPage_]]" attr-for-selected="page-name">
<!-- TODO(crbug.com/1032328): Add support for selecting background
image. -->
<div page-name="backgrounds">backgrounds</div>
<ntp-customize-shortcuts page-name="shortcuts">
</ntp-customize-shortcuts>
<ntp-customize-themes page-name="themes" theme="[[theme]]">
</ntp-customize-themes>
</iron-pages>
</div> </div>
</iron-selector> </div>
<iron-pages id="pages" selected="[[selectedPage_]]"
attr-for-selected="page-name">
<!-- TODO(crbug.com/1032328): Add support for selecting background
image. -->
<div page-name="backgrounds">backgrounds</div>
<ntp-customize-shortcuts page-name="shortcuts"></ntp-customize-shortcuts>
<ntp-customize-themes page-name="themes" theme="[[theme]]">
</ntp-customize-themes>
</iron-pages>
</div> </div>
<div slot="button-container"> <div slot="button-container">
<cr-button class="cancel-button" on-click="onCancelClick_"> <cr-button class="cancel-button" on-click="onCancelClick_">
......
...@@ -43,6 +43,49 @@ class CustomizeDialogElement extends PolymerElement { ...@@ -43,6 +43,49 @@ class CustomizeDialogElement extends PolymerElement {
super(); super();
/** @private {newTabPage.mojom.PageHandlerRemote} */ /** @private {newTabPage.mojom.PageHandlerRemote} */
this.pageHandler_ = BrowserProxy.getInstance().handler; this.pageHandler_ = BrowserProxy.getInstance().handler;
/** @private {!Array<!IntersectionObserver>} */
this.intersectionObservers_ = [];
}
/** @override */
disconnectedCallback() {
super.disconnectedCallback();
this.intersectionObservers_.forEach(observer => {
observer.disconnect();
});
this.intersectionObservers_ = [];
}
/** @override */
ready() {
super.ready();
['menu', 'pages'].forEach(id => {
const container = this.$[id];
const topProbe = document.createElement('div');
container.prepend(topProbe);
const bottomProbe = document.createElement('div');
container.append(bottomProbe);
const topBorder = document.createElement('div');
topBorder.toggleAttribute('scroll-border', true);
container.parentNode.insertBefore(topBorder, container);
const bottomBorder = document.createElement('div');
bottomBorder.toggleAttribute('scroll-border', true);
container.parentNode.insertBefore(bottomBorder, container.nextSibling);
const observer = new IntersectionObserver(entries => {
entries.forEach(({target, intersectionRatio}) => {
const show = intersectionRatio === 0;
if (target === topProbe) {
topBorder.toggleAttribute('show', show);
} else if (target === bottomProbe) {
bottomBorder.toggleAttribute('show', show);
}
});
}, {root: container});
observer.observe(topProbe);
observer.observe(bottomProbe);
this.intersectionObservers_.push(observer);
});
} }
/** @private */ /** @private */
......
...@@ -11,37 +11,41 @@ suite('NewTabPageCustomizeDialogFocusTest', () => { ...@@ -11,37 +11,41 @@ suite('NewTabPageCustomizeDialogFocusTest', () => {
/** @type {!CustomizeDialogElement} */ /** @type {!CustomizeDialogElement} */
let customizeDialog; let customizeDialog;
setup(async () => { setup(() => {
PolymerTest.clearBody(); PolymerTest.clearBody();
customizeDialog = document.createElement('ntp-customize-dialog'); customizeDialog = document.createElement('ntp-customize-dialog');
document.body.appendChild(customizeDialog); document.body.appendChild(customizeDialog);
await flushTasks(); return flushTasks();
}); });
test('space selects focused menu item', async () => { test('space selects focused menu item', () => {
// Arrange. // Arrange.
const menuItem = customizeDialog.shadowRoot.querySelector( const menuItem = customizeDialog.shadowRoot.querySelector(
'.menu-item[page-name="themes"'); '.menu-item[page-name=themes]');
menuItem.focus(); menuItem.focus();
// Act. // Act.
keydown(menuItem, ' '); keydown(menuItem, ' ');
// Assert. // Assert.
assertEquals(customizeDialog.$.menu.selected, 'themes'); const selector = customizeDialog.$.menu.querySelector('iron-selector');
assertTrue(!!selector);
assertEquals('themes', selector.selected);
}); });
test('enter selects focused menu item', async () => { test('enter selects focused menu item', () => {
// Arrange. // Arrange.
const menuItem = customizeDialog.shadowRoot.querySelector( const menuItem = customizeDialog.shadowRoot.querySelector(
'.menu-item[page-name="shortcuts"'); '.menu-item[page-name=shortcuts]');
menuItem.focus(); menuItem.focus();
// Act. // Act.
keydown(menuItem, 'Enter'); keydown(menuItem, 'Enter');
// Assert. // Assert.
assertEquals(customizeDialog.$.menu.selected, 'shortcuts'); const selector = customizeDialog.$.menu.querySelector('iron-selector');
assertTrue(!!selector);
assertEquals('shortcuts', selector.selected);
}); });
}); });
...@@ -4,18 +4,18 @@ ...@@ -4,18 +4,18 @@
import 'chrome://new-tab-page/customize_dialog.js'; import 'chrome://new-tab-page/customize_dialog.js';
import {flushTasks} from 'chrome://test/test_util.m.js'; import {flushTasks, waitAfterNextRender} from 'chrome://test/test_util.m.js';
suite('NewTabPageCustomizeDialogTest', () => { suite('NewTabPageCustomizeDialogTest', () => {
/** @type {!CustomizeDialogElement} */ /** @type {!CustomizeDialogElement} */
let customizeDialog; let customizeDialog;
setup(async () => { setup(() => {
PolymerTest.clearBody(); PolymerTest.clearBody();
customizeDialog = document.createElement('ntp-customize-dialog'); customizeDialog = document.createElement('ntp-customize-dialog');
document.body.appendChild(customizeDialog); document.body.appendChild(customizeDialog);
await flushTasks(); return flushTasks();
}); });
test('creating customize dialog opens cr dialog', () => { test('creating customize dialog opens cr dialog', () => {
...@@ -33,7 +33,7 @@ suite('NewTabPageCustomizeDialogTest', () => { ...@@ -33,7 +33,7 @@ suite('NewTabPageCustomizeDialogTest', () => {
test('selecting menu item shows page', async () => { test('selecting menu item shows page', async () => {
// Act. // Act.
customizeDialog.$.menu.select('themes'); customizeDialog.$.menu.querySelector('[page-name=themes]').click();
await flushTasks(); await flushTasks();
// Assert. // Assert.
...@@ -42,4 +42,46 @@ suite('NewTabPageCustomizeDialogTest', () => { ...@@ -42,4 +42,46 @@ suite('NewTabPageCustomizeDialogTest', () => {
assertEquals(shownPages.length, 1); assertEquals(shownPages.length, 1);
assertEquals(shownPages[0].getAttribute('page-name'), 'themes'); assertEquals(shownPages[0].getAttribute('page-name'), 'themes');
}); });
suite('scroll borders', () => {
/**
* @param {!HTMLElement} container
* @private
*/
async function testScrollBorders(container) {
const assertHidden = el => {
assertTrue(el.matches('[scroll-border]:not([show])'));
};
const assertShown = el => {
assertTrue(el.matches('[scroll-border][show]'));
};
const {firstElementChild: top, lastElementChild: bottom} = container;
const scrollableElement = top.nextSibling;
const dialogBody =
customizeDialog.shadowRoot.querySelector('div[slot=body]');
const heightWithBorders = `${scrollableElement.scrollHeight + 2}px`;
dialogBody.style.height = heightWithBorders;
assertHidden(top);
assertHidden(bottom);
dialogBody.style.height = '50px';
await waitAfterNextRender();
assertHidden(top);
assertShown(bottom);
scrollableElement.scrollTop = 1;
await waitAfterNextRender();
assertShown(top);
assertShown(bottom);
scrollableElement.scrollTop = scrollableElement.scrollHeight;
await waitAfterNextRender();
assertShown(top);
assertHidden(bottom);
dialogBody.style.height = heightWithBorders;
await waitAfterNextRender();
assertHidden(top);
assertHidden(bottom);
}
test('menu', () => testScrollBorders(customizeDialog.$.menuContainer));
test('pages', () => testScrollBorders(customizeDialog.$.pagesContainer));
});
}); });
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