Commit cf5a2fb4 authored by Tibor Goldschwendt's avatar Tibor Goldschwendt Committed by Commit Bot

[ntp][modules] Add generic custom module element

The generic module element implements common UI such as the header and
border. The module supplies the information required to populate the
common UI via its module descriptor. The generic module element also
wraps the local module UI previously instantiated.

Bug: 1110047
Change-Id: I4c2579bb2c0d3e19b9120c03606ad21d30dea529
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2336912
Commit-Queue: Tibor Goldschwendt <tiborg@chromium.org>
Reviewed-by: default avatarMoe Ahmadi <mahmadi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798161}
parent 21b49e10
...@@ -5613,6 +5613,24 @@ Keep your key file in a safe place. You will need it to create new versions of y ...@@ -5613,6 +5613,24 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_NTP_COLORS_DARK_PURPLE" desc="A color option in the customization menu on the New Tab Page."> <message name="IDS_NTP_COLORS_DARK_PURPLE" desc="A color option in the customization menu on the New Tab Page.">
Dark purple Dark purple
</message> </message>
<message name="IDS_NTP_MODULES_DUMMY_NAME" translateable="false" desc="Name shown in the header of the dummy module.">
Dummy
</message>
<message name="IDS_NTP_MODULES_DUMMY_TITLE" translateable="false" desc="Title shown in the header of the dummy module.">
Super Duper Module
</message>
<message name="IDS_NTP_MODULES_DUMMY2_NAME" translateable="false" desc="Name shown in the header of the dummy 2 module.">
Dummy 2
</message>
<message name="IDS_NTP_MODULES_DUMMY2_TITLE" translateable="false" desc="Title shown in the header of the dummy 2 module.">
Even Better Module
</message>
<message name="IDS_NTP_MODULES_KALEIDOSCOPE_NAME" translateable="false" desc="Name shown in the header of the Kaleidoscope module.">
Kaleidoscope
</message>
<message name="IDS_NTP_MODULES_KALEIDOSCOPE_TITLE" desc="Title shown in the header of the Kaleidoscope module.">
Top picks for you
</message>
<!-- Extensions NTP Middle Slot Promo --> <!-- Extensions NTP Middle Slot Promo -->
<message name="IDS_EXTENSIONS_PROMO_PERFORMANCE"> <message name="IDS_EXTENSIONS_PROMO_PERFORMANCE">
......
615a1d35e012617c4de8c15c64087a0a0e010320
\ No newline at end of file
...@@ -49,6 +49,7 @@ js_library("app") { ...@@ -49,6 +49,7 @@ js_library("app") {
deps = [ deps = [
":background_manager", ":background_manager",
":browser_proxy", ":browser_proxy",
":module_wrapper",
":modules", ":modules",
":most_visited", ":most_visited",
":one_google_bar_api", ":one_google_bar_api",
...@@ -240,9 +241,18 @@ js_library("modules") { ...@@ -240,9 +241,18 @@ js_library("modules") {
] ]
} }
js_library("module_wrapper") {
deps = [
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
}
js_library("module_registry") { js_library("module_registry") {
sources = [ "modules/module_registry.js" ] sources = [ "modules/module_registry.js" ]
deps = [ ":module_descriptor" ] deps = [
":module_descriptor",
"//ui/webui/resources/js:cr.m",
]
} }
html_to_js("web_components_local") { html_to_js("web_components_local") {
...@@ -266,6 +276,7 @@ html_to_js("web_components_local") { ...@@ -266,6 +276,7 @@ html_to_js("web_components_local") {
"theme_icon.js", "theme_icon.js",
"iframe.js", "iframe.js",
"voice_search_overlay.js", "voice_search_overlay.js",
"module_wrapper.js",
] ]
} }
......
...@@ -4,6 +4,19 @@ ...@@ -4,6 +4,19 @@
--ntp-theme-text-color: var(--google-grey-800); --ntp-theme-text-color: var(--google-grey-800);
--ntp-theme-text-shadow: none; --ntp-theme-text-shadow: none;
--ntp-one-google-bar-height: 56px; --ntp-one-google-bar-height: 56px;
--ntp-search-box-width: 337px;
}
@media (min-width: 560px) {
:host {
--ntp-search-box-width: 449px;
}
}
@media (min-width: 672px) {
:host {
--ntp-search-box-width: 561px;
}
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
...@@ -61,6 +74,12 @@ ...@@ -61,6 +74,12 @@
margin-bottom: 32px; margin-bottom: 32px;
} }
ntp-fakebox,
ntp-realbox,
ntp-module-wrapper {
width: var(--ntp-search-box-width);
}
ntp-realbox { ntp-realbox {
visibility: hidden; visibility: hidden;
} }
...@@ -84,6 +103,10 @@ ...@@ -84,6 +103,10 @@
width: 100%; width: 100%;
} }
ntp-module-wrapper + ntp-module-wrapper {
margin-top: 24px;
}
#customizeButtonSpacer { #customizeButtonSpacer {
flex-grow: 1; flex-grow: 1;
} }
...@@ -260,7 +283,9 @@ ...@@ -260,7 +283,9 @@
<ntp-iframe id="promo" hidden$="[[!promoLoaded_]]" <ntp-iframe id="promo" hidden$="[[!promoLoaded_]]"
src="chrome-untrusted://new-tab-page/promo"> src="chrome-untrusted://new-tab-page/promo">
</ntp-iframe> </ntp-iframe>
<div id="modules"></div> <template is="dom-repeat" items="[[moduleDescriptors_]]" id="modules">
<ntp-module-wrapper descriptor="[[item]]"></ntp-module-wrapper>
</template>
<a id="backgroundImageAttribution" <a id="backgroundImageAttribution"
href="[[backgroundImageAttributionUrl_]]" href="[[backgroundImageAttributionUrl_]]"
hidden="[[!backgroundImageAttribution1_]]"> hidden="[[!backgroundImageAttribution1_]]">
......
...@@ -10,6 +10,8 @@ import './iframe.js'; ...@@ -10,6 +10,8 @@ import './iframe.js';
import './fakebox.js'; import './fakebox.js';
import './realbox.js'; import './realbox.js';
import './logo.js'; import './logo.js';
import './module_wrapper.js';
import './modules/modules.js'; // Registers module descriptors.
import 'chrome://resources/cr_elements/cr_button/cr_button.m.js'; import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
import 'chrome://resources/cr_elements/shared_style_css.m.js'; import 'chrome://resources/cr_elements/shared_style_css.m.js';
...@@ -22,7 +24,8 @@ import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/poly ...@@ -22,7 +24,8 @@ import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/poly
import {BackgroundManager} from './background_manager.js'; import {BackgroundManager} from './background_manager.js';
import {BrowserProxy} from './browser_proxy.js'; import {BrowserProxy} from './browser_proxy.js';
import {BackgroundSelection, BackgroundSelectionType} from './customize_dialog.js'; import {BackgroundSelection, BackgroundSelectionType} from './customize_dialog.js';
import {registry} from './modules/modules.js'; import {ModuleDescriptor} from './modules/module_descriptor.js';
import {ModuleRegistry} from './modules/module_registry.js';
import {oneGoogleBarApi} from './one_google_bar_api.js'; import {oneGoogleBarApi} from './one_google_bar_api.js';
import {PromoBrowserCommandProxy} from './promo_browser_command_proxy.js'; import {PromoBrowserCommandProxy} from './promo_browser_command_proxy.js';
import {$$, hexColorToSkColor, skColorToRgba} from './utils.js'; import {$$, hexColorToSkColor, skColorToRgba} from './utils.js';
...@@ -198,6 +201,9 @@ class AppElement extends PolymerElement { ...@@ -198,6 +201,9 @@ class AppElement extends PolymerElement {
* @private * @private
*/ */
lazyRender_: Boolean, lazyRender_: Boolean,
/** @private {!Array<!ModuleDescriptor>} */
moduleDescriptors_: Object,
}; };
} }
...@@ -448,15 +454,12 @@ class AppElement extends PolymerElement { ...@@ -448,15 +454,12 @@ class AppElement extends PolymerElement {
} }
/** @private */ /** @private */
onLazyRendered_() { async onLazyRendered_() {
if (!loadTimeData.getBoolean('modulesEnabled')) { if (!loadTimeData.getBoolean('modulesEnabled')) {
return; return;
} }
const container = $$(this, '#modules'); this.moduleDescriptors_ =
if (!container) { await ModuleRegistry.getInstance().initializeModules();
return;
}
registry.instantiateModules(container);
} }
/** @private */ /** @private */
......
...@@ -6,19 +6,6 @@ ...@@ -6,19 +6,6 @@
box-shadow: 0 1px 6px 0 rgba(32, 33, 36, .28); box-shadow: 0 1px 6px 0 rgba(32, 33, 36, .28);
height: var(--ntp-fakebox-height); height: var(--ntp-fakebox-height);
position: relative; position: relative;
width: 337px;
}
@media (min-width: 560px) {
:host {
width: 449px;
}
}
@media (min-width: 672px) {
:host {
width: 561px;
}
} }
:host([hidden_]) { :host([hidden_]) {
......
<style>
:host {
background-color: var(--ntp-background-override-color);
border: solid var(--ntp-border-color) 1px;
border-radius: 5px;
}
#header {
margin: 10px;
}
#title {
color: var(--cr-primary-text-color);
}
#name {
color: var(--cr-secondary-text-color);
}
#moduleElement {
align-items: center;
display: flex;
height: 150px;
justify-content: center;
overflow: hidden;
}
</style>
<div id="header">
<span id="title">[[descriptor.title]]</span>
<span id="name"> • [[descriptor.name]]</span>
</div>
<div id="moduleElement"></div>
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {ModuleDescriptor} from './modules/module_descriptor.js';
/** @fileoverview Element that implements the common module UI. */
class ModuleWrapperElement extends PolymerElement {
static get is() {
return 'ntp-module-wrapper';
}
static get template() {
return html`{__html_template__}`;
}
static get properties() {
return {
/** @type {!ModuleDescriptor} */
descriptor: {
observer: 'onDescriptorChange_',
type: Object,
},
};
}
/** @private */
onDescriptorChange_() {
this.$.moduleElement.innerHTML = '';
this.$.moduleElement.appendChild(this.descriptor.element);
}
}
customElements.define(ModuleWrapperElement.is, ModuleWrapperElement);
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import '../../grid.js'; import '../../grid.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {ModuleDescriptor} from '../module_descriptor.js'; import {ModuleDescriptor} from '../module_descriptor.js';
...@@ -39,7 +40,16 @@ class DummyModuleElement extends PolymerElement { ...@@ -39,7 +40,16 @@ class DummyModuleElement extends PolymerElement {
customElements.define(DummyModuleElement.is, DummyModuleElement); customElements.define(DummyModuleElement.is, DummyModuleElement);
/** @type {!ModuleDescriptor} */ /** @type {!ModuleDescriptor} */
export const dummyDescriptor = { export const dummyDescriptor = new ModuleDescriptor(
id: 'dummy', 'dummy', loadTimeData.getString('modulesDummyName'), () => Promise.resolve({
create: () => Promise.resolve(new DummyModuleElement()), element: new DummyModuleElement(),
}; title: loadTimeData.getString('modulesDummyTitle'),
}));
/** @type {!ModuleDescriptor} */
export const dummyDescriptor2 = new ModuleDescriptor(
'dummy2', loadTimeData.getString('modulesDummy2Name'),
() => Promise.resolve({
element: new DummyModuleElement(),
title: loadTimeData.getString('modulesDummy2Title'),
}));
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import '../../grid.js'; import '../../grid.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {ModuleDescriptor} from '../module_descriptor.js'; import {ModuleDescriptor} from '../module_descriptor.js';
...@@ -40,7 +41,9 @@ class KaleidoscopeModuleElement extends PolymerElement { ...@@ -40,7 +41,9 @@ class KaleidoscopeModuleElement extends PolymerElement {
customElements.define(KaleidoscopeModuleElement.is, KaleidoscopeModuleElement); customElements.define(KaleidoscopeModuleElement.is, KaleidoscopeModuleElement);
/** @type {!ModuleDescriptor} */ /** @type {!ModuleDescriptor} */
export const kaleidoscopeDescriptor = { export const kaleidoscopeDescriptor = new ModuleDescriptor(
id: 'kaleidoscope', 'kaleidoscope', loadTimeData.getString('modulesKaleidoscopeName'),
create: () => Promise.resolve(new KaleidoscopeModuleElement()), () => Promise.resolve({
}; element: new KaleidoscopeModuleElement(),
title: loadTimeData.getString('modulesKaleidoscopeTitle'),
}));
...@@ -3,14 +3,54 @@ ...@@ -3,14 +3,54 @@
// found in the LICENSE file. // found in the LICENSE file.
/** /**
* @fileoverview Provides the module descriptor type declaration. Each module * @fileoverview Provides the module descriptor. Each module must create a
* must create a module descriptor and register it at the NTP. * module descriptor and register it at the NTP.
*/ */
/** /**
* @typedef {{ * @typedef {function(): !Promise<?{
* id: string, * element: !HTMLElement,
* create: function(): !Promise<!HTMLElement>, * title: string,
* }} * }>}
*/ */
export let ModuleDescriptor; let InitializeModuleCallback;
export class ModuleDescriptor {
/**
* @param {string} id
* @param {string} name
* @param {!InitializeModuleCallback} initializeCallback
*/
constructor(id, name, initializeCallback) {
this.id_ = id;
this.name_ = name;
this.title_ = null;
this.element_ = null;
this.initializeCallback_ = initializeCallback;
}
get id() {
return this.id_;
}
get name() {
return this.name_;
}
get title() {
return this.title_;
}
get element() {
return this.element_;
}
async initialize() {
const info = await this.initializeCallback_();
if (!info) {
return;
}
this.title_ = info.title;
this.element_ = info.element;
}
}
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
import {ModuleDescriptor} from './module_descriptor.js'; import {ModuleDescriptor} from './module_descriptor.js';
/** /**
...@@ -10,22 +12,29 @@ import {ModuleDescriptor} from './module_descriptor.js'; ...@@ -10,22 +12,29 @@ import {ModuleDescriptor} from './module_descriptor.js';
*/ */
export class ModuleRegistry { export class ModuleRegistry {
/** @param {!Array<!ModuleDescriptor>} moduleDescriptors */ constructor() {
constructor(moduleDescriptors) {
/** @private {!Array<!ModuleDescriptor>} */ /** @private {!Array<!ModuleDescriptor>} */
this.moduleDescriptors_ = moduleDescriptors; this.descriptors_ = [];
}
/**
* Registers modules via their descriptors.
* @param {!Array<!ModuleDescriptor>} descriptors
*/
registerModules(descriptors) {
/** @type {!Array<!ModuleDescriptor>} */
this.descriptors_ = descriptors;
} }
/** /**
* Instantiates modules and appends them to |container|. * Initializes the modules previously set via |registerModules| and returns
* @param {!Element} container * the initialized descriptors.
* @return {!Promise<!Array<!ModuleDescriptor>>}
*/ */
async instantiateModules(container) { async initializeModules() {
(await Promise.all( await Promise.all(this.descriptors_.map(d => d.initialize()));
this.moduleDescriptors_.map(descriptor => descriptor.create()))) return this.descriptors_.filter(descriptor => !!descriptor.element);
.filter(module => !!module)
.forEach(element => {
container.appendChild(element);
});
} }
} }
addSingletonGetter(ModuleRegistry);
...@@ -7,15 +7,17 @@ ...@@ -7,15 +7,17 @@
*/ */
import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
import {dummyDescriptor} from './dummy/module.js';
import {dummyDescriptor, dummyDescriptor2} from './dummy/module.js';
import {kaleidoscopeDescriptor} from './kaleidoscope/module.js'; import {kaleidoscopeDescriptor} from './kaleidoscope/module.js';
import {ModuleDescriptor} from './module_descriptor.js';
import {ModuleRegistry} from './module_registry.js'; import {ModuleRegistry} from './module_registry.js';
const descriptors = [dummyDescriptor]; /** @type {!Array<!ModuleDescriptor>} */
const descriptors = [dummyDescriptor, dummyDescriptor2];
if (loadTimeData.getBoolean('kaleidoscopeModuleEnabled')) { if (loadTimeData.getBoolean('kaleidoscopeModuleEnabled')) {
descriptors.push(kaleidoscopeDescriptor); descriptors.push(kaleidoscopeDescriptor);
} }
/** @type {!ModuleRegistry} */ ModuleRegistry.getInstance().registerModules(descriptors);
export const registry = new ModuleRegistry(descriptors);
...@@ -17,6 +17,7 @@ export {BrowserProxy} from './browser_proxy.js'; ...@@ -17,6 +17,7 @@ export {BrowserProxy} from './browser_proxy.js';
export {BackgroundSelectionType} from './customize_dialog.js'; export {BackgroundSelectionType} from './customize_dialog.js';
export {dummyDescriptor} from './modules/dummy/module.js'; export {dummyDescriptor} from './modules/dummy/module.js';
export {kaleidoscopeDescriptor} from './modules/kaleidoscope/module.js'; export {kaleidoscopeDescriptor} from './modules/kaleidoscope/module.js';
export {ModuleDescriptor} from './modules/module_descriptor.js';
export {ModuleRegistry} from './modules/module_registry.js'; export {ModuleRegistry} from './modules/module_registry.js';
export {PromoBrowserCommandProxy} from './promo_browser_command_proxy.js'; export {PromoBrowserCommandProxy} from './promo_browser_command_proxy.js';
export {$$, createScrollBorders, decodeString16, hexColorToSkColor, mojoString16, skColorToRgba} from './utils.js'; export {$$, createScrollBorders, decodeString16, hexColorToSkColor, mojoString16, skColorToRgba} from './utils.js';
...@@ -72,6 +72,9 @@ ...@@ -72,6 +72,9 @@
<include name="IDR_NEW_TAB_PAGE_REALBOX_MATCH_JS" <include name="IDR_NEW_TAB_PAGE_REALBOX_MATCH_JS"
file="${root_gen_dir}/chrome/browser/resources/new_tab_page/realbox_match.js" file="${root_gen_dir}/chrome/browser/resources/new_tab_page/realbox_match.js"
use_base_dir="false" type="BINDATA" compress="false" /> use_base_dir="false" type="BINDATA" compress="false" />
<include name="IDR_NEW_TAB_PAGE_MODULE_WRAPPER_JS"
file="${root_gen_dir}/chrome/browser/resources/new_tab_page/module_wrapper.js"
use_base_dir="false" type="BINDATA" compress="false" />
<include name="IDR_NEW_TAB_PAGE_BROWSER_PROXY_JS" <include name="IDR_NEW_TAB_PAGE_BROWSER_PROXY_JS"
file="browser_proxy.js" type="BINDATA" compress="false" /> file="browser_proxy.js" type="BINDATA" compress="false" />
<include name="IDR_NEW_TAB_PAGE_UTILS_JS" <include name="IDR_NEW_TAB_PAGE_UTILS_JS"
......
...@@ -4,25 +4,12 @@ ...@@ -4,25 +4,12 @@
border-radius: calc(0.5 * var(--ntp-realbox-height)); border-radius: calc(0.5 * var(--ntp-realbox-height));
box-shadow: 0 1px 6px 0 rgba(32, 33, 36, .28); box-shadow: 0 1px 6px 0 rgba(32, 33, 36, .28);
height: var(--ntp-realbox-height); height: var(--ntp-realbox-height);
width: 337px;
} }
:host([matches-are-visible]) { :host([matches-are-visible]) {
box-shadow: none; box-shadow: none;
} }
@media (min-width: 560px) {
:host {
width: 449px;
}
}
@media (min-width: 672px) {
:host {
width: 561px;
}
}
#inputWrapper { #inputWrapper {
height: 100%; height: 100%;
position: relative; position: relative;
...@@ -30,8 +17,8 @@ ...@@ -30,8 +17,8 @@
input { input {
background-color: var(--search-box-bg, white); background-color: var(--search-box-bg, white);
border-radius: calc(0.5 * var(--ntp-realbox-height));
border: none; border: none;
border-radius: calc(0.5 * var(--ntp-realbox-height));
color: var(--search-box-text); color: var(--search-box-text);
font-size: 16px; font-size: 16px;
height: 100%; height: 100%;
......
...@@ -174,6 +174,14 @@ content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) { ...@@ -174,6 +174,14 @@ content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) {
// Theme. // Theme.
{"themeCreatedBy", IDS_NEW_TAB_ATTRIBUTION_INTRO}, {"themeCreatedBy", IDS_NEW_TAB_ATTRIBUTION_INTRO},
// Modules.
{"modulesDummyName", IDS_NTP_MODULES_DUMMY_NAME},
{"modulesDummyTitle", IDS_NTP_MODULES_DUMMY_TITLE},
{"modulesDummy2Name", IDS_NTP_MODULES_DUMMY2_NAME},
{"modulesDummy2Title", IDS_NTP_MODULES_DUMMY2_TITLE},
{"modulesKaleidoscopeName", IDS_NTP_MODULES_KALEIDOSCOPE_NAME},
{"modulesKaleidoscopeTitle", IDS_NTP_MODULES_KALEIDOSCOPE_TITLE},
}; };
AddLocalizedStringsBulk(source, kStrings); AddLocalizedStringsBulk(source, kStrings);
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import {$$, BackgroundManager, BackgroundSelectionType, BrowserProxy, PromoBrowserCommandProxy} from 'chrome://new-tab-page/new_tab_page.js'; import {$$, BackgroundManager, BackgroundSelectionType, BrowserProxy, ModuleRegistry, PromoBrowserCommandProxy} from 'chrome://new-tab-page/new_tab_page.js';
import {isMac} from 'chrome://resources/js/cr.m.js'; import {isMac} from 'chrome://resources/js/cr.m.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
import {assertNotStyle, assertStyle, createTestProxy, createTheme} from 'chrome://test/new_tab_page/test_support.js'; import {assertNotStyle, assertStyle, createTestProxy, createTheme} from 'chrome://test/new_tab_page/test_support.js';
import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js'; import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js'; import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js';
...@@ -25,6 +26,9 @@ suite('NewTabPageAppTest', () => { ...@@ -25,6 +26,9 @@ suite('NewTabPageAppTest', () => {
*/ */
let backgroundManager; let backgroundManager;
/** @type {PromiseResolver} */
let moduleResolver;
suiteSetup(() => { suiteSetup(() => {
loadTimeData.overrideValues({ loadTimeData.overrideValues({
realboxEnabled: false, realboxEnabled: false,
...@@ -57,6 +61,10 @@ suite('NewTabPageAppTest', () => { ...@@ -57,6 +61,10 @@ suite('NewTabPageAppTest', () => {
backgroundManager.setResultFor( backgroundManager.setResultFor(
'getBackgroundImageLoadTime', Promise.resolve(0)); 'getBackgroundImageLoadTime', Promise.resolve(0));
BackgroundManager.instance_ = backgroundManager; BackgroundManager.instance_ = backgroundManager;
const moduleRegistry = TestBrowserProxy.fromClass(ModuleRegistry);
moduleResolver = new PromiseResolver();
moduleRegistry.setResultFor('initializeModules', moduleResolver.promise);
ModuleRegistry.instance_ = moduleRegistry;
app = document.createElement('ntp-app'); app = document.createElement('ntp-app');
document.body.appendChild(app); document.body.appendChild(app);
...@@ -423,4 +431,36 @@ suite('NewTabPageAppTest', () => { ...@@ -423,4 +431,36 @@ suite('NewTabPageAppTest', () => {
const {data: commandExecuted} = await eventToPromise('message', window); const {data: commandExecuted} = await eventToPromise('message', window);
assertTrue(commandExecuted); assertTrue(commandExecuted);
}); });
suite('modules', () => {
suiteSetup(() => {
loadTimeData.overrideValues({
modulesEnabled: true,
});
});
test('modules appended to page', async () => {
// Act.
moduleResolver.resolve([
{
id: 'foo',
name: 'Foo',
element: document.createElement('div'),
title: 'Foo Title',
},
{
id: 'bar',
name: 'Bar',
element: document.createElement('div'),
title: 'Bar Title',
}
]);
await flushTasks(); // Wait for module descriptor resolution.
$$(app, '#modules').render();
// Assert.
const modules = app.shadowRoot.querySelectorAll('ntp-module-wrapper');
assertEquals(2, modules.length);
});
});
}); });
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {$$} from 'chrome://new-tab-page/new_tab_page.js';
suite('NewTabPageModuleWrapperTest', () => {
/** @type {!ModuleWrapperElement} */
let moduleWrapper;
setup(() => {
PolymerTest.clearBody();
moduleWrapper = document.createElement('ntp-module-wrapper');
document.body.appendChild(moduleWrapper);
});
test('renders module descriptor', async () => {
// Arrange.
const moduleElement = document.createElement('div');
// Act.
moduleWrapper.descriptor = {
id: 'foo',
name: 'Foo',
title: 'Foo Title',
element: moduleElement,
};
// Assert.
assertEquals('Foo Title', moduleWrapper.$.title.textContent);
assertEquals(' • Foo', moduleWrapper.$.name.textContent);
assertDeepEquals(
moduleElement, $$(moduleWrapper, '#moduleElement').children[0]);
});
});
...@@ -6,9 +6,14 @@ import {$$, dummyDescriptor} from 'chrome://new-tab-page/new_tab_page.js'; ...@@ -6,9 +6,14 @@ import {$$, dummyDescriptor} from 'chrome://new-tab-page/new_tab_page.js';
import {isVisible} from 'chrome://test/test_util.m.js'; import {isVisible} from 'chrome://test/test_util.m.js';
suite('NewTabPageModulesDummyModuleTest', () => { suite('NewTabPageModulesDummyModuleTest', () => {
setup(() => {
PolymerTest.clearBody();
});
test('creates module', async () => { test('creates module', async () => {
// Act. // Act.
const module = await dummyDescriptor.create(); await dummyDescriptor.initialize();
const module = dummyDescriptor.element;
document.body.append(module); document.body.append(module);
module.$.tileList.render(); module.$.tileList.render();
......
...@@ -8,7 +8,8 @@ import {isVisible} from 'chrome://test/test_util.m.js'; ...@@ -8,7 +8,8 @@ import {isVisible} from 'chrome://test/test_util.m.js';
suite('NewTabPageModulesKaleidoscopeModuleTest', () => { suite('NewTabPageModulesKaleidoscopeModuleTest', () => {
test('creates module', async () => { test('creates module', async () => {
// Act. // Act.
const module = await kaleidoscopeDescriptor.create(); await kaleidoscopeDescriptor.initialize();
const module = kaleidoscopeDescriptor.element;
document.body.append(module); document.body.append(module);
module.$.tileList.render(); module.$.tileList.render();
......
...@@ -2,35 +2,42 @@ ...@@ -2,35 +2,42 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import {ModuleRegistry} from 'chrome://new-tab-page/new_tab_page.js'; import {ModuleDescriptor, ModuleRegistry} from 'chrome://new-tab-page/new_tab_page.js';
import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
suite('NewTabPageModulesModuleRegistryTest', () => { suite('NewTabPageModulesModuleRegistryTest', () => {
test('instantiates modules', async () => { test('instantiates modules', async () => {
// Arrange. // Arrange.
const fooModule = document.createElement('div'); const fooModule = document.createElement('div');
const bazModule = document.createElement('div'); const bazModule = document.createElement('div');
const registry = new ModuleRegistry([ const bazModuleResolver = new PromiseResolver();
{ ModuleRegistry.getInstance().registerModules([
id: 'foo', new ModuleDescriptor('foo', 'Foo', () => Promise.resolve({
create: () => Promise.resolve(fooModule), element: fooModule,
}, title: 'Foo Title',
{ })),
id: 'bar', new ModuleDescriptor('bar', 'Bar', () => null),
create: () => null, new ModuleDescriptor('baz', 'Baz', () => bazModuleResolver.promise),
},
{
id: 'baz',
create: () => Promise.resolve(bazModule),
}
]); ]);
const container = document.createElement('div');
// Act. // Act.
await registry.instantiateModules(container); const modulesPromise = ModuleRegistry.getInstance().initializeModules();
// Delayed promise resolution to test async module instantiation.
bazModuleResolver.resolve({
element: bazModule,
title: 'Baz Title',
});
const modules = await modulesPromise;
// Assert. // Assert.
assertEquals(2, container.children.length); assertEquals(2, modules.length);
assertDeepEquals(fooModule, container.children[0]); assertEquals('foo', modules[0].id);
assertDeepEquals(bazModule, container.children[1]); assertEquals('Foo', modules[0].name);
assertEquals('Foo Title', modules[0].title);
assertDeepEquals(fooModule, modules[0].element);
assertEquals('baz', modules[1].id);
assertEquals('Baz', modules[1].name);
assertEquals('Baz Title', modules[1].title);
assertDeepEquals(bazModule, modules[1].element);
}); });
}); });
...@@ -197,6 +197,18 @@ TEST_F('NewTabPageBackgroundManagerTest', 'All', function() { ...@@ -197,6 +197,18 @@ TEST_F('NewTabPageBackgroundManagerTest', 'All', function() {
mocha.run(); mocha.run();
}); });
// eslint-disable-next-line no-var
var NewTabPageModuleWrapperTest = class extends NewTabPageBrowserTest {
/** @override */
get browsePreload() {
return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/module_wrapper_test.js';
}
};
TEST_F('NewTabPageModuleWrapperTest', 'All', function() {
mocha.run();
});
// eslint-disable-next-line no-var // eslint-disable-next-line no-var
var NewTabPageModulesModuleRegistryTest = class extends NewTabPageBrowserTest { var NewTabPageModulesModuleRegistryTest = class extends NewTabPageBrowserTest {
/** @override */ /** @override */
......
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