Skip to content

Commit a39ace3

Browse files
authored
Merge pull request #15259 from guardian/dina/top-bar-component-hosted-content
Create `HostedContentHeader` component
2 parents 374931a + 988dea3 commit a39ace3

4 files changed

Lines changed: 345 additions & 2 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { palette as sourcePalette } from '@guardian/source/foundations';
2+
import { HostedContentHeader } from './HostedContentHeader';
3+
import { Section } from './Section';
4+
5+
export default {
6+
component: HostedContentHeader,
7+
title: 'Components/HostedContentHeader',
8+
};
9+
10+
export const Default = () => {
11+
return (
12+
<Section
13+
fullWidth={true}
14+
showSideBorders={false}
15+
showTopBorder={false}
16+
shouldCenter={false}
17+
backgroundColour={sourcePalette.neutral[7]}
18+
padSides={false}
19+
element="aside"
20+
>
21+
<HostedContentHeader
22+
accentColor={sourcePalette.brand[400]}
23+
branding="Branding"
24+
/>
25+
</Section>
26+
);
27+
};
28+
Default.storyName = 'default';
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
import { css } from '@emotion/react';
2+
import {
3+
from,
4+
palette as sourcePalette,
5+
space,
6+
textSans15,
7+
textSans17,
8+
textSansBold12,
9+
textSansBold14,
10+
visuallyHidden,
11+
} from '@guardian/source/foundations';
12+
import { SvgGuardianLogo } from '@guardian/source/react-components';
13+
import { nestedOphanComponents } from '../lib/ophan-helpers';
14+
15+
type Props = {
16+
accentColor: string;
17+
branding: string;
18+
};
19+
20+
const HOSTED_CONTENT_HEIGHT_MOBILE = 48;
21+
const HOSTED_CONTENT_HEIGHT_DESKTOP = 58;
22+
23+
const headerWrapperStyles = css`
24+
position: relative;
25+
display: flex;
26+
justify-content: space-between;
27+
width: 100%;
28+
margin: 0 auto;
29+
padding: 0;
30+
height: ${HOSTED_CONTENT_HEIGHT_MOBILE}px;
31+
color: ${sourcePalette.neutral[100]};
32+
33+
${from.tablet} {
34+
height: ${HOSTED_CONTENT_HEIGHT_DESKTOP}px;
35+
}
36+
37+
${from.desktop} {
38+
max-width: 61.25rem;
39+
}
40+
41+
${from.leftCol} {
42+
max-width: 71.25rem;
43+
}
44+
45+
${from.wide} {
46+
max-width: 81.25rem;
47+
}
48+
`;
49+
50+
const hostedByStyles = css`
51+
${textSansBold12};
52+
display: block;
53+
margin-left: -51px;
54+
margin-bottom: -11px;
55+
letter-spacing: 0.03125rem;
56+
color: ${sourcePalette.neutral[73]};
57+
58+
${from.tablet} {
59+
${textSansBold14};
60+
margin-left: -60px;
61+
}
62+
`;
63+
64+
const logoStyles = css`
65+
position: relative;
66+
display: flex;
67+
align-self: end;
68+
margin-bottom: 1px;
69+
70+
svg {
71+
width: 94px;
72+
height: auto;
73+
74+
${from.tablet} {
75+
width: 120px;
76+
}
77+
}
78+
`;
79+
80+
const titleStyles = css`
81+
${textSansBold14};
82+
position: relative;
83+
height: auto;
84+
line-height: 0.945rem;
85+
padding: 0.3125rem 0.375rem 0.25rem;
86+
letter-spacing: 0.03125rem;
87+
88+
${from.desktop} {
89+
text-align: center;
90+
margin: 2px 0;
91+
}
92+
`;
93+
94+
const badgeWrapperStyles = css`
95+
position: absolute;
96+
display: block;
97+
width: 80px;
98+
height: 45px; /* This is temporary, replace with actual badge height */
99+
top: 100%;
100+
background-color: ${sourcePalette.neutral[60]};
101+
text-align: center;
102+
z-index: 10;
103+
104+
${from.desktop} {
105+
width: 132px;
106+
height: 75px;
107+
}
108+
`;
109+
110+
const HeaderWrapper = ({ children }: { children: React.ReactNode }) => (
111+
<div css={headerWrapperStyles}>{children}</div>
112+
);
113+
114+
const Left = ({ children }: { children: React.ReactNode }) => (
115+
<div
116+
css={css`
117+
display: flex;
118+
`}
119+
>
120+
{children}
121+
</div>
122+
);
123+
124+
const Right = ({ children }: { children: React.ReactNode }) => (
125+
<div
126+
css={css`
127+
display: flex;
128+
padding: ${space[1]}px 10px;
129+
130+
@media (min-width: 330px) {
131+
margin-right: 0.625rem;
132+
}
133+
`}
134+
>
135+
{children}
136+
</div>
137+
);
138+
139+
const HeaderSection = ({
140+
children,
141+
isFirst,
142+
}: {
143+
children: React.ReactNode;
144+
isFirst?: boolean;
145+
}) => (
146+
<div
147+
css={css`
148+
height: 100%;
149+
display: flex;
150+
align-items: center;
151+
${isFirst ? 'margin-left: 1.25rem;' : 'margin-left: 0.625rem;'}
152+
153+
${from.desktop} {
154+
${isFirst ? null : 'margin-left: 1.25rem;'}
155+
}
156+
157+
${from.leftCol} {
158+
${isFirst ? null : 'margin-left: 2rem;'}
159+
}
160+
`}
161+
>
162+
{children}
163+
</div>
164+
);
165+
166+
const TitleAndBadge = ({ accentColor, branding }: Props) => (
167+
<>
168+
<div
169+
css={css`
170+
box-sizing: border-box;
171+
width: 80px;
172+
align-self: flex-end;
173+
background-color: ${accentColor};
174+
175+
${from.desktop} {
176+
width: 132px;
177+
}
178+
`}
179+
>
180+
<p css={titleStyles}>Advertiser content</p>
181+
</div>
182+
183+
{/* The following div is a placeholder for the badge */}
184+
<div css={badgeWrapperStyles}>{branding}</div>
185+
</>
186+
);
187+
188+
{
189+
/* TODO: waiting for design confirmation so it's just a placeholder for now */
190+
}
191+
const About = () => (
192+
<div
193+
css={css`
194+
color: ${sourcePalette.neutral[100]};
195+
margin-top: 1.25rem;
196+
197+
${from.tablet} {
198+
margin-top: 1.8rem;
199+
}
200+
`}
201+
>
202+
<p
203+
css={css`
204+
${textSans15};
205+
206+
${from.desktop} {
207+
${textSans17};
208+
}
209+
`}
210+
>
211+
About
212+
</p>
213+
</div>
214+
);
215+
216+
{
217+
/* Can't reuse Logo.tsx until we add a new palette to work with hosted. The color doesn't work with palette --masthead-nav-link-text */
218+
}
219+
const Logo = () => (
220+
<a
221+
href="/"
222+
data-link-name={nestedOphanComponents('header', 'logo')}
223+
css={css`
224+
cursor: pointer;
225+
text-decoration: none;
226+
`}
227+
>
228+
<span
229+
css={css`
230+
${visuallyHidden};
231+
`}
232+
>
233+
The Guardian - Back to home
234+
</span>
235+
<p css={hostedByStyles}>Hosted by</p>
236+
237+
<SvgGuardianLogo textColor={`${sourcePalette.neutral[100]}`} />
238+
</a>
239+
);
240+
241+
const HostedContentLogo = () => (
242+
<div
243+
css={css`
244+
position: relative;
245+
display: flex;
246+
`}
247+
>
248+
<div css={logoStyles}>
249+
<Logo />
250+
</div>
251+
</div>
252+
);
253+
254+
export const HostedContentHeader = ({ accentColor, branding }: Props) => {
255+
return (
256+
<HeaderWrapper>
257+
<Left>
258+
<HeaderSection isFirst={true}>
259+
<TitleAndBadge
260+
accentColor={accentColor}
261+
branding={branding}
262+
/>
263+
</HeaderSection>
264+
<HeaderSection>
265+
<About />
266+
</HeaderSection>
267+
</Left>
268+
<Right>
269+
<HostedContentLogo />
270+
</Right>
271+
</HeaderWrapper>
272+
);
273+
};

dotcom-rendering/src/layouts/HostedArticleLayout.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { css } from '@emotion/react';
2+
import { palette as sourcePalette } from '@guardian/source/foundations';
3+
import { HostedContentHeader } from '../components/HostedContentHeader';
4+
import { Section } from '../components/Section';
25
import { grid } from '../grid';
36
import type { RenderingTarget } from '../types/renderingTarget';
7+
import { Stuck } from './lib/stickiness';
48

59
interface Props {
610
renderingTarget: RenderingTarget;
@@ -21,7 +25,24 @@ const border = css`
2125
export const HostedArticleLayout = (props: WebProps | AppProps) => {
2226
return (
2327
<>
24-
{props.renderingTarget === 'Web' ? 'Masthead' : null}
28+
{props.renderingTarget === 'Web' ? (
29+
<Stuck>
30+
<Section
31+
fullWidth={true}
32+
showSideBorders={false}
33+
showTopBorder={false}
34+
shouldCenter={false}
35+
backgroundColour={sourcePalette.neutral[7]}
36+
padSides={false}
37+
element="aside"
38+
>
39+
<HostedContentHeader
40+
accentColor={sourcePalette.brand[400]}
41+
branding="logo"
42+
/>
43+
</Section>
44+
</Stuck>
45+
) : null}
2546
<main>
2647
<header css={[grid.container, border]}>
2748
<div css={[grid.column.all]}>Main media</div>

dotcom-rendering/src/layouts/HostedGalleryLayout.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { css } from '@emotion/react';
2+
import { palette as sourcePalette } from '@guardian/source/foundations';
3+
import { HostedContentHeader } from '../components/HostedContentHeader';
4+
import { Section } from '../components/Section';
25
import { grid } from '../grid';
36
import type { RenderingTarget } from '../types/renderingTarget';
7+
import { Stuck } from './lib/stickiness';
48

59
interface Props {
610
renderingTarget: RenderingTarget;
@@ -21,7 +25,24 @@ const border = css`
2125
export const HostedGalleryLayout = (props: WebProps | AppProps) => {
2226
return (
2327
<>
24-
{props.renderingTarget === 'Web' ? 'Masthead' : null}
28+
{props.renderingTarget === 'Web' ? (
29+
<Stuck>
30+
<Section
31+
fullWidth={true}
32+
showSideBorders={false}
33+
showTopBorder={false}
34+
shouldCenter={false}
35+
backgroundColour={sourcePalette.neutral[7]}
36+
padSides={false}
37+
element="aside"
38+
>
39+
<HostedContentHeader
40+
accentColor={sourcePalette.brand[400]}
41+
branding="logo"
42+
/>
43+
</Section>
44+
</Stuck>
45+
) : null}
2546
<main>
2647
<header css={[grid.container, border]}>
2748
<div

0 commit comments

Comments
 (0)