Don't wanna be here? Send us removal request.
Text
Redux Testing
Redux testing is essential in order to keep your store reliable and understandable. The great thing about Redux is that it is framework agnostic and writing tests for it is really easy.
Actions
Here are the actions we can perform.
export const INCREMENT = 'INCREMENT'; export const DECREMENT = 'DECREMENT'; export const ADD_COUNTER = 'ADD_COUNTER'; export const REMOVE_COUNTER = 'REMOVE_COUNTER';
Reducer
Let's start with a simple reducer for counters.
import * as types from '../constants/ActionTypes'; const CountersReducer = (state = [0], action) => { switch (action.type) { case types.INCREMENT: return [ ...state.slice(0, action.index), state[action.index] + 1, ...state.slice(action.index + 1), ]; case types.DECREMENT: return [ ...state.slice(0, action.index), state[action.index] - 1, ...state.slice(action.index + 1), ]; case types.ADD_COUNTER: return [...state, 0]; case types.REMOVE_COUNTER: return [ ...state.slice(0, action.index), ...state.slice(action.index + 1), ]; default: return state; } }; export default CountersReducer;
This reducer has four actions that be performed. It can increment or decrement a single counter, and it can remove counters from the state.
Tests
Now for the testing of this reducer.
import CountersReducer from '../CountersReducer'; import * as types from '../../constants/ActionTypes'; import deepFreeze from 'deep-freeze'; describe('Counters Reducer', () => { it('should return the initial state', () => { expect(CountersReducer(undefined, {})).toEqual([0]); }); it('should increment the counter', () => { const beforeState = [0]; const afterState = [1]; const action = { type: types.INCREMENT, index: 0 }; deepFreeze(beforeState); expect(CountersReducer(beforeState, action)).toEqual(afterState); }); it('should decrement the counter', () => { const beforeState = [0]; const afterState = [-1]; const action = { type: types.DECREMENT, index: 0 }; deepFreeze(beforeState); expect(CountersReducer(beforeState, action)).toEqual(afterState); }); it('should add a counter', () => { const beforeState = [0]; const afterState = [0, 0]; const action = { type: types.ADD_COUNTER }; deepFreeze(beforeState); expect(CountersReducer(beforeState, action)).toEqual(afterState); }); it('should remove a counter', () => { const beforeState = [0, 0]; const afterState = [0]; const index = 0; const action = { type: types.REMOVE_COUNTER, index }; deepFreeze(beforeState); expect(CountersReducer(beforeState, action)).toEqual(afterState); }); });
The first test begins with testing the initial state of the reducer. In this case we start with one counter with a default value of 0.
The next tests increments the first counter by one and after that decrements the first counter.
Then the final tests add and remove a counter.
Conclusion
Testing Redux is fairly simple and straightforward. There are some more complex situations and asynchronous behavior that might get tricky, but in the end your app with thank you.
0 notes
Text
Redux Todos
Redux is a state manager for Javascript. You can use these patterns to store your state in any Javascript project not just React. It will help you write consistent behavior running in different environments. You can learn more about redux at:
https://redux.js.org/
We will be building the Todos store based on the tutorial from the Redux website and including it in our React Material UI Todos app. Here are the links to the tutorial and full code base.
https://redux.js.org/basics
https://gitlab.com/nthchildconsulting/material-ui-todos
Redux
First we will create a folder called redux in the src directory of our app. This way we can keep everything self contained.
Constants
I like to create a folder called constants that will hold all of our constants we need throughout our store. Within that folder create a file called ActionTypes.js that will describe what kind of actions we can perform on our store.
src/redux/constants/ActionTypes.js
export const ADD_TODO = 'ADD_TODO'; export const TOGGLE_TODO = 'TOGGLE_TODO'; export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';
You can see that we will perform three simple actions. One to create a todo, toggle a todo complete, and setting the visibility of what todos have been completed or not.
Actions
Now we will create a folder called actions that will hold all of the actions that we will perform in our store.
src/redux/actions/TodosActions.js
import * as types from '../constants/ActionTypes'; let nextTodoId = 0; export const addTodo = text => ({ type: types.ADD_TODO, id: nextTodoId++, text, }); export const setVisibilityFilter = filter => ({ type: types.SET_VISIBILITY_FILTER, filter, }); export const toggleTodo = id => ({ type: types.TOGGLE_TODO, id, }); export const VisibilityFilters = { SHOW_ALL: 'SHOW_ALL', SHOW_COMPLETED: 'SOME_COMPLETED', SHOW_ACTIVE: 'SHOW_ACTIVE', };
Within the reducer of Redux it will expect a type and any other data needed to perform the action. In this file we setup the data structure that will be sent into the reducer. We have three functions, addTodo, toggleTodo, and setVisibilityFilter. You will be able to use these functions within your app.
Reducers
Reducers are where the implementations of the actions happen. Create a folder called reducers. We will add two reducers to the folder.
src/redux/reducers/TodosReducer.js
import * as types from '../constants/ActionTypes'; const TodosReducer = (state = [], action) => { switch (action.type) { case types.ADD_TODO: return [ ...state, { id: action.id, text: action.text, completed: false, }, ]; case types.TOGGLE_TODO: return state.map(todo => { return todo.id === action.id ? { ...todo, completed: !todo.completed } : todo; }); default: return state; } }; export default TodosReducer;
You can see from the code that we are checking the type of action and returning a brand new state based on the action. When we call addTodo it will create a new todo object and put it on the state. If we call toggleTodo it will map through all the todos and toggle the one we clicked on.
src/redux/reducers/VisibilityFilterReducer.js
import { VisibilityFilters } from '../actions/TodosActions'; import * as types from '../constants/ActionTypes'; const VisibilityFilterReducer = ( state = VisibilityFilters.SHOW_ALL, action ) => { switch (action.type) { case types.SET_VISIBILITY_FILTER: return action.filter; default: return state; } }; export default VisibilityFilterReducer;
This reducer will handle the setVisibilityFilter action and just set what will be visible in our todos.
Now lets combine both of our reducers to use in our main store.
src/redux/reducers/index.js
import { combineReducers } from 'redux'; import TodosReducer from './TodosReducer'; import VisibilityFilterReducer from './VisibilityFilterReducer'; export default combineReducers({ todos: TodosReducer, visibilityFilter: VisibilityFilterReducer, });
src/redux/store.js
import { applyMiddleware, createStore } from 'redux'; import logger from 'redux-logger'; import thunkMiddleware from 'redux-thunk'; import reducers from './reducers'; export default createStore(reducers, applyMiddleware(thunkMiddleware, logger));
Now we can add in the store into our React app, or whatever app you need to.
src/index.js
import React from 'react'; import App from './containers/App/App'; import { render } from 'react-dom'; import registerServiceWorker from './registerServiceWorker'; import './index.css'; import { Provider } from 'react-redux'; import store from './redux/store'; const rootElement = document.getElementById('root'); render( <Provider store={store}> <App /> </Provider>, rootElement ); registerServiceWorker();
We have setup in our React app to use a Provider with our redux store. We can then dispatch actions to the store. Here is an example of adding a todo.
src/containers/AddTodo/AddTodo.js
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { addTodo } from '../../redux/actions/TodosActions'; import Input from '@material-ui/core/Input'; import Button from '@material-ui/core/Button'; import AddIcon from '@material-ui/icons/Add'; import { withStyles } from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; const styles = theme => ({ button: { [theme.breakpoints.down('xs')]: { width: '35px', height: '35px', }, }, }); class AddTodo extends Component { state = { todo: '', }; handleChange = event => { this.setState({ todo: event.target.value }); }; handleClick = () => { if (!this.state.todo.trim()) { return; } this.props.dispatch(addTodo(this.state.todo)); this.setState({ todo: '' }); }; render() { const { classes } = this.props; return ( <div> <Grid container spacing={24} justify="center"> <Grid item xs={10} sm={10}> <Input fullWidth placeholder="What will you do next?" value={this.state.todo} onChange={this.handleChange} onKeyPress={e => { if (e.key === 'Enter') { e.preventDefault(); this.handleClick(); } }} /> </Grid> <Grid item xs={2}> <Button variant="fab" color="primary" aria-label="add" className={classes.button} onClick={this.handleClick} > <AddIcon /> </Button> </Grid> </Grid> </div> ); } } AddTodo.propTypes = { classes: PropTypes.object.isRequired, }; export default connect()(withStyles(styles)(AddTodo));
We can use the redux connect to add the store to our props. When we click on the button to add a todo it will call the handleClick event and dispatch our addTodo action.
Conclusion
There is a bit of boilerplate when using Redux but you can explicitly set up all your actions and easily test all the of functionality of your app. Once you have all your actions coded all you need to do is provide your app with the Redux store and it will be available to all your components.
0 notes
Text
Material UI Todos
Creating a Todos application is all the buzz now days. Getting with the times requires learning new ways to do things. We will code some React components that will use Google Material Design to quickly get some awesome looking interfaces.
The entire code base is located at:
https://gitlab.com/nthchildconsulting/material-ui-todos
We will be using the following technologies:
Create React App
Material UI
Redux
React App
index.js
import React from 'react'; import App from './containers/App/App'; import { render } from 'react-dom'; import registerServiceWorker from './registerServiceWorker'; import './index.css'; import { Provider } from 'react-redux'; import store from './redux/store'; const rootElement = document.getElementById('root'); render( <Provider store={store}> <App /> </Provider>, rootElement ); registerServiceWorker();
In this file we import our main App container and our Redux store. We then inject the store in a Provider component so that all the child components will have access to the Redux store.
We will focus on React components in this article. In the next article we will focus on the Redux store.
containers/App/App.js
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Footer from '../../containers/Footer/Footer'; import AddTodo from '../../containers/AddTodo/AddTodo'; import VisibilityTodoList from '../../containers/VisibilityTodoList/VisibilityTodoList'; import { withStyles } from '@material-ui/core/styles'; import Paper from '@material-ui/core/Paper'; import Grid from '@material-ui/core/Grid'; const styles = theme => ({ root: { flexGrow: 1, backgroundColor: theme.palette.background.paper, }, paper: { padding: theme.spacing.unit * 6, [theme.breakpoints.down('xs')]: { padding: theme.spacing.unit * 2, }, textAlign: 'center', color: theme.palette.text.secondary, }, }); class App extends Component { render() { const { classes } = this.props; return ( <div className={classes.root}> <Grid container spacing={24} justify="center"> <Grid item xs={12} sm={10} md={8}> <Paper className={classes.paper}> <AddTodo /> <VisibilityTodoList /> </Paper> </Grid> </Grid> <Footer /> </div> ); } } App.propTypes = { classes: PropTypes.object.isRequired, }; export default withStyles(styles)(App);
In the main App component we will start with some Material UI grids. We use the grid system for consistency between layouts. You can read more about grids here:
https://material-ui.com/layout/grid/
First create a Grid container then next the children under the container with Grid item
Our main app grid will just contain one item that will include add todo, and todo list components.
containers/AddTodo/AddTodo.js
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { addTodo } from '../../redux/actions/TodosActions'; import Input from '@material-ui/core/Input'; import Button from '@material-ui/core/Button'; import AddIcon from '@material-ui/icons/Add'; import { withStyles } from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; const styles = theme => ({ button: { [theme.breakpoints.down('xs')]: { width: '35px', height: '35px', }, }, }); class AddTodo extends Component { state = { todo: '', }; handleChange = event => { this.setState({ todo: event.target.value }); }; handleClick = () => { if (!this.state.todo.trim()) { return; } this.props.dispatch(addTodo(this.state.todo)); this.setState({ todo: '' }); }; render() { const { classes } = this.props; return ( <div> <Grid container spacing={24} justify="center"> <Grid item xs={10} sm={10}> <Input fullWidth placeholder="What will you do next?" value={this.state.todo} onChange={this.handleChange} onKeyPress={e => { if (e.key === 'Enter') { e.preventDefault(); this.handleClick(); } }} /> </Grid> <Grid item xs={2}> <Button variant="fab" color="primary" aria-label="add" className={classes.button} onClick={this.handleClick} > <AddIcon /> </Button> </Grid> </Grid> </div> ); } } AddTodo.propTypes = { classes: PropTypes.object.isRequired, }; export default connect()(withStyles(styles)(AddTodo));
In the AddTodo component we will have another grid container with two grid items. The great thing about Material UI is that you can use its breakpoints to use for different width devices.
xs={10} // For smaller devices only span 10 columns sm={8} // For small devices and up only span 8 columns
Then using the state through React, once the button is clicked, it will dispatch a Redux action to add the todo to the store.
components/TodoList/TodoList.js
import React from 'react'; import PropTypes from 'prop-types'; import Todo from '../Todo/Todo'; import List from '@material-ui/core/List'; const TodoList = ({ todos, toggleTodo }) => ( <List> {todos.map(todo => ( <Todo key={todo.id} {...todo} onClick={() => toggleTodo(todo.id)} /> ))} </List> ); TodoList.propTypes = { todos: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.number.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired, }).isRequired ).isRequired, toggleTodo: PropTypes.func.isRequired, }; export default TodoList;
In the TodoList we bring in the List component which has many customizable properties. You can read more here:
https://material-ui.com/demos/lists/
components/Todo/Todo.js
import React from 'react'; import PropTypes from 'prop-types'; import ListItem from '@material-ui/core/ListItem'; import ListItemText from '@material-ui/core/ListItemText'; const Todo = ({ onClick, completed, text }) => ( <ListItem divider onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none', }} > <ListItemText primary={text} /> </ListItem> ); Todo.propTypes = { onClick: PropTypes.func.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired, }; export default Todo;
In the Todo component we use the ListItem and ListItemText components. This makes it easy for material ui to line up components.
Conclusion
With just a few Material UI components you can quickly make an expressive UI for use on modern browsers and mobile devices.
In the next article we will focus on the Redux store.
0 notes
Text
Generating a Favicon
Generating favicons for your website can be created in many ways. You can use any graphic editor you wish but for this article we will be using GIMP. It is free and for suited for our uses here in this article.
GIMP homepage
We will also be using an online generator with our image that will generate all the needed icons for different platforms.
https://realfavicongenerator.net/
GIMP
Select File > New
Set the width and height to 260
Set the Fill With option to Transparency
Now you have a canvas to create your icon. Remember this is a large image and will be shrunk down to image sizes such as 16x16 and 32x32. Making it this large to begin with is recommended by the realfavicongenerator app.
To make rounded corners select the Rectangle Select Tool and then in the settings area mark the checkbox named Rounded Corners.
Now select an area that isn't quite as big as the whole canvas.
Fill in the background with your color schema and add some text with your logo. Make sure it isn't going outside the selection. Give it some headroom so it won't get cutoff when generating the icon.
Real Fav Icon Generator App
Now go to:
https://realfavicongenerator.net/
Upload your image and it will give you some options about what icons to generate.
Usually stick with the defaults.
Then it will show a download Favicon Package button. Click that and download your icons.
It will also tell you where to put your assets and what HTML to use, such as:
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><link rel="mask-icon" href="/safari-pinned-tab.svg"><meta content="#da532c"><meta content="#ffffff">
0 notes
Text
Gitlab deploy to Heroku
Setting up a Gitlab deploy to Heroku is pretty easy. We will use the code from the last article to build upon our Node.js Express API. If you need to go back and begin with that article please do so.
Last Article - Docker Node.js Express Server
Create your Gitlab Repo
On your main Gitlab homepage click New Project
Give it the name of my-app
You will want to leave it as a private app since you will use a Heroku secret API key to deploy. However for this article I am leaving open to public so you can view the whole repo.
Here is the link to the full source code:
Gitlab Repo
If this is your first time using Gitlab I suggest you create an SSH key to use with Git. Here is an article on how to do that.
Gitlab SSH Keys
Let's begin by cloning the repo to our local machine.
git clone [email protected]:example/my-app.git cd my-app
You will want to change example to your own user.
Now that we have created our Gitlab repo we can now move over our code from the last article and set up some deploy scripts.
Gitlab CI
Create a file called .gitlab-ci.yml in the root of your project.
image: docker:git services: - docker:dind stages: - build - release - deploy variables: GITLAB_GROUP: example GITLAB_PROJECT: my-app GITLAB_CONTAINER_TEST_IMAGE: registry.gitlab.com/$GITLAB_GROUP/$GITLAB_PROJECT:$CI_COMMIT_SHA GITLAB_CONTAINER_RELEASE_IMAGE: registry.gitlab.com/$GITLAB_GROUP/$GITLAB_PROJECT:latest HEROKU_CONTAINER_RELEASE_IMAGE: registry.heroku.com/$GITLAB_PROJECT/web before_script: - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.com build: stage: build script: - docker build -t $GITLAB_CONTAINER_TEST_IMAGE . - docker push $GITLAB_CONTAINER_TEST_IMAGE release: stage: release script: - docker pull $GITLAB_CONTAINER_TEST_IMAGE - docker tag $GITLAB_CONTAINER_TEST_IMAGE $GITLAB_CONTAINER_RELEASE_IMAGE - docker push $GITLAB_CONTAINER_RELEASE_IMAGE only: - master deploy: stage: deploy script: - docker login --username=_ --password=$HEROKU_STAGING_API_KEY registry.heroku.com - docker build -t $HEROKU_CONTAINER_RELEASE_IMAGE . - docker push $HEROKU_CONTAINER_RELEASE_IMAGE only: - master when: manual
Change the GITLAB_GROUP to your own group.
This will tell Gitlab how to deploy your repo to Heroku. There are three stages. build, release, deploy
There are many predefined variables but the only one we need to care about is:
HEROKU_STAGING_API_KEY
On the left nav menu of Gitlab go to Settings > CI / CD > Variables.
Add a variable called HEROKU_STAGING_API_KEY and give it the value of your Heroku API Key. You can generate an API key by going to your Heroku settings page.
Now every time you merge into the master branch it will build a docker container ready to deploy to your Heroku account.
Go to CI / CD in Gitlab and you will be able to see your pipeline building. Then when it is ready to deploy you can click the manual play button on the right to deploy to Heroku.
0 notes
Text
Docker Node.js Express Server
Setting up a Node.js Express server to use within Docker is useful. Docker will be helpful to quickly mock up some api endpoints and keep environments simple.
In the next article we will go through what it will take to deploy this Docker container to Heroku with Gitlab.
Creating our working directory
Let's create a directory we can work out of. In this directory we will have all our files to run a Node.js Express server, and a Docker container.
In the next article we will set up this directory to use version control with Gitlab.
mkdir my-app cd my-app
Creating a Node.js Express server
Let's start building our Express server. If you don't know what Node.js or Express is then I suggest you research those technologies. This article assumes you know the basics of Javascript, Node.js, and Express.
Create the package.json file:
npm init
Running this command will ask you some questions about how to set up your Node package. Give it the name my-app and accept the default parameters.
Now we will install a npm package to help us set up the Express server. If you need more control over Express I suggest you use the Express package right out of the box.
npm install --save boilerpress
Let's create some run scripts we can use to start the server. Add this to your package.json file.
"scripts": { "start": "node /usr/src/app/index.js" }
Create an index.js file:
const { app, start } = require('boilerpress'); const os = require('os'); // Add any middleware you need app.use((req, res, next) => { res.locals = { host: os.hostname(), title: 'Express', }; next(); }); app.use((req, res) => { res.send('Hello World!'); }); // Start the Express server start();
When we run npm start it will start the server we defined in the index.js file. Using the package boilerpress it will help us quickly mock up a server.
Creating the Docker image
Create a file named Dockerfile:
FROM node RUN mkdir -p /usr/src/app COPY . /usr/src/app WORKDIR /usr/src/app RUN npm i CMD npm start
This is the a simple Dockerfile. You will probably want to set a specific version of the node image to build from and set up a user, but this will work for a quick mock up.
This will copy over the entire working directory to inside the docker image at /usr/src/app.
Create a docker-compose.yml file:
version: '3' services: boilerpress: build: . ports: - "3000:3000" env_file: - .env
Let's set up an .env file that we can use to have environment variables inside our docker image.
Create a .env file:
NODE_ENV=development PORT=3000
Now we can run docker commands to build our image and run the container:
docker-compose build docker-compose up
Testing the running server
Navigate with your browser to:
localhost:3000
You will see the words Hello World!
In the next article we will build upon this example, set up some more api endpoints and go through what it will take to deploy this Docker server to Heroku with Gitlab.
0 notes