Bug 1519766 - basic List component for DiscoveryStream (WIP), r=thecount (#4654)

This commit is contained in:
Dan Mosedale 2019-01-16 10:09:23 -08:00 коммит произвёл GitHub
Родитель 31ef3d1eca
Коммит 6967f2e7a0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 206 добавлений и 10 удалений

Просмотреть файл

@ -47,7 +47,9 @@ export class _DiscoveryStreamBase extends React.PureComponent {
case "HorizontalRule":
return (<HorizontalRule />);
case "List":
return (<List feed={component.feed} />);
return (<List
feed={component.feed}
header={component.header} />);
default:
return (<div>{component.type}</div>);
}

Просмотреть файл

@ -1,3 +1,8 @@
.outer-wrapper a {
color: $grey-90;
}
.discovery-stream.ds-layout {
$columns: 12;
display: grid;

Просмотреть файл

@ -1,15 +1,60 @@
import {connect} from "react-redux";
import React from "react";
export class _List extends React.PureComponent {
render() {
// const feed = this.props.DiscoveryStream.feeds[this.props.feed.url];
return (
<div className="ds-list">
List
</div>
);
}
/**
* @note exported for testing only
*/
export function ListItem(props) {
// TODO performance: get feeds to send appropriately sized images rather
// than waiting longer and scaling down on client?
return (
<li className="ds-list-item">
<a className="ds-list-item-link" href={props.url}>
<div className="ds-list-item-text">
<div className="ds-list-item-title">
<b>
{props.title}
</b>
</div>
<div className="ds-list-item-info">
{`${props.domain} · TODO:Topic`}
</div>
</div>
<img className="ds-list-image" src={props.image_src} />
</a>
</li>
);
}
/**
* @note exported for testing only
*/
export function _List(props) {
const feed = props.DiscoveryStream.feeds[props.feed.url];
if (!feed || !feed.data || !feed.data.recommendations) {
return null;
}
const recs = feed.data.recommendations;
let recMarkup = recs.slice(0, props.items).map((rec, index) => (
<ListItem {...rec} key={`ds-list-item-$index`} />)
);
return (
<div>
<h3 className="ds-list-title">{props.header.title}</h3>
<hr className="ds-list-border" />
<ul className="ds-list">{recMarkup}</ul>
</div>
);
}
_List.defaultProps = {
items: 6, // Number of stories to display. TODO: get from endpoint
};
export const List = connect(state => ({DiscoveryStream: state.DiscoveryStream}))(_List);

Просмотреть файл

@ -0,0 +1,58 @@
.ds-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-row-gap: 10px;
grid-column-gap: 24px;
// reset some stuff from <ul>. Should maybe be hoisted when we have better
// regression detection?
padding-inline-start: 0;
}
.ds-list-item {
// XXX see if we really want absolute units, maybe hoist somewhere central?
line-height: 20px;
font-size: 13px;
// reset some stuff from <li>. Should maybe be hoisted when we have better
// regression detection?
display: block;
text-align: start;
.ds-list-item-link {
mix-blend-mode: normal;
padding-bottom: 16px;
display: flex;
justify-content: space-between;
}
.ds-list-item-info {
color: $grey-50;
overflow: hidden;
text-overflow: ellipsis;
}
.ds-list-item-title {
margin-bottom: 8px;
padding-bottom: 8px;
}
.ds-list-item-text {
display: flex;
justify-content: space-between;
flex-direction: column;
}
.ds-list-image {
width: 72px;
height: 72px;
object-fit: cover;
border: 0.5px solid $black-12;
box-sizing: border-box;
border-radius: 4px;
}
}

Просмотреть файл

@ -45,6 +45,7 @@ $grey-90-90: rgba($grey-90, 0.9);
$black: #000;
$black-5: rgba($black, 0.05);
$black-10: rgba($black, 0.1);
$black-12: rgba($black, 0.12);
$black-15: rgba($black, 0.15);
$black-20: rgba($black, 0.2);
$black-25: rgba($black, 0.25);

Просмотреть файл

@ -0,0 +1,85 @@
import {_List as List, ListItem} from "content-src/components/DiscoveryStreamComponents/List/List";
import {GlobalOverrider} from "test/unit/utils";
import React from "react";
import {shallow} from "enzyme";
describe("<List> presentation component", () => {
const ValidRecommendations = [{a: 1}, {a: 2}];
const ValidListProps = {
DiscoveryStream: {
feeds: {
fakeFeedUrl: {
data: {
recommendations: ValidRecommendations,
},
},
},
},
feed: {
url: "fakeFeedUrl",
},
header: {
title: "fakeFeedTitle",
},
};
it("should return null if feed.data is falsy", () => {
const ListProps = {
DiscoveryStream: {feeds: {a: "stuff"}},
feed: {url: "a"},
};
const wrapper = shallow(<List {...ListProps} />);
assert.isNull(wrapper.getElement());
});
it("should return something containing a <ul> if props are valid", () => {
const wrapper = shallow(<List {...ValidListProps} />);
const list = wrapper.find("ul");
assert.ok(wrapper.exists());
assert.lengthOf(list, 1);
});
it("should return the right number of ListItems if props are valid", () => {
const wrapper = shallow(<List {...ValidListProps} />);
const listItem = wrapper.find(ListItem);
assert.lengthOf(listItem, ValidRecommendations.length);
});
});
describe("<ListItem> presentation component", () => {
const ValidListItemProps = {
url: "FAKE_URL",
title: "FAKE_TITLE",
domain: "example.com",
image_src: "FAKE_IMAGE_SRC",
};
let globals;
beforeEach(() => {
globals = new GlobalOverrider();
});
afterEach(() => {
globals.sandbox.restore();
});
it("should contain 'a.ds-list-item-link' with the props.url set", () => {
const wrapper = shallow(<ListItem {...ValidListItemProps} />);
const anchors = wrapper.find(
`a.ds-list-item-link[href="${ValidListItemProps.url}"]`);
assert.lengthOf(anchors, 1);
});
it("should include an img with a src of props.image_src", () => {
const wrapper = shallow(<ListItem {...ValidListItemProps} />);
const anchors = wrapper.find(`img[src="${ValidListItemProps.image_src}"]`);
assert.lengthOf(anchors, 1);
});
});