UNPKG

7.43 kBJavaScriptView Raw
1import React from "react"
2import PropTypes from "prop-types"
3import { graphql, Link, navigate } from "gatsby"
4import queryString from "query-string"
5
6class Dev404Page extends React.Component {
7 static propTypes = {
8 data: PropTypes.object,
9 custom404: PropTypes.element,
10 location: PropTypes.object,
11 }
12
13 constructor(props) {
14 super(props)
15 const { data, location } = this.props
16 const pagePaths = data.allSitePage.nodes.map(node => node.path)
17 const urlState = queryString.parse(location.search)
18
19 const initialPagePathSearchTerms = urlState.filter ? urlState.filter : ``
20
21 this.state = {
22 hasMounted: false,
23 showCustom404: process.env.GATSBY_DISABLE_CUSTOM_404 || false,
24 initPagePaths: pagePaths,
25 pagePathSearchTerms: initialPagePathSearchTerms,
26 pagePaths: this.getFilteredPagePaths(
27 pagePaths,
28 initialPagePathSearchTerms
29 ),
30 }
31 this.showCustom404 = this.showCustom404.bind(this)
32 this.handlePagePathSearch = this.handlePagePathSearch.bind(this)
33 this.handleSearchTermChange = this.handleSearchTermChange.bind(this)
34 }
35
36 componentDidMount() {
37 this.setState({
38 hasMounted: true,
39 })
40 }
41
42 showCustom404() {
43 this.setState({ showCustom404: true })
44 }
45
46 handleSearchTermChange(event) {
47 const searchValue = event.target.value
48
49 this.setSearchUrl(searchValue)
50
51 this.setState({
52 pagePathSearchTerms: searchValue,
53 })
54 }
55
56 handlePagePathSearch(event) {
57 event.preventDefault()
58 const allPagePaths = [...this.state.initPagePaths]
59 this.setState({
60 pagePaths: this.getFilteredPagePaths(
61 allPagePaths,
62 this.state.pagePathSearchTerms
63 ),
64 })
65 }
66
67 getFilteredPagePaths(allPagePaths, pagePathSearchTerms) {
68 const searchTerm = new RegExp(`${pagePathSearchTerms}`)
69 return allPagePaths.filter(pagePath => searchTerm.test(pagePath))
70 }
71
72 setSearchUrl(searchValue) {
73 const {
74 location: { pathname, search },
75 } = this.props
76
77 const searchMap = queryString.parse(search)
78 searchMap.filter = searchValue
79
80 const newSearch = queryString.stringify(searchMap)
81
82 if (search !== `?${newSearch}`) {
83 navigate(`${pathname}?${newSearch}`, { replace: true })
84 }
85 }
86
87 render() {
88 if (!this.state.hasMounted) {
89 return null
90 }
91
92 const { pathname } = this.props.location
93 let newFilePath
94 let newAPIPath
95 if (pathname === `/`) {
96 newFilePath = `src/pages/index.js`
97 } else if (pathname.slice(0, 4) === `/api`) {
98 newAPIPath = `src${pathname}.js`
99 } else if (pathname.slice(-1) === `/`) {
100 newFilePath = `src/pages${pathname.slice(0, -1)}.js`
101 } else {
102 newFilePath = `src/pages${pathname}.js`
103 }
104
105 return this.state.showCustom404 ? (
106 this.props.custom404
107 ) : (
108 <div>
109 <h1>Gatsby.js development 404 page</h1>
110 <p>
111 There's not a page or function yet at{` `}
112 <code>{pathname}</code>
113 </p>
114 {this.props.custom404 ? (
115 <p>
116 <button onClick={this.showCustom404}>
117 Preview custom 404 page
118 </button>
119 </p>
120 ) : (
121 <p>
122 {`A custom 404 page wasn't detected - if you would like to add one, create a component in your site directory at `}
123 <code>src/pages/404.js</code>.
124 </p>
125 )}
126 {newFilePath && (
127 <div>
128 <h2>Create a page at this url</h2>
129 <p>
130 Create a React.js component like the following in your site
131 directory at
132 {` `}"<code>{newFilePath}</code>"{` `}
133 and then refresh to show the new page component you created.
134 </p>
135 <pre
136 style={{
137 border: `1px solid lightgray`,
138 padding: `8px`,
139 maxWidth: `80ch`,
140 background: `#f3f3f3`,
141 }}
142 >
143 <code
144 dangerouslySetInnerHTML={{
145 __html: `import * as React from "react"
146
147export default function Component () {
148 return "Hello world"
149}`,
150 }}
151 />
152 </pre>
153 </div>
154 )}
155 {newAPIPath && (
156 <div>
157 <h2>Create an API function at this url</h2>
158 <p>
159 Create a javascript file like the following in your site directory
160 at
161 {` `}"<code>{newAPIPath}</code>"{` `}
162 and refresh to execute the new API function you created.
163 </p>
164 <pre
165 style={{
166 border: `1px solid lightgray`,
167 padding: `8px`,
168 maxWidth: `80ch`,
169 background: `#f3f3f3`,
170 }}
171 >
172 <code
173 dangerouslySetInnerHTML={{
174 __html: `
175export default function API (req, res) {
176 res.json({ hello: "world" })
177}`,
178 }}
179 />
180 </pre>
181 </div>
182 )}
183 {this.state.initPagePaths.length > 0 && (
184 <div>
185 <hr />
186 <p>
187 If you were trying to reach another page or function, perhaps you
188 can find it below.
189 </p>
190 <h2>Functions ({this.props.data.allSiteFunction.nodes.length})</h2>
191 <ul>
192 {this.props.data.allSiteFunction.nodes.map(node => {
193 const functionRoute = `/api/${node.functionRoute}`
194 return (
195 <li key={functionRoute}>
196 <a href={functionRoute}>{functionRoute}</a>
197 </li>
198 )
199 })}
200 </ul>
201 <h2>
202 Pages (
203 {this.state.pagePaths.length != this.state.initPagePaths.length
204 ? `${this.state.pagePaths.length}/${this.state.initPagePaths.length}`
205 : this.state.initPagePaths.length}
206 )
207 </h2>
208
209 <form onSubmit={this.handlePagePathSearch}>
210 <label>
211 Search:
212 <input
213 type="text"
214 id="search"
215 placeholder="Search pages..."
216 value={this.state.pagePathSearchTerms}
217 onChange={this.handleSearchTermChange}
218 />
219 </label>
220 <input type="submit" value="Submit" />
221 </form>
222 <ul>
223 {this.state.pagePaths.map(
224 (pagePath, index) =>
225 index < 100 && (
226 <li key={pagePath}>
227 <Link to={pagePath}>{pagePath}</Link>
228 </li>
229 )
230 )}
231 {this.state.pagePaths.length > 100 && (
232 <p style={{ fontWeight: `bold` }}>
233 ... and {this.state.pagePaths.length - 100} more.
234 </p>
235 )}
236 </ul>
237 </div>
238 )}
239 </div>
240 )
241 }
242}
243
244export default Dev404Page
245
246// ESLint is complaining about the backslash in regex
247/* eslint-disable */
248export const pagesQuery = graphql`
249 query PagesQuery {
250 allSiteFunction {
251 nodes {
252 functionRoute
253 }
254 }
255 allSitePage(filter: { path: { regex: "/^(?!\/dev-404-page).+$/" } }) {
256 nodes {
257 path
258 }
259 }
260 }
261`
262/* eslint-enable */