Skip to content
This repository was archived by the owner on Sep 1, 2021. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/components/blocks/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React from 'react';
import * as AssetUtils from 'utils/Asset';
import { BlockElement } from 'types/capi-thrift-models';

// ----- Setup ----- //

Expand All @@ -17,6 +18,9 @@ interface Image {

// ----- Functions ----- //

const isImage = (elem: BlockElement): boolean =>
elem.type === 'image';

const imageElement = (alt: string, assets: AssetUtils.Asset[], salt: string): React.ReactNode =>
h('img', {
sizes: '100%',
Expand All @@ -39,6 +43,7 @@ function imageBlock(image: Image, assets: AssetUtils.Asset[], salt: string): Rea
// ----- Exports ----- //

export {
isImage,
imageBlock,
imageElement
};
111 changes: 53 additions & 58 deletions src/components/liveblog/LiveblogArticle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import LiveblogKeyEvents from './LiveblogKeyEvents';
import LiveblogBody from './LiveblogBody';
import HeaderImage from '../shared/HeaderImage';
import Tags from '../shared/Tags';
import { Option } from 'types/Option';
import { PillarStyles, PillarId, wideColumnWidth, baseMultiply } from 'styles';
import { Series, Contributor } from '../../types/Capi';
import { Tag, Block, BlockElement } from 'types/capi-thrift-models';
import { PillarStyles, wideColumnWidth, baseMultiply, getPillarStyles } from 'styles';
import { Tag } from 'types/capi-thrift-models';
import { css, SerializedStyles } from '@emotion/core'
import { palette, wide } from '@guardian/src-foundations'
import { Env } from 'types/Env';
import { Reader } from 'types/Reader';
import { isImage } from 'components/blocks/image';
import { fromNullable } from 'types/Option';

const LiveblogArticleStyles: SerializedStyles = css`
background: ${palette.neutral[97]};
Expand Down Expand Up @@ -43,62 +45,55 @@ const HeaderImageStyles = (pillarStyles: PillarStyles): SerializedStyles => css`
`;

interface LiveblogArticleProps {
headline: string;
standfirst: string;
bylineHtml: string;
webPublicationDate: string;
body: string;
tags: Tag[];
pillarId: PillarId;
mainImage: Option<BlockElement>;
pillarStyles: PillarStyles;
contributors: Contributor[];
series: Series;
bodyElements: Block[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
capi: any;
isLive: boolean;
imageSalt: string;
}

const LiveblogArticle = ({
headline,
standfirst,
bylineHtml,
webPublicationDate,
pillarId,
tags,
mainImage,
pillarStyles,
contributors,
series,
bodyElements,
imageSalt
}: LiveblogArticleProps): JSX.Element =>
<main css={LiveblogArticleStyles}>
<div css={BorderStyles}>
<LiveblogSeries series={series} pillarStyles={pillarStyles}/>
<LiveblogHeadline headline={headline} pillarStyles={pillarStyles}/>
<LiveblogStandfirst standfirst={standfirst} pillarStyles={pillarStyles}/>
<LiveblogByline
byline={bylineHtml}
pillarStyles={pillarStyles}
pillarId={pillarId}
publicationDate={webPublicationDate}
contributors={contributors}
imageSalt={imageSalt}
/>
<HeaderImage
image={mainImage}
imageSalt={imageSalt}
className={HeaderImageStyles(pillarStyles)}
/>
<LiveblogKeyEvents bodyElements={bodyElements} pillarStyles={pillarStyles}/>
<LiveblogBody
bodyElements={bodyElements}
pillarStyles={pillarStyles}
imageSalt={imageSalt}
/>
<Tags tags={tags} background={palette.neutral[93]}/>
</div>
</main>
function LiveblogArticle({ capi }: LiveblogArticleProps): Reader<Env, JSX.Element> {

const { type, fields, tags, webPublicationDate, pillarId, blocks } = capi;
const [series] = tags.filter((tag: Tag) => tag.type === 'series');
const pillarStyles = getPillarStyles(pillarId);
const contributors = tags.filter((tag: Tag) => tag.type === 'contributor');
const bodyElements = type === 'liveblog' ? blocks.body : blocks.body[0].elements;
const image = fromNullable(blocks.main.elements.filter(isImage)[0]);

const headerImage = HeaderImage({ image, className: HeaderImageStyles(pillarStyles) });
const liveblogByline = LiveblogByline({
byline: fields.bylineHtml,
pillarId,
publicationDate: webPublicationDate,
contributors,
});
const liveblogBody = LiveblogBody({ bodyElements, pillarStyles });

return Reader.sequence([ headerImage, liveblogByline, liveblogBody ])
.map(([ headerImg, byline, body ]) =>
// This is not an iterator, ESLint is confused
// eslint-disable-next-line react/jsx-key
<main css={LiveblogArticleStyles}>
<div css={BorderStyles}>
<LiveblogSeries series={series} pillarStyles={pillarStyles}/>
<LiveblogHeadline
headline={fields.headline}
pillarStyles={pillarStyles}
/>
<LiveblogStandfirst
standfirst={fields.standfirst}
pillarStyles={pillarStyles}
/>
{ byline }
{ headerImg }
<LiveblogKeyEvents
bodyElements={bodyElements}
pillarStyles={pillarStyles}
/>
{ body }
<Tags tags={tags} background={palette.neutral[93]}/>
</div>
</main>
);
}

export default LiveblogArticle;
29 changes: 15 additions & 14 deletions src/components/liveblog/LiveblogBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { render } from 'renderBlocks';
import { Block } from 'types/capi-thrift-models';

import { css, SerializedStyles } from '@emotion/core'
import { Reader } from 'types/Reader';
import { Env } from 'types/Env';

const LiveBodyStyles = (pillarStyles: PillarStyles): SerializedStyles => css`
.rich-link,
Expand All @@ -25,33 +27,32 @@ const LiveBodyStyles = (pillarStyles: PillarStyles): SerializedStyles => css`
interface LiveblogBodyProps {
pillarStyles: PillarStyles;
bodyElements: Block[];
imageSalt: string;
}

const LiveblogBody= ({ pillarStyles, bodyElements, imageSalt }: LiveblogBodyProps): JSX.Element => {
function LiveblogBody({ pillarStyles, bodyElements }: LiveblogBodyProps): Reader<Env, JSX.Element> {
const initialBlocks = bodyElements.slice(0, 7);
const LoadMore = ({ total }: { total: number }): JSX.Element | null => total > 10
? <LiveblogLoadMore pillarStyles={pillarStyles}/>
: null;

return (
<article css={LiveBodyStyles(pillarStyles)}>
{
initialBlocks.map((block: Block) => {
return <LiveblogBlock
Reader.asks(env =>
<article css={LiveBodyStyles(pillarStyles)}>
{ initialBlocks.map((block: Block) =>
<LiveblogBlock
key={block.id}
pillarStyles={pillarStyles}
highlighted={!!block.attributes.keyEvent}
title={block.title}
firstPublishedDate={block.firstPublishedDate}
lastModifiedDate={block.lastModifiedDate}>
<>{render(block.elements, imageSalt).html}</>
</LiveblogBlock>
})
}
<LoadMore total={bodyElements.length}/>
</article>
)
lastModifiedDate={block.lastModifiedDate}
>
<>{render(block.elements).run(env).html}</>
</LiveblogBlock>
) }
<LoadMore total={bodyElements.length}/>
</article>
))
}

export default LiveblogBody;
27 changes: 12 additions & 15 deletions src/components/liveblog/LiveblogByline.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { textSans, PillarStyles, PillarId } from '../../styles';
import { textSans, PillarStyles, PillarId, getPillarStyles } from 'styles';

import { Keyline } from '../shared/Keyline';

Expand All @@ -9,6 +9,8 @@ import { Contributor } from '../../types/Capi';
import { formatDate } from 'utils/date';
import Avatar from 'components/shared/Avatar';
import LeftColumn from 'components/shared/LeftColumn';
import { Env } from 'types/Env';
import { Reader } from 'types/Reader';

const LiveblogBylineStyles = ({ liveblogBackground }: PillarStyles): SerializedStyles => css`
background: ${liveblogBackground};
Expand Down Expand Up @@ -46,39 +48,34 @@ const LiveblogBylineStyles = ({ liveblogBackground }: PillarStyles): SerializedS

interface LiveblogBylineProps {
byline: string;
pillarStyles: PillarStyles;
publicationDate: string;
contributors: Contributor[];
pillarId: PillarId;
imageSalt: string;
}

const LiveblogByline = ({
function LiveblogByline({
byline,
pillarStyles,
publicationDate,
contributors,
pillarId,
imageSalt
}: LiveblogBylineProps): JSX.Element => {

return (
}: LiveblogBylineProps): Reader<Env, JSX.Element> {
const pillarStyles = getPillarStyles(pillarId);

return Avatar({ contributors, bgColour: pillarStyles.featureHeadline }).map(avatar =>
// This is not an iterator, ESLint is confused
// eslint-disable-next-line react/jsx-key

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a valid warning but should we disable it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think React may throw a warning for this at runtime, so yeah maybe we don't need the lint rule. Also, I don't think it would matter on the server because isn't it a performance optimisation for re-renders?

<div css={[LiveblogBylineStyles(pillarStyles)]}>
<Keyline pillar={pillarId} type={'liveblog'}/>
<LeftColumn>
<Avatar
contributors={contributors}
bgColour={pillarStyles.featureHeadline}
imageSalt={imageSalt}
/>
{ avatar }
<div className="author">
<address dangerouslySetInnerHTML={{__html: byline}}></address>
<time className="date">{ formatDate(new Date(publicationDate)) }</time>
<div className="follow">Get alerts on this story</div>
</div>
</LeftColumn>
</div>
)
);
}

export default LiveblogByline;
Loading