Skip to content
This repository was archived by the owner on Mar 12, 2020. It is now read-only.

Commit e39bfda

Browse files
authored
[WIP] Add: teaserPresenter (#12)
* add presenter and start tests * test rest of presenter features * add linting to make verify * add tint to headshot presenter * migrate to image service v2 for headshots * add display tag presenter function * add alt attribute to presenter headshot function * add class modifiers presenter function * first attempt at converting templates to new content model * correct path to presenter * updating presenter model * get presenter working as node module * move to partials * increase specificity of partials location * top story split into heavy and standard * references for related content partial * references for related content partial properly * stream page logic for display tag * add timestamp modifier and genre tag to partials * remove @Financial-Times from teaser referencing * tweaks to templates and partials * linting whitespace * add presenter for genre prefix * swap to genre filter by id and add q&a review interview * add headshot to standard template * remove scss_lint from gitignore * Add class to timestamp * add extra class modifiers * add premium label to title * update templates to old n-teaser levels * align premium label to old data model markup * add optional display tag to extra light template * add extra data to extra light fragment * correct mainImage properties passed to nImage * updates to data model & fragment based on actual data * link to the concept not article in tag * add lifestyle teaser * titles down to h3 * double brand author display logic * Revert "double brand author display logic" This reverts commit 766d06e. * escape characters in title
1 parent b50067a commit e39bfda

29 files changed

+854
-165
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
node_modules/
22
bower_components/
3+
.editorconfig
4+
.eslintrc.js

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
11
include n.Makefile
2+
3+
unit-test:
4+
mocha 'tests/**/*.spec.js' --inline-diffs
5+
6+
test: verify unit-test

demos/index.html

Lines changed: 0 additions & 18 deletions
This file was deleted.

demos/script.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const Handlebars = require('handlebars');
2+
13
const data = {
24
img: {
35
alt: 'demo image'

demos/style.css

Lines changed: 0 additions & 11 deletions
This file was deleted.

main.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
module.exports = {
2-
fragments: require('./data_model/fragments')
2+
fragments: require('./src/data-model/fragments'),
3+
presenter: require('./src/handlebars-helpers/nTeaserPresenter')
34
}

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@
88
"url": "git+https://github.com/Financial-Times/n-teaser.git"
99
},
1010
"license": "MIT",
11+
"dependencies": {
12+
13+
},
1114
"devDependencies": {
1215
"bower": "^1.7.9",
13-
"npm-prepublish": "^1.2.1"
16+
"npm-prepublish": "^1.2.1",
17+
"chai": "^3.2.0",
18+
"eslint": "^2.10.2",
19+
"lintspaces-cli": "^0.1.1",
20+
"mocha": "^2.2.5"
1421
}
1522
}

data_model/fragments.js renamed to src/data-model/fragments.js

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,15 @@ module.exports = {
1111
teaserExtraLight: `
1212
fragment TeaserExtraLight on Content {
1313
type: __typename
14+
id
1415
isPremium
1516
relativeUrl
1617
title
1718
promotionalTitle
1819
publishedDate
1920
initialPublishedDate
20-
}
21-
`,
22-
teaserLight: `
23-
fragment TeaserLight on Content {
24-
id
25-
teaserTag {
26-
prefLabel
27-
relativeUrl
28-
}
29-
genreTag(only: ["Analysis"]) {
21+
isEditorsChoice
22+
genreTag(only: ["MQ==-R2VucmVz", "Mw==-R2VucmVz", "OQ==-R2VucmVz", "NA==-R2VucmVz", "MTA=-R2VucmVz"]) {
3023
prefLabel
3124
}
3225
primaryBrandTag {
@@ -40,6 +33,27 @@ module.exports = {
4033
isOpinion
4134
}
4235
`,
36+
teaserLight: `
37+
fragment TeaserLight on Content {
38+
id
39+
teaserTag {
40+
prefLabel
41+
relativeUrl
42+
}
43+
}
44+
`,
45+
teaserLifestyle: `
46+
fragment TeaserLifestyle on Content {
47+
mainImage {
48+
title
49+
description
50+
url
51+
width
52+
height
53+
ratio
54+
}
55+
}
56+
`,
4357
teaserStandard: `
4458
fragment TeaserStandard on Content {
4559
standfirst
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
const TeaserPresenter = require('../presenters/teaser-presenter');
3+
4+
module.exports = (context, options) => {
5+
if (options.hash) Object.assign(context, options.hash);
6+
if (options.data) {
7+
const nTeaserPresenter = new TeaserPresenter(context);
8+
return options.fn(context, {data: { nTeaserPresenter } } );
9+
}
10+
};

src/presenters/teaser-presenter.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
const ONE_HOUR = 1000 * 60 * 60;
2+
const MAX_RELATED_CONTENT = 3;
3+
const HEADSHOT_BASE_URL = 'https://www.ft.com/__origami/service/image/v2/images/raw/';
4+
const HEADSHOT_URL_PARAMS = '?source=next&fit=scale-down&compression=best&width=75&tint=054593,fff1e0';
5+
const TEMPLATES_WITH_HEADSHOTS = ['light','standard','lifestyle'];
6+
const TEMPLATES_WITH_IMAGES = ['heavy', 'top-story-heavy','lifestyle'];
7+
const LIVEBLOG_MAPPING = {
8+
inprogress: 'last post',
9+
comingsoon: 'coming soon',
10+
closed: 'liveblog closed'
11+
};
12+
13+
const TeaserPresenter = class TeaserPresenter {
14+
15+
constructor (data) {
16+
this.data = data || {};
17+
}
18+
19+
// returns all top level class names appropriate for the teaser
20+
get classModifiers () {
21+
const mods = this.data.mods || [];
22+
if (
23+
this.headshot &&
24+
TEMPLATES_WITH_HEADSHOTS.some(template => template === this.data.template)
25+
) {
26+
mods.push('has-headshot');
27+
}
28+
if (this.data.size) mods.push(this.data.size);
29+
if (
30+
this.data.mainImage &&
31+
TEMPLATES_WITH_IMAGES.some(template => template === this.data.template)
32+
) {
33+
mods.push('has-image');
34+
}
35+
if (this.data.isOpinion) {
36+
mods.push('opinion');
37+
}
38+
if (this.data.isEditorsChoice) {
39+
mods.push('highlight');
40+
}
41+
return mods;
42+
}
43+
44+
//returns tag to be displayed
45+
get displayTag () {
46+
if ((this.data.streamId && this.data.primaryBrandTag) &&
47+
(this.data.streamId === this.data.primaryBrandTag.idV1)) {
48+
return this.data.teaserTag || null;
49+
}
50+
return this.data.primaryBrandTag || this.data.teaserTag || null;
51+
}
52+
53+
//returns genre prefix
54+
get genrePrefix () {
55+
if (!this.data.genreTag || this.data.primaryBrandTag === this.displayTag) {
56+
return null;
57+
}
58+
return this.data.genreTag.prefLabel;
59+
}
60+
61+
//returns publishedDate, status, classModifier
62+
get timeObject () {
63+
if (this.data.liveBlog && this.data.liveBlog.status) {
64+
return this.liveBlog();
65+
} else {
66+
return {
67+
publishedDate: this.data.publishedDate,
68+
status: this.timeStatus(),
69+
classModifier: this.timeStatus()
70+
}
71+
}
72+
}
73+
74+
// returns an array of content items related to the main article
75+
get relatedContent () {
76+
if (this.data.storyPackage.length > 0) {
77+
return this.data.storyPackage.slice(0, MAX_RELATED_CONTENT);
78+
} else {
79+
return this.data.primaryTag.latestContent
80+
.filter(content => content.id !== this.data.id)
81+
.slice(0, MAX_RELATED_CONTENT);
82+
}
83+
}
84+
85+
// returns url and name for author headshot when primary brand tag is an author with a headshot
86+
get headshot () {
87+
if (this.data.primaryBrandTag && this.data.primaryBrandTag.attributes.length > 0) {
88+
const fileName = this.data.primaryBrandTag.attributes[0].value;
89+
return {
90+
src: `${HEADSHOT_BASE_URL}${fileName}${HEADSHOT_URL_PARAMS}`,
91+
alt: this.data.primaryBrandTag.prefLabel
92+
};
93+
} else {
94+
return null;
95+
}
96+
}
97+
98+
//returns prefix for timestamp (null / 'new' / 'updated')
99+
timeStatus () {
100+
const now = Date.now();
101+
const publishedDate = new Date(this.data.publishedDate).getTime();
102+
const initialPublishedDate = new Date(this.data.initialPublishedDate).getTime();
103+
let status = null;
104+
if (now - publishedDate < ONE_HOUR) {
105+
if (publishedDate === initialPublishedDate) {
106+
status = 'new';
107+
} else {
108+
status = 'updated';
109+
}
110+
}
111+
return status
112+
}
113+
114+
// returns publishedDate, status, classModifier
115+
liveBlog () {
116+
return {
117+
publishedDate: this.data.liveBlog.latestUpdate && this.data.liveBlog.latestUpdate.date,
118+
status: LIVEBLOG_MAPPING[this.data.liveBlog.status],
119+
classModifier: this.data.liveBlog.status
120+
}
121+
}
122+
123+
};
124+
125+
module.exports = TeaserPresenter;

templates/extra-light.html

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
<div class="o-teaser{{#mods}} o-teaser--{{this}}{{/mods}}" data-o-component="o-teaser" data-trackable="teaser">
2-
<div class="o-teaser__content">
3-
{{> n-teaser/templates/partials/heading}}
1+
{{#nTeaserPresenter @this template='extra-light' mods=mods}}
2+
<div class="o-teaser{{#each @nTeaserPresenter.classModifiers}} o-teaser--{{this}}{{/each}}" data-o-component="o-teaser" data-trackable="teaser">
3+
<div class="o-teaser__content">
44

5-
{{#if lastPublished}}
6-
<time data-o-component="o-date" class="o-date o-teaser__timestamp" data-o-date-format="time-ago-limit-4-hours" datetime="{{lastPublished}}">{{#dateformat "dddd, d mmmm, yyyy"}}{{lastPublished}}{{/dateformat}}</time>
7-
{{/if}}
5+
{{#ifSome isOpinion primaryBrandTag}}
6+
{{> n-teaser/templates/partials/display-tag}}
7+
{{/ifSome}}
8+
9+
{{> n-teaser/templates/partials/title}}
10+
11+
{{> n-teaser/templates/partials/timestamp}}
12+
13+
</div>
814
</div>
9-
</div>
15+
{{/nTeaserPresenter}}

templates/heavy.html

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
<div class="o-teaser{{#mods}} o-teaser--{{this}}{{/mods}}{{#if image}} o-teaser--has-image{{/if}}{{#ifEquals type 'opinion'}} o-teaser--opinion{{/ifEquals}}{{#ifEquals type 'editors-pick'}} o-teaser--highlight{{/ifEquals}}" data-o-component="o-teaser" data-trackable="teaser">
2-
<div class="o-teaser__content">
3-
{{> n-teaser/templates/partials/meta}}
4-
{{> n-teaser/templates/partials/heading}}
5-
6-
{{#if summary}}
7-
<p class="o-teaser__standfirst">{{{summary}}}</p>
8-
{{/if}}
9-
10-
{{#if lastPublished}}
11-
<time data-o-component="o-date" class="o-date o-teaser__timestamp" data-o-date-format="time-ago-limit-4-hours" datetime="{{lastPublished}}">{{#dateformat "dddd, d mmmm, yyyy"}}{{lastPublished}}{{/dateformat}}</time>
12-
{{/if}}
13-
14-
{{#actions}}
15-
<div class="o-teaser__action">
16-
{{{this}}}
17-
</div>
18-
{{/actions}}
19-
</div>
1+
{{#nTeaserPresenter @this template='heavy' mods=mods}}
2+
<div class="o-teaser{{#each @nTeaserPresenter.classModifiers}} o-teaser--{{this}}{{/each}}" data-o-component="o-teaser" data-trackable="teaser">
3+
<div class="o-teaser__content">
4+
5+
{{> n-teaser/templates/partials/display-tag}}
6+
7+
{{> n-teaser/templates/partials/title}}
8+
9+
{{> n-teaser/templates/partials/standfirst}}
10+
11+
{{> n-teaser/templates/partials/timestamp}}
2012

21-
{{>n-teaser/templates/partials/image}}
22-
</div>
13+
{{> n-teaser/templates/partials/actions}}
14+
15+
</div>
16+
17+
{{> n-teaser/templates/partials/main-image}}
18+
19+
</div>
20+
{{/nTeaserPresenter}}

templates/lifestyle.html

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{{#nTeaserPresenter @this template='lifestyle' mods=mods}}
2+
<div class="o-teaser{{#each @nTeaserPresenter.classModifiers}} o-teaser--{{this}}{{/each}}" data-o-component="o-teaser" data-trackable="teaser">
3+
4+
{{> n-teaser/templates/partials/main-image}}
5+
6+
<div class="o-teaser__content">
7+
8+
{{> n-teaser/templates/partials/display-tag}}
9+
10+
{{> n-teaser/templates/partials/title}}
11+
12+
{{> n-teaser/templates/partials/timestamp}}
13+
14+
{{> n-teaser/templates/partials/headshot}}
15+
16+
</div>
17+
</div>
18+
{{/nTeaserPresenter}}

templates/light.html

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
<div class="o-teaser{{#mods}} o-teaser--{{this}}{{/mods}}{{#if brand.headshot}} o-teaser--has-headshot{{/if}}{{#ifEquals type 'opinion'}} o-teaser--opinion{{/ifEquals}}{{#ifEquals type 'editors-pick'}} o-teaser--highlight{{/ifEquals}}" data-o-component="o-teaser" data-trackable="teaser">
2-
<div class="o-teaser__content">
3-
{{> n-teaser/templates/partials/meta}}
4-
{{> n-teaser/templates/partials/heading}}
1+
{{#nTeaserPresenter @this template='light' mods=mods}}
2+
<div class="o-teaser{{#each @nTeaserPresenter.classModifiers}} o-teaser--{{this}}{{/each}}" data-o-component="o-teaser" data-trackable="teaser">
3+
<div class="o-teaser__content">
54

6-
{{#if lastPublished}}
7-
<time data-o-component="o-date" class="o-date o-teaser__timestamp" data-o-date-format="time-ago-limit-4-hours" datetime="{{lastPublished}}">{{#dateformat "dddd, d mmmm, yyyy"}}{{lastPublished}}{{/dateformat}}</time>
8-
{{/if}}
5+
{{> n-teaser/templates/partials/display-tag}}
96

10-
{{#actions}}
11-
<div class="o-teaser__action">
12-
{{{this}}}
13-
</div>
14-
{{/actions}}
7+
{{> n-teaser/templates/partials/title}}
158

16-
{{#if brand.headshot}}
17-
<img src="{{brand.headshot}}?source=next&amp;width=75&amp;tint=054593,fff1e0" alt="{{#if brand.name}}Photo of {{brand.name}}{{/if}}" class="o-teaser__headshot">
18-
{{/if}}
19-
9+
{{> n-teaser/templates/partials/timestamp}}
10+
11+
{{> n-teaser/templates/partials/actions}}
12+
13+
{{> n-teaser/templates/partials/headshot}}
14+
15+
</div>
2016
</div>
21-
</div>
17+
{{/nTeaserPresenter}}

templates/partials/actions.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{{#actions}}
2+
<div class="o-teaser__action">
3+
{{{this}}}
4+
</div>
5+
{{/actions}}

0 commit comments

Comments
 (0)