fix(client): scroll top and focus an article on guide navigation

This commit is contained in:
Valeriy S
2019-01-23 09:39:35 +03:00
committed by Stuart Taylor
parent a44935a520
commit 1f4fa29860
6 changed files with 149 additions and 168 deletions

View File

@ -29,70 +29,86 @@ const propTypes = {
location: PropTypes.object
};
const Layout = ({ children }) => (
<StaticQuery
query={graphql`
query LayoutQuery {
allNavigationNode {
edges {
node {
dashedName
hasChildren
isStubbed
parentPath
path
title
class Layout extends React.Component {
getContentRef = ref => (this.contentRef = ref);
handleNavigation = () => {
this.contentRef.scrollTop = 0;
this.contentRef.focus();
};
render() {
return (
<StaticQuery
query={graphql`
query LayoutQuery {
allNavigationNode {
edges {
node {
dashedName
hasChildren
isStubbed
parentPath
path
title
}
}
}
}
}
}
`}
render={data => {
const { edges } = data.allNavigationNode;
const pages = edges.map(edge => edge.node);
return (
<NavigationContext>
{({
toggleDisplaySideNav,
displaySideNav,
expandedState,
toggleExpandedState
}) => (
<Fragment>
<Spacer size={2} />
<Grid>
<Row>
<Col
md={4}
smHidden={!displaySideNav}
xsHidden={!displaySideNav}
>
<SideNav
expandedState={expandedState}
pages={pages}
toggleDisplaySideNav={toggleDisplaySideNav}
toggleExpandedState={toggleExpandedState}
/>
</Col>
<Col
className='content'
md={8}
smHidden={displaySideNav}
xsHidden={displaySideNav}
>
<main className='main' id='main' tabIndex='-1'>
{children}
</main>
</Col>
</Row>
</Grid>
</Fragment>
)}
</NavigationContext>
);
}}
/>
);
`}
render={data => {
const { edges } = data.allNavigationNode;
const pages = edges.map(edge => edge.node);
return (
<NavigationContext>
{({
toggleDisplaySideNav,
displaySideNav,
expandedState,
toggleExpandedState
}) => (
<Fragment>
<Spacer size={2} />
<Grid>
<Row>
<Col
md={4}
smHidden={!displaySideNav}
xsHidden={!displaySideNav}
>
<SideNav
expandedState={expandedState}
onNavigate={this.handleNavigation}
pages={pages}
toggleDisplaySideNav={toggleDisplaySideNav}
toggleExpandedState={toggleExpandedState}
/>
</Col>
<Col
md={8}
smHidden={displaySideNav}
xsHidden={displaySideNav}
>
<main
className='content'
id='main'
ref={this.getContentRef}
tabIndex='-1'
>
{this.props.children}
</main>
</Col>
</Row>
</Grid>
</Fragment>
)}
</NavigationContext>
);
}}
/>
);
}
}
Layout.displayName = 'Layout';
Layout.propTypes = propTypes;

View File

@ -4,23 +4,30 @@ import Link from 'gatsby-link';
const propTypes = {
isStubbed: PropTypes.bool,
onNavigate: PropTypes.func.isRequired,
path: PropTypes.string,
router: PropTypes.object,
title: PropTypes.string,
toggleDisplaySideNav: PropTypes.func.isRequired
};
function NavItem(props) {
const { isStubbed, path, title } = props;
return (
<li>
<Link data-navitem='true' onClick={props.toggleDisplaySideNav} to={path}>
<span className={'navItemTitle' + (isStubbed ? ' stubbed' : '')}>
{title}
</span>
</Link>
</li>
);
class NavItem extends React.Component {
handleClick = () => {
this.props.toggleDisplaySideNav();
this.props.onNavigate();
};
render() {
const { isStubbed, path, title } = this.props;
return (
<li>
<Link data-navitem='true' onClick={this.handleClick} to={path}>
<span className={'navItemTitle' + (isStubbed ? ' stubbed' : '')}>
{title}
</span>
</Link>
</li>
);
}
}
NavItem.displayName = 'NavItem';

View File

@ -9,6 +9,7 @@ const propTypes = {
handleClick: PropTypes.func.isRequired,
hasChildren: PropTypes.bool,
isExpanded: PropTypes.bool,
onNavigate: PropTypes.func.isRequired,
path: PropTypes.string,
title: PropTypes.string,
toggleDisplaySideNav: PropTypes.func.isRequired
@ -52,9 +53,10 @@ class NavPanel extends Component {
}
handleTitleClick() {
const { path, toggleDisplaySideNav } = this.props;
const { path, toggleDisplaySideNav, onNavigate } = this.props;
toggleDisplaySideNav();
navigate(path);
onNavigate();
}
renderHeader() {
@ -94,8 +96,6 @@ class NavPanel extends Component {
return (
<Panel
bsClass='panelStyle panel'
collapsible={true}
expanded={isExpanded}
id={`${dashedName}-panel`}
role='listitem'
>

View File

@ -7,6 +7,7 @@ import NavItem from './NavItem';
const propTypes = {
expandedState: PropTypes.object,
onNavigate: PropTypes.func.isRequired,
pages: PropTypes.arrayOf(PropTypes.object),
parents: PropTypes.arrayOf(PropTypes.object),
toggleDisplaySideNav: PropTypes.func.isRequired,
@ -42,7 +43,9 @@ class SideNav extends Component {
const [category] = pages.filter(page => page.path === path);
const { title, hasChildren, dashedName } = category;
const children = this.renderChildren(childrenForParent, pages);
const children = isExpanded
? this.renderChildren(childrenForParent, pages)
: null;
return (
<NavPanel
dashedName={dashedName}
@ -50,11 +53,12 @@ class SideNav extends Component {
hasChildren={hasChildren}
isExpanded={isExpanded}
key={parent.path}
onNavigate={this.props.onNavigate}
path={parent.path}
title={title}
toggleDisplaySideNav={this.props.toggleDisplaySideNav}
>
{isExpanded ? children : null}
{children}
</NavPanel>
);
}
@ -68,6 +72,7 @@ class SideNav extends Component {
<NavItem
isStubbed={child.isStubbed}
key={child.path}
onNavigate={this.props.onNavigate}
path={child.path}
title={child.title}
toggleDisplaySideNav={this.props.toggleDisplaySideNav}

View File

@ -84,36 +84,6 @@
height: 20px;
}
/* 404 */
.flexWrapper {
align-items: center;
display: flex;
flex-direction: column;
height: 50vh;
justify-content: center;
}
.verticalAlign {
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
vertical-align: middle;
}
.button {
color: #006400;
background: #fff;
border-color: #006400;
}
.button:hover {
color: #fff;
background: #006400;
border-color: #fff;
}
/* SideNav */
.caretStyle {
@ -197,6 +167,6 @@
position: absolute;
}
.article {
.content {
outline: 0;
}

View File

@ -1,4 +1,4 @@
import React, { Component, Fragment } from 'react';
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { graphql } from 'gatsby';
import Helmet from 'react-helmet';
@ -13,65 +13,48 @@ const propTypes = {
})
};
class GuideArticle extends Component {
constructor(props) {
super(props);
this.article = null;
}
componentDidMount() {
if (this.article && document.activeElement.hasAttribute('data-navitem')) {
this.article.focus();
}
}
render() {
const {
location: { pathname },
data: {
markdownRemark: {
html,
fields: { slug },
frontmatter: { title }
}
},
pageContext: { meta }
} = this.props;
return (
<Fragment>
<Helmet>
<title>{`${title} | freeCodeCamp Guide`}</title>
<link href={`https://www.freecodecamp.org${slug}`} rel='canonical' />
<meta
content={`https://www.freecodecamp.org${slug}`}
property='og:url'
/>
<meta content={title} property='og:title' />
<meta
content={meta.description ? meta.description : ''}
property='og:description'
/>
<meta
content={meta.description ? meta.description : ''}
name='description'
/>
<meta content={meta.featureImage} property='og:image' />
</Helmet>
<Breadcrumbs path={pathname} />
<article
className='article'
dangerouslySetInnerHTML={{ __html: html }}
id='article'
ref={article => {
this.article = article;
}}
tabIndex='-1'
const GuideArticle = props => {
const {
location: { pathname },
data: {
markdownRemark: {
html,
fields: { slug },
frontmatter: { title }
}
},
pageContext: { meta }
} = props;
return (
<Fragment>
<Helmet>
<title>{`${title} | freeCodeCamp Guide`}</title>
<link href={`https://www.freecodecamp.org${slug}`} rel='canonical' />
<meta
content={`https://www.freecodecamp.org${slug}`}
property='og:url'
/>
</Fragment>
);
}
}
<meta content={title} property='og:title' />
<meta
content={meta.description ? meta.description : ''}
property='og:description'
/>
<meta
content={meta.description ? meta.description : ''}
name='description'
/>
<meta content={meta.featureImage} property='og:image' />
</Helmet>
<Breadcrumbs path={pathname} />
<article
className='article'
dangerouslySetInnerHTML={{ __html: html }}
id='article'
tabIndex='-1'
/>
</Fragment>
);
};
GuideArticle.displayName = 'GuideArticle';
GuideArticle.propTypes = propTypes;