From 56f3f68f5d1170a084e081a9525842aca63784b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Dubois?= Date: Wed, 2 Nov 2016 16:08:56 +0200 Subject: [PATCH] feat(example): Simplify navigation, extract to header (#1079) --- app/components/Header/HeaderLink.js | 25 +++++++++++++ app/components/Header/NavBar.js | 5 +++ app/components/Header/index.js | 20 ++++++++-- app/components/Header/messages.js | 17 +++++++++ app/components/Header/tests/index.test.js | 8 ++-- app/containers/FeaturePage/index.js | 18 +-------- app/containers/FeaturePage/messages.js | 4 -- .../FeaturePage/tests/index.test.js | 17 +-------- app/containers/HomePage/index.js | 24 ------------ app/containers/HomePage/messages.js | 4 -- app/containers/HomePage/tests/index.test.js | 37 ------------------- app/containers/NotFoundPage/index.js | 19 +--------- app/containers/NotFoundPage/messages.js | 4 -- .../NotFoundPage/tests/index.test.js | 24 +----------- app/translations/de.json | 7 ++-- app/translations/en.json | 7 ++-- 16 files changed, 78 insertions(+), 162 deletions(-) create mode 100644 app/components/Header/HeaderLink.js create mode 100644 app/components/Header/NavBar.js create mode 100644 app/components/Header/messages.js diff --git a/app/components/Header/HeaderLink.js b/app/components/Header/HeaderLink.js new file mode 100644 index 0000000000..76b261ead6 --- /dev/null +++ b/app/components/Header/HeaderLink.js @@ -0,0 +1,25 @@ +import { Link } from 'react-router'; +import styled from 'styled-components'; + +export default styled(Link)` + display: inline-flex; + padding: 0.25em 2em; + margin: 1em; + text-decoration: none; + border-radius: 4px; + -webkit-font-smoothing: antialiased; + -webkit-touch-callout: none; + user-select: none; + cursor: pointer; + outline: 0; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-weight: bold; + font-size: 16px; + border: 2px solid #41ADDD; + color: #41ADDD; + + &:active { + background: #41ADDD; + color: #FFF; + } +`; diff --git a/app/components/Header/NavBar.js b/app/components/Header/NavBar.js new file mode 100644 index 0000000000..ee00700bb8 --- /dev/null +++ b/app/components/Header/NavBar.js @@ -0,0 +1,5 @@ +import styled from 'styled-components'; + +export default styled.div` + text-align: center; +`; diff --git a/app/components/Header/index.js b/app/components/Header/index.js index c6bf1de68e..3d65f7c062 100644 --- a/app/components/Header/index.js +++ b/app/components/Header/index.js @@ -1,15 +1,29 @@ import React from 'react'; +import { FormattedMessage } from 'react-intl'; import A from './A'; import Img from './Img'; +import NavBar from './NavBar'; +import HeaderLink from './HeaderLink'; import Banner from './banner.jpg'; +import messages from './messages'; class Header extends React.Component { // eslint-disable-line react/prefer-stateless-function render() { return ( - - react-boilerplate - Logo - +
+ + react-boilerplate - Logo + + + + + + + + + +
); } } diff --git a/app/components/Header/messages.js b/app/components/Header/messages.js new file mode 100644 index 0000000000..10e94b4b15 --- /dev/null +++ b/app/components/Header/messages.js @@ -0,0 +1,17 @@ +/* + * HomePage Messages + * + * This contains all the text for the HomePage component. + */ +import { defineMessages } from 'react-intl'; + +export default defineMessages({ + home: { + id: 'boilerplate.components.Header.home', + defaultMessage: 'Home', + }, + features: { + id: 'boilerplate.components.Header.features', + defaultMessage: 'Features', + }, +}); diff --git a/app/components/Header/tests/index.test.js b/app/components/Header/tests/index.test.js index 4d98b5847b..a817b2d11b 100644 --- a/app/components/Header/tests/index.test.js +++ b/app/components/Header/tests/index.test.js @@ -1,14 +1,14 @@ import expect from 'expect'; -import { render } from 'enzyme'; +import { shallow } from 'enzyme'; import React from 'react'; import Header from '../index'; describe('
', () => { - it('should render the logo', () => { - const renderedComponent = render( + it('should render a div', () => { + const renderedComponent = shallow(
); - expect(renderedComponent.find('img').length).toEqual(1); + expect(renderedComponent.find('div').length).toEqual(1); }); }); diff --git a/app/containers/FeaturePage/index.js b/app/containers/FeaturePage/index.js index 4c5b2269d5..69140b948f 100644 --- a/app/containers/FeaturePage/index.js +++ b/app/containers/FeaturePage/index.js @@ -4,23 +4,16 @@ * List all the features */ import React from 'react'; -import { connect } from 'react-redux'; -import { push } from 'react-router-redux'; import Helmet from 'react-helmet'; import messages from './messages'; import { FormattedMessage } from 'react-intl'; -import Button from 'components/Button'; import H1 from 'components/H1'; import List from './List'; import ListItem from './ListItem'; import ListItemTitle from './ListItemTitle'; -export class FeaturePage extends React.Component { - openHomePage = () => { - this.props.dispatch(push('/')); - }; - +export default class FeaturePage extends React.Component { // eslint-disable-line react/prefer-stateless-function render() { return (
@@ -79,16 +72,7 @@ export class FeaturePage extends React.Component {

-
); } } - -FeaturePage.propTypes = { - dispatch: React.PropTypes.func, -}; - -export default connect()(FeaturePage); diff --git a/app/containers/FeaturePage/messages.js b/app/containers/FeaturePage/messages.js index 96ffec94a0..a8a16001a8 100644 --- a/app/containers/FeaturePage/messages.js +++ b/app/containers/FeaturePage/messages.js @@ -92,8 +92,4 @@ export default defineMessages({ id: 'boilerplate.containers.FeaturePage.internationalization.message', defaultMessage: 'Scalable apps need to support multiple languages, easily add and support multiple languages with `react-intl`.', }, - homeButton: { - id: 'boilerplate.containers.FeaturePage.home', - defaultMessage: 'Home', - }, }); diff --git a/app/containers/FeaturePage/tests/index.test.js b/app/containers/FeaturePage/tests/index.test.js index 49fca038c1..f2067ebe8b 100644 --- a/app/containers/FeaturePage/tests/index.test.js +++ b/app/containers/FeaturePage/tests/index.test.js @@ -2,10 +2,9 @@ import expect from 'expect'; import { shallow } from 'enzyme'; import React from 'react'; -import Button from 'components/Button'; import { FormattedMessage } from 'react-intl'; import messages from '../messages'; -import { FeaturePage } from '../index'; +import FeaturePage from '../index'; import H1 from 'components/H1'; describe('', () => { @@ -19,18 +18,4 @@ describe('', () => { )).toEqual(true); }); - - it('should link to "/"', (done) => { - // Spy on the openRoute method of the FeaturePage - const dispatch = (action) => { - expect(action.payload.args).toEqual('/'); - done(); - }; - - const renderedComponent = shallow( - - ); - const button = renderedComponent.find(Button); - button.prop('handleRoute')(); - }); }); diff --git a/app/containers/HomePage/index.js b/app/containers/HomePage/index.js index 400e5b5bca..4033088e4c 100644 --- a/app/containers/HomePage/index.js +++ b/app/containers/HomePage/index.js @@ -8,11 +8,9 @@ import React from 'react'; import Helmet from 'react-helmet'; import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; -import { push } from 'react-router-redux'; import { createStructuredSelector } from 'reselect'; import AtPrefix from './AtPrefix'; -import Button from 'components/Button'; import CenteredSection from './CenteredSection'; import Form from './Form'; import H2 from 'components/H2'; @@ -37,21 +35,6 @@ export class HomePage extends React.Component { this.props.onSubmitForm(); } } - /** - * Changes the route - * - * @param {string} route The route we want to go to - */ - openRoute = (route) => { - this.props.changeRoute(route); - }; - - /** - * Changed route to '/features' - */ - openFeaturesPage = () => { - this.openRoute('/features'); - }; render() { let mainContent = null; @@ -110,9 +93,6 @@ export class HomePage extends React.Component { {mainContent} - ); @@ -120,7 +100,6 @@ export class HomePage extends React.Component { } HomePage.propTypes = { - changeRoute: React.PropTypes.func, loading: React.PropTypes.bool, error: React.PropTypes.oneOfType([ React.PropTypes.object, @@ -138,13 +117,10 @@ HomePage.propTypes = { export function mapDispatchToProps(dispatch) { return { onChangeUsername: (evt) => dispatch(changeUsername(evt.target.value)), - changeRoute: (url) => dispatch(push(url)), onSubmitForm: (evt) => { if (evt !== undefined && evt.preventDefault) evt.preventDefault(); dispatch(loadRepos()); }, - - dispatch, }; } diff --git a/app/containers/HomePage/messages.js b/app/containers/HomePage/messages.js index f2113a76dd..14ef14e97f 100644 --- a/app/containers/HomePage/messages.js +++ b/app/containers/HomePage/messages.js @@ -26,8 +26,4 @@ export default defineMessages({ id: 'boilerplate.containers.HomePage.tryme.atPrefix', defaultMessage: '@', }, - featuresButton: { - id: 'boilerplate.containers.HomePage.features.Button', - defaultMessage: 'Features', - }, }); diff --git a/app/containers/HomePage/tests/index.test.js b/app/containers/HomePage/tests/index.test.js index c189a9db11..4309e45e66 100644 --- a/app/containers/HomePage/tests/index.test.js +++ b/app/containers/HomePage/tests/index.test.js @@ -10,7 +10,6 @@ import { IntlProvider } from 'react-intl'; import { HomePage, mapDispatchToProps } from '../index'; import { changeUsername } from '../actions'; import { loadRepos } from '../../App/actions'; -import { push } from 'react-router-redux'; import RepoListItem from 'containers/RepoListItem'; import List from 'components/List'; import LoadingIndicator from 'components/LoadingIndicator'; @@ -73,26 +72,6 @@ describe('', () => { expect(renderedComponent.contains()).toEqual(true); }); - it('should link to /features', () => { - const openRouteSpy = expect.createSpy(); - - // Spy on the openRoute method of the HomePage - const openRoute = (dest) => { - if (dest === '/features') { - openRouteSpy(); - } - }; - - const renderedComponent = mount( - - - - ); - const button = renderedComponent.find('button'); - button.simulate('click'); - expect(openRouteSpy).toHaveBeenCalled(); - }); - describe('mapDispatchToProps', () => { describe('onChangeUsername', () => { it('should be injected', () => { @@ -111,22 +90,6 @@ describe('', () => { }); }); - describe('changeRoute', () => { - it('should be injected', () => { - const dispatch = expect.createSpy(); - const result = mapDispatchToProps(dispatch); - expect(result.changeRoute).toExist(); - }); - - it('should dispatch push when called', () => { - const dispatch = expect.createSpy(); - const result = mapDispatchToProps(dispatch); - const route = '/'; - result.changeRoute(route); - expect(dispatch).toHaveBeenCalledWith(push(route)); - }); - }); - describe('onSubmitForm', () => { it('should be injected', () => { const dispatch = expect.createSpy(); diff --git a/app/containers/NotFoundPage/index.js b/app/containers/NotFoundPage/index.js index 2aa8c0f9d6..0368d35286 100644 --- a/app/containers/NotFoundPage/index.js +++ b/app/containers/NotFoundPage/index.js @@ -5,34 +5,17 @@ */ import React from 'react'; -import { connect } from 'react-redux'; -import { push } from 'react-router-redux'; import messages from './messages'; import { FormattedMessage } from 'react-intl'; -import Button from 'components/Button'; import H1 from 'components/H1'; -export function NotFound(props) { +export default function NotFound() { return (

-
); } - -NotFound.propTypes = { - dispatch: React.PropTypes.func, -}; - -// Wrap the component to inject dispatch and state into it -export default connect()(NotFound); diff --git a/app/containers/NotFoundPage/messages.js b/app/containers/NotFoundPage/messages.js index 28db83bf44..bdbad6b6c1 100644 --- a/app/containers/NotFoundPage/messages.js +++ b/app/containers/NotFoundPage/messages.js @@ -10,8 +10,4 @@ export default defineMessages({ id: 'boilerplate.containers.NotFoundPage.header', defaultMessage: 'Page not found.', }, - homeButton: { - id: 'boilerplate.containers.NotFoundPage.home', - defaultMessage: 'Home', - }, }); diff --git a/app/containers/NotFoundPage/tests/index.test.js b/app/containers/NotFoundPage/tests/index.test.js index fe2e7f27ea..637a7ccfaa 100644 --- a/app/containers/NotFoundPage/tests/index.test.js +++ b/app/containers/NotFoundPage/tests/index.test.js @@ -7,9 +7,8 @@ import { shallow } from 'enzyme'; import React from 'react'; import { FormattedMessage } from 'react-intl'; -import { NotFound } from '../index'; +import NotFound from '../index'; import H1 from 'components/H1'; -import Button from 'components/Button'; describe('', () => { it('should render the Page Not Found text', () => { @@ -24,25 +23,4 @@ describe('', () => { /> )).toEqual(true); }); - - it('should render a button', () => { - const renderedComponent = shallow( - - ); - const renderedButton = renderedComponent.find(Button); - expect(renderedButton.length).toEqual(1); - }); - - it('should link to "/"', (done) => { - const dispatch = (action) => { - expect(action.payload.args).toEqual('/'); - done(); - }; - - const renderedComponent = shallow( - - ); - const button = renderedComponent.find(Button); - button.prop('handleRoute')(); - }); }); diff --git a/app/translations/de.json b/app/translations/de.json index 8846b62819..73bdb7be02 100644 --- a/app/translations/de.json +++ b/app/translations/de.json @@ -3,12 +3,13 @@ "app.components.LocaleToggle.en": "", "boilerplate.components.Footer.author.message": "Mit Liebe gemacht von {author}.", "boilerplate.components.Footer.license.message": "Dieses Projekt wird unter der MIT-Lizenz veröffentlicht.", + "boilerplate.components.Header.home": "", + "boilerplate.containers.Header.features": "", "boilerplate.containers.FeaturePage.css.header": "", "boilerplate.containers.FeaturePage.css.message": "Die nächste Generation von CSS", "boilerplate.containers.FeaturePage.feedback.header": "Sofortiges Feedback", "boilerplate.containers.FeaturePage.feedback.message": "Genießen Sie die beste Entwicklungserfahrung und programmieren Sie Ihre App so schnell wie noch nie! Ihre Änderungen an dem CSS und JavaScript sind sofort reflektiert, ohne die Seite aktualisieren zu müssen. So bleibt der Anwendungszustand bestehen, auch wenn Sie etwas in dem darunter liegenden Code aktualisieren!", "boilerplate.containers.FeaturePage.header": "", - "boilerplate.containers.FeaturePage.home": "", "boilerplate.containers.FeaturePage.internationalization.header": "Komplette i18n Standard-Internationalisierung und Pluralisierung", "boilerplate.containers.FeaturePage.internationalization.message": "Das Internet ist global. Mehrsprachige- und Pluralisierungsunterstützung ist entscheidend für große Web-Anwendungen.", "boilerplate.containers.FeaturePage.javascript.header": "Das Internet ist global. Mehrsprachige- und Pluralisierungsunterstützung ist entscheidend für große Web-Anwendungen.", @@ -21,12 +22,10 @@ "boilerplate.containers.FeaturePage.scaffolding.message": "Automatisieren Sie die Kreation von Komponenten, Containern, Routen, Selektoren und Sagas – und ihre Tests – direkt von dem Terminal!", "boilerplate.containers.FeaturePage.state_management.header": "Berechenbare Stateverwaltung", "boilerplate.containers.FeaturePage.state_management.message": "Unidirectional data flow erlaubt uns alle Änderungen ihrer Applikation zu loggen und time travel debugging einzusetzen.", - "boilerplate.containers.HomePage.features.Button": "", "boilerplate.containers.HomePage.start_project.header": "Beginnen Sie Ihr nächstes React Projekt in Sekunden", "boilerplate.containers.HomePage.start_project.message": "Ein skalierendes, offline-first Fundament mit der besten DX und einem Fokus auf Performance und bewährte Methoden", "boilerplate.containers.HomePage.tryme.atPrefix": "", "boilerplate.containers.HomePage.tryme.header": "Probiere mich!", "boilerplate.containers.HomePage.tryme.message": "Zeige die Github Repositories von", - "boilerplate.containers.NotFoundPage.header": "Seite nicht gefunden.", - "boilerplate.containers.NotFoundPage.home": "" + "boilerplate.containers.NotFoundPage.header": "Seite nicht gefunden." } diff --git a/app/translations/en.json b/app/translations/en.json index 5591a8bd01..273fa275bb 100644 --- a/app/translations/en.json +++ b/app/translations/en.json @@ -1,12 +1,13 @@ { "boilerplate.components.Footer.author.message": "Made with love by {author}.", "boilerplate.components.Footer.license.message": "This project is licensed under the MIT license.", + "boilerplate.components.Header.home": "Home", + "boilerplate.containers.Header.features": "Features", "boilerplate.containers.FeaturePage.css.header": "Features", "boilerplate.containers.FeaturePage.css.message": "Next generation CSS", "boilerplate.containers.FeaturePage.feedback.header": "Instant feedback", "boilerplate.containers.FeaturePage.feedback.message": "Enjoy the best DX and code your app at the speed of thought! Your\n saved changes to the CSS and JS are reflected instantaneously\n without refreshing the page. Preserve application state even when\n you update something in the underlying code!", "boilerplate.containers.FeaturePage.header": "Features", - "boilerplate.containers.FeaturePage.home": "Home", "boilerplate.containers.FeaturePage.internationalization.header": "Complete i18n Standard Internationalization & Pluralization", "boilerplate.containers.FeaturePage.internationalization.message": "Scalable apps need to support multiple languages, easily add and support multiple languages with `react-intl`.", "boilerplate.containers.FeaturePage.javascript.header": "Next generation JavaScript", @@ -19,12 +20,10 @@ "boilerplate.containers.FeaturePage.scaffolding.message": "Automate the creation of components, containers, routes, selectors\n and sagas - and their tests - right from the CLI!", "boilerplate.containers.FeaturePage.state_management.header": "Predictable state management", "boilerplate.containers.FeaturePage.state_management.message": "Unidirectional data flow allows for change logging and time travel\n debugging.", - "boilerplate.containers.HomePage.features.Button": "Features", "boilerplate.containers.HomePage.start_project.header": "Start your next react project in seconds", "boilerplate.containers.HomePage.start_project.message": "A highly scalable, offline-first foundation with the best DX and a focus on performance and best practices", "boilerplate.containers.HomePage.tryme.atPrefix": "@", "boilerplate.containers.HomePage.tryme.header": "Try me!", "boilerplate.containers.HomePage.tryme.message": "Show Github repositories by", - "boilerplate.containers.NotFoundPage.header": "Page not found.", - "boilerplate.containers.NotFoundPage.home": "Home" + "boilerplate.containers.NotFoundPage.header": "Page not found." }