Skip to content

Commit 2e02d9c

Browse files
authored
Add generic banner infrastructure (#3274)
1 parent cc44647 commit 2e02d9c

File tree

10 files changed

+196
-26
lines changed

10 files changed

+196
-26
lines changed

gatsby-browser.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
const React = require('react');
1010
const ReactDOM = require('react-dom');
11+
const {BannerContextManager} = require('components/Banner');
1112

1213
// Import global styles
1314
require('normalize.css');
@@ -22,3 +23,7 @@ window.ReactDOM = ReactDOM;
2223
// A stub function is needed because gatsby won't load this file otherwise
2324
// (https://github.com/gatsbyjs/gatsby/issues/6759)
2425
exports.onClientEntry = () => {};
26+
27+
exports.wrapRootElement = ({element}) => (
28+
<BannerContextManager>{element}</BannerContextManager>
29+
);

src/components/Banner/Banner.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* @emails react-core
5+
* @flow
6+
*/
7+
8+
// $FlowFixMe Update Flow
9+
import React, {useContext} from 'react';
10+
import BannerContext from './BannerContext';
11+
import {media} from 'theme';
12+
13+
export default function Banner() {
14+
const {banner, dismiss} = useContext(BannerContext);
15+
if (banner === null) {
16+
return null;
17+
}
18+
return (
19+
<div
20+
css={{
21+
height: banner.normalHeight,
22+
fontSize: 20,
23+
padding: 20,
24+
textAlign: 'center',
25+
26+
[media.lessThan('small')]: {
27+
height: banner.smallHeight,
28+
},
29+
}}>
30+
{banner.content(dismiss)}
31+
</div>
32+
);
33+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* @emails react-core
5+
* @flow
6+
*/
7+
8+
import React from 'react';
9+
10+
// $FlowFixMe Update Flow
11+
export default React.createContext({
12+
banner: null,
13+
dismiss() {},
14+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* @emails react-core
5+
* @flow
6+
*/
7+
8+
// $FlowFixMe Update Flow
9+
import React, {useState, useLayoutEffect} from 'react';
10+
import BannerContext from './BannerContext';
11+
12+
let activeBanner = null;
13+
14+
// Example usage:
15+
// activeBanner = {
16+
// storageId: 'dismiss_banner_blm',
17+
// normalHeight: 60,
18+
// smallHeight: 80,
19+
// content: dismiss => (
20+
// <div>
21+
// <a href="test">Test</a> <button onClick={dismiss}>close</button>
22+
// </div>
23+
// ),
24+
// };
25+
26+
if (activeBanner) {
27+
try {
28+
if (localStorage.getItem(activeBanner.storageId)) {
29+
activeBanner = null;
30+
}
31+
} catch (err) {
32+
// Ignore.
33+
}
34+
}
35+
36+
type Props = {
37+
children: mixed,
38+
};
39+
40+
export default function BannerContextManager({children}: Props) {
41+
const [bannerContext, setBannerContext] = useState({
42+
banner: null,
43+
dismiss() {},
44+
});
45+
46+
// Apply after hydration.
47+
useLayoutEffect(() => {
48+
if (activeBanner) {
49+
let banner = activeBanner;
50+
setBannerContext({
51+
banner,
52+
dismiss: () => {
53+
try {
54+
localStorage.setItem(banner.storageId, 'true');
55+
} catch (err) {
56+
// Ignore.
57+
}
58+
// Don't show for next navigations within the session.
59+
activeBanner = null;
60+
// Hide immediately.
61+
setBannerContext({
62+
banner: null,
63+
dismiss() {},
64+
});
65+
},
66+
});
67+
}
68+
}, []);
69+
70+
return (
71+
<BannerContext.Provider value={bannerContext}>
72+
{children}
73+
</BannerContext.Provider>
74+
);
75+
}

src/components/Banner/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* @emails react-core
5+
*/
6+
7+
import Banner from './Banner';
8+
import BannerContext from './BannerContext';
9+
import BannerContextManager from './BannerContextManager';
10+
11+
export default Banner;
12+
export {BannerContext, BannerContextManager};

src/components/LayoutHeader/Header.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* @flow
66
*/
77

8+
import Banner from 'components/Banner';
89
import Container from 'components/Container';
910
import HeaderLink from './HeaderLink';
1011
import {Link} from 'gatsby';
@@ -242,6 +243,9 @@ const Header = ({location}: {location: Location}) => (
242243
</div>
243244
</div>
244245
</Container>
246+
<Container>
247+
<Banner />
248+
</Container>
245249
</header>
246250
);
247251

src/components/MarkdownHeader/MarkdownHeader.js

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,38 @@
66
*/
77

88
import Flex from 'components/Flex';
9-
import React from 'react';
9+
// $FlowFixMe Update Flow
10+
import React, {useContext} from 'react';
11+
import {BannerContext} from 'components/Banner';
1012
import {colors, fonts, media} from 'theme';
1113

12-
const MarkdownHeader = ({title}: {title: string}) => (
13-
<Flex type="header" halign="space-between" valign="baseline">
14-
<h1
15-
css={{
16-
color: colors.dark,
17-
marginBottom: 0,
18-
marginTop: 40,
19-
...fonts.header,
14+
const MarkdownHeader = ({title}: {title: string}) => {
15+
const {banner} = useContext(BannerContext);
16+
return (
17+
<Flex type="header" halign="space-between" valign="baseline">
18+
<h1
19+
css={{
20+
color: colors.dark,
21+
marginBottom: 0,
22+
marginTop: 40 + (banner ? banner.normalHeight : 0),
23+
...fonts.header,
2024

21-
[media.size('medium')]: {
22-
marginTop: 60,
23-
},
25+
[media.lessThan('small')]: {
26+
marginTop: 40 + (banner ? banner.smallHeight : 0),
27+
},
2428

25-
[media.greaterThan('large')]: {
26-
marginTop: 80,
27-
},
28-
}}>
29-
{title}
30-
</h1>
31-
</Flex>
32-
);
29+
[media.size('medium')]: {
30+
marginTop: 60 + (banner ? banner.normalHeight : 0),
31+
},
32+
33+
[media.greaterThan('large')]: {
34+
marginTop: 80 + (banner ? banner.normalHeight : 0),
35+
},
36+
}}>
37+
{title}
38+
</h1>
39+
</Flex>
40+
);
41+
};
3342

3443
export default MarkdownHeader;

src/components/MarkdownPage/MarkdownPage.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import Container from 'components/Container';
99
import Flex from 'components/Flex';
1010
import MarkdownHeader from 'components/MarkdownHeader';
1111
import NavigationFooter from 'templates/components/NavigationFooter';
12-
import React from 'react';
12+
// $FlowFixMe Update Flow
13+
import React, {useContext} from 'react';
14+
import {BannerContext} from 'components/Banner';
1315
import StickyResponsiveSidebar from 'components/StickyResponsiveSidebar';
1416
import TitleAndMetaTags from 'components/TitleAndMetaTags';
1517
import FeedbackForm from 'components/FeedbackForm';
@@ -56,6 +58,7 @@ const MarkdownPage = ({
5658
sectionList,
5759
titlePostfix = '',
5860
}: Props) => {
61+
const {banner} = useContext(BannerContext);
5962
const hasAuthors = authors.length > 0;
6063
const titlePrefix = markdownRemark.frontmatter.title || '';
6164

@@ -73,6 +76,9 @@ const MarkdownPage = ({
7376
flex: '1 0 auto',
7477
position: 'relative',
7578
zIndex: 0,
79+
'& h1, & h2, & h3, & h4, & h5, & h6': {
80+
scrollMarginTop: banner ? banner.normalHeight : 0,
81+
},
7682
}}>
7783
<TitleAndMetaTags
7884
ogDescription={ogDescription}

src/components/StickyResponsiveSidebar/StickyResponsiveSidebar.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import Container from 'components/Container';
99
import React, {Component} from 'react';
10+
import {BannerContext} from 'components/Banner';
1011
import Sidebar from 'templates/components/Sidebar';
1112
import {colors, media} from 'theme';
1213
import ChevronSvg from 'templates/components/ChevronSvg';
@@ -24,6 +25,8 @@ type Props = {
2425
};
2526

2627
class StickyResponsiveSidebar extends Component<Props, State> {
28+
static contextType = BannerContext;
29+
2730
constructor(props: Props) {
2831
super(props);
2932

@@ -42,8 +45,9 @@ class StickyResponsiveSidebar extends Component<Props, State> {
4245

4346
render() {
4447
const {open} = this.state;
48+
const {banner} = this.context;
4549
const smallScreenSidebarStyles = {
46-
top: 0,
50+
top: banner ? banner.smallHeight : 0,
4751
left: 0,
4852
bottom: 0,
4953
right: 0,
@@ -117,18 +121,18 @@ class StickyResponsiveSidebar extends Component<Props, State> {
117121
transition: 'transform 0.5s ease',
118122
}}
119123
css={{
120-
marginTop: 60,
124+
marginTop: 60 + (banner ? banner.normalHeight : 0),
121125

122126
[media.size('xsmall')]: {
123127
marginTop: 40,
124128
},
125129

126130
[media.between('small', 'medium')]: {
127-
marginTop: 0,
131+
marginTop: 20 + (banner ? banner.normalHeight : 0),
128132
},
129133

130134
[media.between('medium', 'large')]: {
131-
marginTop: 50,
135+
marginTop: 50 + (banner ? banner.normalHeight : 0),
132136
},
133137

134138
[media.greaterThan('small')]: {

src/pages/index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* @emails react-core
55
*/
66

7+
import {BannerContext} from 'components/Banner';
78
import ButtonLink from 'components/ButtonLink';
89
import Container from 'components/Container';
910
import Flex from 'components/Flex';
@@ -20,6 +21,8 @@ import {babelURL} from 'site-constants';
2021
import logoWhiteSvg from 'icons/logo-white.svg';
2122

2223
class Home extends Component {
24+
static contextType = BannerContext;
25+
2326
state = {
2427
babelLoaded: false,
2528
};
@@ -40,6 +43,7 @@ class Home extends Component {
4043
render() {
4144
const {babelLoaded} = this.state;
4245
const {data, location} = this.props;
46+
const {banner} = this.context;
4347
const {codeExamples, examples, marketing} = data;
4448

4549
const code = codeExamples.edges.reduce((lookup, {node}) => {
@@ -53,7 +57,11 @@ class Home extends Component {
5357
title="React &ndash; A JavaScript library for building user interfaces"
5458
canonicalUrl={createCanonicalUrl('/')}
5559
/>
56-
<div css={{width: '100%'}}>
60+
<div
61+
css={{
62+
width: '100%',
63+
marginTop: banner ? banner.normalHeight : 0,
64+
}}>
5765
<header
5866
css={{
5967
backgroundColor: colors.dark,

0 commit comments

Comments
 (0)