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

[ntp][modules] Render shopping module

Screenshot: https://screenshot.googleplex.com/8XenkGQNbhfpQKv

Fixed: 1130855
Change-Id: Ieb2ab938047b5fcc4fe1bc689661369ec5bf22c1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2431783
Commit-Queue: Tibor Goldschwendt <tiborg@chromium.org>
Reviewed-by: default avatarEsmael Elmoslimany <aee@chromium.org>
Cr-Commit-Position: refs/heads/master@{#811009}
parent 58957734
...@@ -70,13 +70,13 @@ ...@@ -70,13 +70,13 @@
ntp-fakebox, ntp-fakebox,
ntp-realbox { ntp-realbox {
flex-shrink: 0;
margin-bottom: 32px; margin-bottom: 32px;
} }
ntp-fakebox, ntp-fakebox,
ntp-realbox, ntp-realbox,
ntp-module-wrapper { ntp-module-wrapper {
flex-shrink: 0;
width: var(--ntp-search-box-width); width: var(--ntp-search-box-width);
} }
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
background-color: var(--ntp-background-override-color); background-color: var(--ntp-background-override-color);
border: solid var(--ntp-border-color) 1px; border: solid var(--ntp-border-color) 1px;
border-radius: 5px; border-radius: 5px;
display: block;
overflow: hidden;
} }
#header { #header {
...@@ -25,7 +27,6 @@ ...@@ -25,7 +27,6 @@
align-items: center; align-items: center;
display: flex; display: flex;
justify-content: center; justify-content: center;
overflow: hidden;
} }
</style> </style>
<div id="header"> <div id="header">
......
<style>
:host {
box-sizing: border-box;
display: block;
height: 100%;
padding-inline-end: 15px;
padding-inline-start: 15px;
width: 100%;
}
#products {
display: flex;
flex-direction: row;
}
.product {
border-radius: 4px;
display: flex;
flex-direction: column;
outline: none;
text-decoration: none;
width: 120px;
}
:host-context(.focus-outline-visible) .product:focus {
box-shadow: var(--ntp-focus-shadow);
}
.product + .product {
margin-inline-start: 16px;
}
.image {
background: #F6F6F6;
border-radius: 4px;
box-sizing: border-box;
height: 120px;
margin-bottom: 8px;
padding: 10px;
width: 120px;
}
img {
height: 100%;
object-fit: contain;
width: 100%;
}
.price {
color: var(--cr-primary-text-color);
font-size: 13px;
font-weight: bold;
height: 14px;
line-height: 15px;
margin-bottom: 8px;
}
.name {
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
color: var(--cr-primary-text-color);
display: -webkit-box;
font-size: 12px;
height: 40px;
line-height: 20px;
margin-bottom: 5px;
overflow: hidden;
}
.info {
color: var(--cr-secondary-text-color);
font-size: 11px;
height: 13px;
text-overflow: ellipsis;
}
#relatedSearches {
display: flex;
flex-direction: row;
margin-top: 16px;
}
.pill {
align-items: center;
border: solid var(--ntp-border-color) 1px;
border-radius: 16px;
box-sizing: border-box;
display: flex;
flex-direction: row;
flex-shrink: 0;
height: 32px;
outline: none;
text-decoration: none;
}
:host-context(.focus-outline-visible) .pill:focus {
box-shadow: var(--ntp-focus-shadow);
}
.pill + .pill {
margin-inline-start: 8px;
}
.loupe {
-webkit-mask-image: url(search.svg);
-webkit-mask-repeat: no-repeat;
-webkit-mask-size: 100%;
background-color: var(--cr-secondary-text-color);
height: 16px;
margin-inline-start: 12px;
width: 16px;
}
.search-text {
color: var(--cr-primary-text-color);
font-size: 13px;
margin-inline-end: 12px;
margin-inline-start: 8px;
}
</style>
<div id="products">
<template is="dom-repeat" id="productsRepeat"
items="[[shoppingTask.products]]">
<a class="product" href="[[item.targetUrl.url]]">
<div class="image">
<img is="ntp-img" auto-src="[[item.imageUrl.url]]"></img>
</div>
<div class="price">[[item.price]]</div>
<div class="name" title="[[item.name]]">[[item.name]]</div>
<div class="info">[[item.info]]</div>
</a>
</template>
</div>
<div id="relatedSearches">
<template is="dom-repeat" id="relatedSearchesRepeat"
items="[[shoppingTask.relatedSearches]]">
<a class="pill" href="[[item.targetUrl.url]]">
<div class="loupe"></div>
<div class="search-text">[[item.text]]</div>
</a>
</template>
</div>
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
// 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 {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import '../../img.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';
import {ShoppingTasksHandlerProxy} from './shopping_tasks_handler_proxy.js'; import {ShoppingTasksHandlerProxy} from './shopping_tasks_handler_proxy.js';
...@@ -22,25 +23,34 @@ class ShoppingTasksModuleElement extends PolymerElement { ...@@ -22,25 +23,34 @@ class ShoppingTasksModuleElement extends PolymerElement {
return html`{__html_template__}`; return html`{__html_template__}`;
} }
constructor() { static get properties() {
super(); return {
ShoppingTasksHandlerProxy.getInstance() /** @type {shoppingTasks.mojom.ShoppingTask} */
.handler.getPrimaryShoppingTask() shoppingTask: Object,
.then(data => { };
// TODO(crbug.com/1130855): Do something useful with the data.
console.log(data);
});
} }
} }
customElements.define( customElements.define(
ShoppingTasksModuleElement.is, ShoppingTasksModuleElement); ShoppingTasksModuleElement.is, ShoppingTasksModuleElement);
/** @return {!Promise<?{element: !HTMLElement, title: string}>} */
async function createModule() {
const {shoppingTask} = await ShoppingTasksHandlerProxy.getInstance()
.handler.getPrimaryShoppingTask();
if (!shoppingTask) {
return null;
}
const element = new ShoppingTasksModuleElement();
element.shoppingTask = shoppingTask;
return {
element: element,
title: shoppingTask.title,
};
}
/** @type {!ModuleDescriptor} */ /** @type {!ModuleDescriptor} */
export const shoppingTasksDescriptor = new ModuleDescriptor( export const shoppingTasksDescriptor = new ModuleDescriptor(
/*id=*/ 'shopping_tasks', /*id=*/ 'shopping_tasks',
/*name=*/ 'Shopping Tasks', /*name=*/ 'Shopping Tasks',
/*heightPx=*/ 260, () => Promise.resolve({ /*heightPx=*/ 270, createModule);
element: new ShoppingTasksModuleElement(),
title: 'Shopping Tasks',
}));
...@@ -18,18 +18,88 @@ suite('NewTabPageModulesShoppingTasksModuleTest', () => { ...@@ -18,18 +18,88 @@ suite('NewTabPageModulesShoppingTasksModuleTest', () => {
testProxy = TestBrowserProxy.fromClass(ShoppingTasksHandlerProxy); testProxy = TestBrowserProxy.fromClass(ShoppingTasksHandlerProxy);
testProxy.handler = TestBrowserProxy.fromClass( testProxy.handler = TestBrowserProxy.fromClass(
shoppingTasks.mojom.ShoppingTasksHandlerRemote); shoppingTasks.mojom.ShoppingTasksHandlerRemote);
testProxy.handler.setResultFor(
'getPrimaryShoppingTask', Promise.resolve(null));
ShoppingTasksHandlerProxy.instance_ = testProxy; ShoppingTasksHandlerProxy.instance_ = testProxy;
}); });
test('creates module', async () => { test('creates no module if no task', async () => {
// Arrange.
testProxy.handler.setResultFor(
'getPrimaryShoppingTask', Promise.resolve({shoppingTask: null}));
// Act.
await shoppingTasksDescriptor.initialize();
// Assert.
assertEquals(1, testProxy.handler.getCallCount('getPrimaryShoppingTask'));
assertEquals(null, shoppingTasksDescriptor.element);
});
test('creates module if task', async () => {
// Arrange.
const shoppingTask = {
title: 'Hello world',
products: [
{
name: 'foo',
imageUrl: {url: 'https://foo.com/img.png'},
price: '1 gazillion dollars',
info: 'foo info',
targetUrl: {url: 'https://foo.com'},
},
{
name: 'bar',
imageUrl: {url: 'https://bar.com/img.png'},
price: '2 gazillion dollars',
info: 'bar info',
targetUrl: {url: 'https://bar.com'},
},
],
relatedSearches: [
{
text: 'baz',
targetUrl: {url: 'https://baz.com'},
},
{
text: 'blub',
targetUrl: {url: 'https://blub.com'},
},
],
};
testProxy.handler.setResultFor(
'getPrimaryShoppingTask', Promise.resolve({shoppingTask}));
// Act. // Act.
await shoppingTasksDescriptor.initialize(); await shoppingTasksDescriptor.initialize();
const module = shoppingTasksDescriptor.element; const module = shoppingTasksDescriptor.element;
document.body.append(module); document.body.append(module);
module.$.productsRepeat.render();
module.$.relatedSearchesRepeat.render();
// Assert. // Assert.
const products = Array.from(module.shadowRoot.querySelectorAll('.product'));
const pills = Array.from(module.shadowRoot.querySelectorAll('.pill'));
assertEquals(1, testProxy.handler.getCallCount('getPrimaryShoppingTask')); assertEquals(1, testProxy.handler.getCallCount('getPrimaryShoppingTask'));
assertEquals(2, products.length);
assertEquals(2, pills.length);
assertEquals('https://foo.com/', products[0].href);
assertEquals(
'https://foo.com/img.png', products[0].querySelector('img').autoSrc);
assertEquals(
'1 gazillion dollars', products[0].querySelector('.price').innerText);
assertEquals('foo', products[0].querySelector('.name').innerText);
assertEquals('foo', products[0].querySelector('.name').title);
assertEquals('foo info', products[0].querySelector('.info').innerText);
assertEquals('https://bar.com/', products[1].href);
assertEquals(
'https://bar.com/img.png', products[1].querySelector('img').autoSrc);
assertEquals(
'2 gazillion dollars', products[1].querySelector('.price').innerText);
assertEquals('bar', products[1].querySelector('.name').innerText);
assertEquals('bar', products[1].querySelector('.name').title);
assertEquals('bar info', products[1].querySelector('.info').innerText);
assertEquals('https://baz.com/', pills[0].href);
assertEquals('baz', pills[0].querySelector('.search-text').innerText);
assertEquals('https://blub.com/', pills[1].href);
assertEquals('blub', pills[1].querySelector('.search-text').innerText);
}); });
}); });
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