Skip to content

Commit fcd7155

Browse files
committed
Query content based on path; redirect from / to /Preface
1 parent 082be4c commit fcd7155

File tree

3 files changed

+160
-43
lines changed

3 files changed

+160
-43
lines changed

src/components/App.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ import logo from '../logo.svg'
33
import StarCount from './StarCount'
44
import TableOfContents from './TableOfContents'
55
import Section from './Section'
6+
import { Switch, Route, Redirect } from 'react-router'
7+
8+
const Book = () => (
9+
<div>
10+
<TableOfContents />
11+
<Section />
12+
</div>
13+
)
614

715
class App extends Component {
816
render() {
@@ -13,8 +21,10 @@ class App extends Component {
1321
<img src={logo} className="App-logo" alt="logo" />
1422
<h1 className="App-title">The GraphQL Guide</h1>
1523
</header>
16-
<TableOfContents />
17-
<Section />
24+
<Switch>
25+
<Route exact path="/" render={() => <Redirect to="/Preface" />} />
26+
<Route component={Book} />
27+
</Switch>
1828
</div>
1929
)
2030
}

src/components/Section.js

Lines changed: 130 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,149 @@
11
import React from 'react'
22
import Skeleton from 'react-loading-skeleton'
33
import PropTypes from 'prop-types'
4-
import { graphql } from 'react-apollo'
4+
import { Query } from 'react-apollo'
55
import gql from 'graphql-tag'
66
import { withRouter } from 'react-router'
7+
import get from 'lodash/get'
78

8-
const Section = ({
9-
loading,
10-
sectionContent,
11-
location: { state: { chapter, section } }
12-
}) => (
13-
<section className="Section">
14-
<div className="Section-header-wrapper">
15-
<header className="Section-header">
16-
{chapter.number !== null ? (
17-
<div>
18-
<h1>{section.title}</h1>
19-
<h2>
20-
{'Chapter ' + chapter.number}
21-
<span className="Section-number-divider" />
22-
{'Section ' + section.number}
23-
</h2>
24-
</div>
25-
) : (
26-
<h1>{chapter.title}</h1>
27-
)}
28-
</header>
29-
</div>
30-
<div className="Section-content">
31-
{loading ? <Skeleton count={7} /> : sectionContent}
32-
</div>
33-
</section>
34-
)
9+
import { deslugify } from '../lib/helpers'
10+
11+
const Section = ({ loading, section, chapter }) => {
12+
let headerContent = null,
13+
sectionContent = null
14+
15+
if (loading) {
16+
headerContent = (
17+
<h1>
18+
<Skeleton />
19+
</h1>
20+
)
21+
sectionContent = <Skeleton count={7} />
22+
} else if (!section) {
23+
headerContent = (
24+
<h1>
25+
<span role="img" aria-label="magnifying glass">
26+
🔍
27+
</span>{' '}
28+
404 page not found
29+
</h1>
30+
)
31+
} else {
32+
if (chapter.number !== null) {
33+
headerContent = (
34+
<div>
35+
<h1>{section.title}</h1>
36+
<h2>
37+
{'Chapter ' + chapter.number}
38+
<span className="Section-number-divider" />
39+
{'Section ' + section.number}
40+
</h2>
41+
</div>
42+
)
43+
} else {
44+
headerContent = <h1>{chapter.title}</h1>
45+
}
46+
47+
sectionContent = section.content
48+
}
49+
50+
return (
51+
<section className="Section">
52+
<div className="Section-header-wrapper">
53+
<header className="Section-header">{headerContent}</header>
54+
</div>
55+
<div className="Section-content">{sectionContent}</div>
56+
</section>
57+
)
58+
}
3559

3660
Section.propTypes = {
37-
sectionContent: PropTypes.string,
38-
location: PropTypes.object.isRequired,
61+
section: PropTypes.shape({
62+
title: PropTypes.string,
63+
number: PropTypes.number,
64+
content: PropTypes.string
65+
}),
66+
chapter: PropTypes.shape({
67+
title: PropTypes.string,
68+
number: PropTypes.number
69+
}),
3970
loading: PropTypes.bool.isRequired
4071
}
4172

42-
const SECTION_QUERY = gql`
73+
const SECTION_BY_ID_QUERY = gql`
4374
query SectionContent($id: String!) {
4475
section(id: $id) {
4576
content
4677
}
4778
}
4879
`
4980

50-
const withData = graphql(SECTION_QUERY, {
51-
options: ({ location: { state: { section: { id } } } }) => ({
52-
variables: { id }
53-
}),
54-
props: ({ data: { section, loading } }) => ({
55-
sectionContent: section && section.content,
56-
loading
57-
})
58-
})
81+
const SECTION_BY_CHAPTER_TITLE_QUERY = gql`
82+
query SectionByChapterTitle($title: String!) {
83+
chapterByTitle(title: $title) {
84+
title
85+
section(number: 1) {
86+
content
87+
}
88+
}
89+
}
90+
`
91+
92+
const SECTION_BY_NUMBER_QUERY = gql`
93+
query SectionByNumber($chapterNumber: Int!, $sectionNumber: Int!) {
94+
chapterByNumber(number: $chapterNumber) {
95+
number
96+
section(number: $sectionNumber) {
97+
number
98+
title
99+
content
100+
}
101+
}
102+
}
103+
`
104+
105+
const SectionWithData = ({ location: { state, pathname } }) => {
106+
const page = deslugify(pathname)
107+
108+
let query, variables, createProps
109+
110+
if (state) {
111+
query = SECTION_BY_ID_QUERY
112+
variables = { id: state.section.id }
113+
createProps = ({ data, loading }) => ({
114+
section: {
115+
...state.section,
116+
content: get(data, 'section.content')
117+
},
118+
chapter: state.chapter,
119+
loading
120+
})
121+
} else if (page.chapterTitle) {
122+
query = SECTION_BY_CHAPTER_TITLE_QUERY
123+
variables = { title: page.chapterTitle }
124+
createProps = ({ data, loading }) => ({
125+
section: get(data, 'chapterByTitle.section'),
126+
chapter: {
127+
...data.chapterByTitle,
128+
number: null
129+
},
130+
loading
131+
})
132+
} else if (page.chapterNumber) {
133+
query = SECTION_BY_NUMBER_QUERY
134+
variables = page
135+
createProps = ({ data, loading }) => ({
136+
section: get(data, 'chapterByNumber.section'),
137+
chapter: data.chapterByNumber,
138+
loading
139+
})
140+
}
141+
142+
return (
143+
<Query query={query} variables={variables}>
144+
{queryInfo => <Section {...createProps(queryInfo)} />}
145+
</Query>
146+
)
147+
}
59148

60-
export default withRouter(withData(Section))
149+
export default withRouter(SectionWithData)

src/lib/helpers.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,21 @@ export const slugify = (chapter, section) => {
1818
const sectionSlug = section.number + '-' + withHyphens(section.title)
1919
return `/${chapterSlug}/${sectionSlug}`
2020
}
21+
22+
// parse a path:
23+
// /Introduction
24+
// -> { chapterTitle: 'Introduction' }
25+
//
26+
// /1-Understanding-GraphQL-through-REST/1-Introduction
27+
// -> { chapterNumber: 1, sectionNumber: 1 }
28+
export const deslugify = path => {
29+
const [, chapterSlug, sectionSlug] = path.split('/')
30+
const chapterIsNumbered = !!sectionSlug
31+
32+
return chapterIsNumbered
33+
? {
34+
chapterNumber: parseInt(chapterSlug.split('-')[0], 10),
35+
sectionNumber: parseInt(sectionSlug.split('-')[0], 10)
36+
}
37+
: { chapterTitle: chapterSlug }
38+
}

0 commit comments

Comments
 (0)