commit 6553936e693e24a8ac6178f4962af15e0ea87dfd parent b1309b9b7e3dd45fa56c906c4b7b29e6f981e3cb Author: Konstantin Tarkus <hello@tarkus.me> Date: Wed, 2 Mar 2016 23:20:00 +0300 Move page/screen components to src/routes along with the routing info Diffstat:
27 files changed, 382 insertions(+), 207 deletions(-)
diff --git a/README.md b/README.md @@ -42,6 +42,7 @@ visit our sponsors: │ ├── /data/ # GraphQL server schema │ ├── /decorators/ # Higher-order React components │ ├── /public/ # Static files which are copied into the /build/public folder +│ ├── /routes/ # Page/screen components along with the routing information │ ├── /stores/ # Stores contain the application state and logic │ ├── /views/ # Express.js views for index and error pages │ ├── /client.js # Client-side startup script diff --git a/package.json b/package.json @@ -64,11 +64,11 @@ "glob": "^7.0.0", "jade-loader": "^0.8.0", "jest-cli": "^0.8.2", - "jscs": "^2.10.1", + "jscs": "^2.11.0", "json-loader": "^0.5.4", "mkdirp": "^0.5.1", "ncp": "^2.0.0", - "postcss": "^5.0.18", + "postcss": "^5.0.19", "postcss-import": "^8.0.2", "postcss-loader": "^0.8.1", "postcss-scss": "^0.1.6", @@ -80,8 +80,8 @@ "replace": "^0.3.0", "url-loader": "^0.5.7", "webpack": "^1.12.14", - "webpack-hot-middleware": "^2.8.1", - "webpack-middleware": "^1.4.0" + "webpack-hot-middleware": "^2.9.0", + "webpack-middleware": "^1.5.1" }, "jest": { "rootDir": "./src", diff --git a/src/components/ContactPage/ContactPage.js b/src/components/ContactPage/ContactPage.js @@ -1,39 +0,0 @@ -/** - * React Starter Kit (https://www.reactstarterkit.com/) - * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -import React, { Component, PropTypes } from 'react'; -import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './ContactPage.scss'; - -const title = 'Contact Us'; - -class ContactPage extends Component { - - static contextTypes = { - onSetTitle: PropTypes.func.isRequired, - }; - - componentWillMount() { - this.context.onSetTitle(title); - } - - render() { - return ( - <div className={s.root}> - <div className={s.container}> - <h1>{title}</h1> - <p>...</p> - </div> - </div> - ); - } - -} - -export default withStyles(ContactPage, s); diff --git a/src/components/ContactPage/ContactPage.scss b/src/components/ContactPage/ContactPage.scss @@ -1,20 +0,0 @@ -/** - * React Starter Kit (https://www.reactstarterkit.com/) - * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -@import '../variables.scss'; - -.root { - -} - -.container { - margin: 0 auto; - padding: 0 0 40px; - max-width: $max-content-width; -} diff --git a/src/components/ContactPage/package.json b/src/components/ContactPage/package.json @@ -1,6 +0,0 @@ -{ - "name": "ContactPage", - "version": "0.0.0", - "private": true, - "main": "./ContactPage.js" -} diff --git a/src/components/LoginPage/LoginPage.js b/src/components/LoginPage/LoginPage.js @@ -1,39 +0,0 @@ -/** - * React Starter Kit (https://www.reactstarterkit.com/) - * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -import React, { Component, PropTypes } from 'react'; -import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './LoginPage.scss'; - -const title = 'Log In'; - -class LoginPage extends Component { - - static contextTypes = { - onSetTitle: PropTypes.func.isRequired, - }; - - componentWillMount() { - this.context.onSetTitle(title); - } - - render() { - return ( - <div className={s.root}> - <div className={s.container}> - <h1>{title}</h1> - <p>...</p> - </div> - </div> - ); - } - -} - -export default withStyles(LoginPage, s); diff --git a/src/components/LoginPage/LoginPage.scss b/src/components/LoginPage/LoginPage.scss @@ -1,20 +0,0 @@ -/** - * React Starter Kit (https://www.reactstarterkit.com/) - * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -@import '../variables.scss'; - -.root { - -} - -.container { - margin: 0 auto; - padding: 0 0 40px; - max-width: $max-content-width; -} diff --git a/src/components/LoginPage/package.json b/src/components/LoginPage/package.json @@ -1,6 +0,0 @@ -{ - "name": "LoginPage", - "version": "0.0.0", - "private": true, - "main": "./LoginPage.js" -} diff --git a/src/components/RegisterPage/RegisterPage.js b/src/components/RegisterPage/RegisterPage.js @@ -1,39 +0,0 @@ -/** - * React Starter Kit (https://www.reactstarterkit.com/) - * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -import React, { Component, PropTypes } from 'react'; -import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './RegisterPage.scss'; - -const title = 'New User Registration'; - -class RegisterPage extends Component { - - static contextTypes = { - onSetTitle: PropTypes.func.isRequired, - }; - - componentWillMount() { - this.context.onSetTitle(title); - } - - render() { - return ( - <div className={s.root}> - <div className={s.container}> - <h1>{title}</h1> - <p>...</p> - </div> - </div> - ); - } - -} - -export default withStyles(RegisterPage, s); diff --git a/src/components/RegisterPage/RegisterPage.scss b/src/components/RegisterPage/RegisterPage.scss @@ -1,20 +0,0 @@ -/** - * React Starter Kit (https://www.reactstarterkit.com/) - * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -@import '../variables.scss'; - -.root { - -} - -.container { - margin: 0 auto; - padding: 0 0 40px; - max-width: $max-content-width; -} diff --git a/src/components/RegisterPage/package.json b/src/components/RegisterPage/package.json @@ -1,6 +0,0 @@ -{ - "name": "RegisterPage", - "version": "0.0.0", - "private": true, - "main": "./RegisterPage.js" -} diff --git a/src/data/queries/news.js b/src/data/queries/news.js @@ -0,0 +1,39 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import { GraphQLList as List } from 'graphql'; +import fetch from '../../core/fetch'; +import NewsItemType from '../types/NewsItemType'; + +// React.js News Feed (RSS) +const url = 'http://ajax.googleapis.com/ajax/services/feed/load' + + '?v=1.0&num=10&q=https://reactjsnews.com/feed.xml'; + +let items = []; +let lastFetchTime = new Date(1970, 0, 1); + +const news = { + type: new List(NewsItemType), + resolve() { + if ((new Date() - lastFetchTime) > 1000 * 60 * 10 /* 10 mins */) { + lastFetchTime = new Date(); + fetch(url) + .then(response => response.json()) + .then(data => { + if (data.responseStatus === 200) { + items = data.responseData.feed.entries; + } + }); + } + + return items; + }, +}; + +export default news; diff --git a/src/data/schema.js b/src/data/schema.js @@ -14,6 +14,7 @@ import { import me from './queries/me'; import content from './queries/content'; +import news from './queries/news'; const schema = new Schema({ query: new ObjectType({ @@ -21,6 +22,7 @@ const schema = new Schema({ fields: { me, content, + news, }, }), }); diff --git a/src/data/types/NewsItemType.js b/src/data/types/NewsItemType.js @@ -0,0 +1,27 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import { + GraphQLObjectType as ObjectType, + GraphQLString as StringType, + GraphQLNonNull as NonNull, +} from 'graphql'; + +const NewsItemType = new ObjectType({ + name: 'NewsItem', + fields: { + title: { type: new NonNull(StringType) }, + link: { type: new NonNull(StringType) }, + author: { type: StringType }, + publishedDate: { type: new NonNull(StringType) }, + contentSnippet: { type: StringType }, + }, +}); + +export default NewsItemType; diff --git a/src/routes.js b/src/routes.js @@ -12,23 +12,25 @@ import Router from 'react-routing/src/Router'; import fetch from './core/fetch'; import App from './components/App'; import ContentPage from './components/ContentPage'; -import ContactPage from './components/ContactPage'; -import LoginPage from './components/LoginPage'; -import RegisterPage from './components/RegisterPage'; import NotFoundPage from './components/NotFoundPage'; import ErrorPage from './components/ErrorPage'; +const routes = [ + require('./routes/home'), + require('./routes/contact'), + require('./routes/login'), + require('./routes/register'), +]; + const router = new Router(on => { on('*', async (state, next) => { const component = await next(); return component && <App context={state.context}>{component}</App>; }); - on('/contact', async () => <ContactPage />); - - on('/login', async () => <LoginPage />); - - on('/register', async () => <RegisterPage />); + routes.forEach(route => { + on(route.path, route.action); + }); on('*', async (state) => { const query = `/graphql?query={content(path:"${state.path}"){path,title,content,component}}`; diff --git a/src/routes/Contact/index.js b/src/routes/Contact/index.js @@ -0,0 +1,18 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React from 'react'; +import Contact from './Contact'; + +export const path = '/contact'; +export const action = async (state) => { + const title = 'Contact Us'; + state.context.onSetTitle(title); + return <Contact title={title} />; +}; diff --git a/src/routes/Home/Home.js b/src/routes/Home/Home.js @@ -0,0 +1,43 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React, { PropTypes } from 'react'; +import withStyles from 'isomorphic-style-loader/lib/withStyles'; +import s from './Home.scss'; + +function Home({ news }) { + return ( + <div className={s.root}> + <div className={s.container}> + <h1 className={s.title}>React.js News</h1> + <ul className={s.news}> + {news.map((item, index) => ( + <li key={index} className={s.newsItem}> + <a href={item.link} className={s.newsTitle}>{item.title}</a> + <span + className={s.newsDesc} + dangerouslySetInnerHTML={{ __html: item.contentSnippet }} + /> + </li> + ))} + </ul> + </div> + </div> + ); +} + +Home.propTypes = { + news: PropTypes.arrayOf(PropTypes.shape({ + title: PropTypes.string.isRequired, + link: PropTypes.string.isRequired, + contentSnippet: PropTypes.string, + })).isRequired, +}; + +export default withStyles(Home, s); diff --git a/src/routes/Home/Home.scss b/src/routes/Home/Home.scss @@ -0,0 +1,38 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +@import '../../components/variables.scss'; + +.root { + padding-left: 20px; + padding-right: 20px; +} + +.container { + margin: 0 auto; + padding: 0 0 40px; + max-width: $max-content-width; +} + +.news { + padding: 0; +} + +.newsItem { + list-style-type: none; + padding-bottom: 6px; +} + +.newsTitle { + font-size: 1.125em; +} + +.newsTitle, .newsDesc { + display: block; +} diff --git a/src/routes/Home/index.js b/src/routes/Home/index.js @@ -0,0 +1,20 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React from 'react'; +import Home from './Home'; +import fetch from '../../core/fetch'; + +export const path = '/'; +export const action = async (state) => { + const response = await fetch('/graphql?query={news{title,link,contentSnippet}}'); + const { data } = await response.json(); + state.context.onSetTitle('React.js Starter Kit'); + return <Home news={data.news} />; +}; diff --git a/src/routes/contact/Contact.js b/src/routes/contact/Contact.js @@ -0,0 +1,27 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React, { PropTypes } from 'react'; +import withStyles from 'isomorphic-style-loader/lib/withStyles'; +import s from './Contact.scss'; + +function Contact({ title }) { + return ( + <div className={s.root}> + <div className={s.container}> + <h1>{title}</h1> + <p>...</p> + </div> + </div> + ); +} + +Contact.propTypes = { title: PropTypes.string.isRequired }; + +export default withStyles(Contact, s); diff --git a/src/routes/contact/Contact.scss b/src/routes/contact/Contact.scss @@ -0,0 +1,21 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +@import '../../components/variables.scss'; + +.root { + padding-left: 20px; + padding-right: 20px; +} + +.container { + margin: 0 auto; + padding: 0 0 40px; + max-width: $max-content-width; +} diff --git a/src/routes/login/Login.js b/src/routes/login/Login.js @@ -0,0 +1,27 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React, { PropTypes } from 'react'; +import withStyles from 'isomorphic-style-loader/lib/withStyles'; +import s from './Login.scss'; + +function Login({ title }) { + return ( + <div className={s.root}> + <div className={s.container}> + <h1>{title}</h1> + <p>...</p> + </div> + </div> + ); +} + +Login.propTypes = { title: PropTypes.string.isRequired }; + +export default withStyles(Login, s); diff --git a/src/routes/login/Login.scss b/src/routes/login/Login.scss @@ -0,0 +1,21 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +@import '../../components/variables.scss'; + +.root { + padding-left: 20px; + padding-right: 20px; +} + +.container { + margin: 0 auto; + padding: 0 0 40px; + max-width: $max-content-width; +} diff --git a/src/routes/login/index.js b/src/routes/login/index.js @@ -0,0 +1,18 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React from 'react'; +import Login from './Login'; + +export const path = '/login'; +export const action = async (state) => { + const title = 'Log In'; + state.context.onSetTitle(title); + return <Login title={title} />; +}; diff --git a/src/routes/register/Register.js b/src/routes/register/Register.js @@ -0,0 +1,27 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React, { PropTypes } from 'react'; +import withStyles from 'isomorphic-style-loader/lib/withStyles'; +import s from './Register.scss'; + +function Register({ title }) { + return ( + <div className={s.root}> + <div className={s.container}> + <h1>{title}</h1> + <p>...</p> + </div> + </div> + ); +} + +Register.propTypes = { title: PropTypes.string.isRequired }; + +export default withStyles(Register, s); diff --git a/src/routes/register/Register.scss b/src/routes/register/Register.scss @@ -0,0 +1,21 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +@import '../../components/variables.scss'; + +.root { + padding-left: 20px; + padding-right: 20px; +} + +.container { + margin: 0 auto; + padding: 0 0 40px; + max-width: $max-content-width; +} diff --git a/src/routes/register/index.js b/src/routes/register/index.js @@ -0,0 +1,18 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React from 'react'; +import Register from './Register'; + +export const path = '/register'; +export const action = async (state) => { + const title = 'New User Registration'; + state.context.onSetTitle(title); + return <Register title={title} />; +};