Skip to content

Commit

Permalink
Created a redux flash messages library
Browse files Browse the repository at this point in the history
  • Loading branch information
MrHus committed Apr 4, 2017
1 parent d2b1241 commit 77c7d96
Show file tree
Hide file tree
Showing 15 changed files with 820 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"presets": ["es2015", "react"],
"plugins": [
"transform-object-rest-spread",
"transform-async-to-generator",
["transform-runtime", {
"polyfill": false,
"regenerator": true
}],
"transform-flow-strip-types"
]
}
8 changes: 8 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[ignore]
lib/

[include]

[libs]

[options]
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.

# Build output
/lib

# dependencies
/node_modules

# testing
/coverage

# misc
.DS_Store
.env
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.vscode
6 changes: 6 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
tests
.babelrc
.vscode
coverage
.flowconfig
src
7 changes: 7 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Copyright 2016-2017 42BV (http://www.42.nl)

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

207 changes: 207 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
# About

This library makes it easy to create flash messages and to store them in a Redux store.

What makes this project a little different from most flash message libraries is that it is UI agnostic. This library does not render the FlashMessages for you it only stores them!

# Installation

`npm install redux-flash-messages --save`

# Getting started.

We assume you have a working Redux project, if you do not yet have Redux add Redux to your project by following the Redux's instructions.

First install the following dependencies in the package.json:

1. "react-redux": "5.0.3",
2. "redux": "3.6.0",

Now add the flash-message-reducer to your rootReducer for example:

```JavaScript
// @flow

import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';

import type { FlashMessageStore } from '../jarb-redux-form';
import { flashMessage } from '../jarb-redux-form';

export type Store = {
flashMessage: FlashMessageStore
};

// Use ES6 object literal shorthand syntax to define the object shape
const rootReducer = combineReducers({
flashMessage
});

export default rootReducer;
```

This should add the FlashMessageStore to Redux, which will store
the flash messages.

Next you have to configure the flashMessages module:

```JavaScript
import { createStore } from 'redux';
import { configureFlashMessages } from 'redux-flash-messages';

export const store = createStore(
rootReducer,
);

configureFlashMessages({
// The dispatch function for the Redux store.
dispatch: store.dispatch
});
```

The redux-flash-messages module must be configured before the application is rendered.

# Rendering flash messages

Next we need to render the flash messages from the Redux store. How you do this is entirely up to you, but here is a small example:

```JavaScript
// @flow

import React, { Component } from 'react';
import { connect } from 'react-redux';

import type { Store } from '../../../redux/root-reducer';
import type { Dispatch } from '../../../redux/models';

import type { FlashMessage as FlashMessageShape } from 'redux-flash-messages';
import { removeFlashMessage } from 'redux-flash-messages';

import './FlashMessage.css';

type Props = {
messages: Array<FlashMessageShape>,
dispatch: Dispatch
};

export class FlashMessage extends Component<void, Props, void> {

onFlashMessageClick(flashMessage: FlashMessageShape) {
/*
Make sure the onClick is called when a user clicks
on the flash message.
Otherwise callbacks on Flash Messages will not work.
*/
flashMessage.onClick(flashMessage);

// This implementation deletes the flash message when it is clicked.
this.props.dispatch(removeFlashMessage(flashMessage.id));
}

render() {
const messages = this.props.messages;

return (
<div>
{ messages.map((message) => this.renderMessage(message))}
</div>
);
}

/*
This renders a rather simple flash message.
But you could and should use the 'type' property to
render the flash message in a different style for each 'type'.
*/
renderMessage(message: FlashMessageShape) {
return (
<div
key={ message.id }
className={ `flash-message ${message.type}`}
onClick={ () => this.onFlashMessageClick(message) }
>
{ message.text }
</div>
);
}
}

export default connect((store: Store) => {
return {
messages: store.flashMessage.messages
};
})(FlashMessage);
```

Where the contents of 'FlashMessage.css' is:

```CSS
.flash-message {
position: absolute;
width: 100%;
height: 50px;
text-align: center;
z-index: 9000;
background-color: white;
border: black solid 2px;
padding: 12.5px 0;
}
```

# Sending flash messages

Now that we can see the flash messages we can use the following convenience methods to send flash messages:

```JavaScript
import { addError, addWarning, addSuccess, addInfo, addApocalypse } from '../src/service';

// Renders a message for 10000 milliseconds
addError({ text: 'Epic error', data: { age: 12 }, onClick: (flashMessage) => {
console.log('I was clicked');
console.log(flashMessage);
}});

// Renders a message for 7000 milliseconds
addWarning({ text: 'Epic warning', data: { tree: 'house' }, onClick: (flashMessage) => {
console.log('I was clicked');
console.log(flashMessage);
}});

// Renders a message for 2000 milliseconds
addSuccess({ text: 'Epic success', data: { win: true }, onClick: (flashMessage) => {
console.log('I was clicked');
console.log(flashMessage);
}});

// Renders a message for 5000 milliseconds
addInfo({ text: 'Epic info', data: { yo: 'man' }, onClick: (flashMessage) => {
console.log('I was clicked');
console.log(flashMessage);
}});

// Renders a message which is not automatically removed
addApocalypse({ text: 'TOTAL ANNIHILATION', data: { fail: true }, onClick: (flashMessage) => {
console.log('I was clicked');
console.log(flashMessage);
}});
```

The `onClick` and the `data` keys are optional. The `data`
key can be used to send whatever `data` you want to the
component which renders the flash message.

# Creating a custom flash message type.

If the default types are not enough you can always create your own flash message creator:

You do this by calling `addFlashMessageOfType` manually.

```JavaScript
import { addFlashMessageOfType } from '../src/service';

export function addNotice({ text, onClick, data }: FlashMessageConfig) {
addFlashMessageOfType('NOTICE', 1000, text, onClick, data);
}
```
53 changes: 53 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "redux-flash-messages",
"version": "0.0.1",
"description": "Storing flash messages and removing them via Redux.",
"files": [
"lib"
],
"main": "lib/index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/42BV/redux-flash-messages.git"
},
"keywords": [
"redux",
"flash-messages"
],
"author": "Maarten Hus",
"license": "ISC",
"bugs": {
"url": "https://github.com/42BV/redux-flash-messages/issues"
},
"homepage": "https://github.com/42BV/redux-flash-messages#readme",
"peerDependencies": {
"redux": "^3.6.0"
},
"devDependencies": {
"babel": "6.23.0",
"babel-cli": "6.24.0",
"babel-jest": "19.0.0",
"babel-plugin-transform-async-to-generator": "6.22.0",
"babel-plugin-transform-flow-strip-types": "6.22.0",
"babel-plugin-transform-object-rest-spread": "6.23.0",
"babel-plugin-transform-runtime": "6.23.0",
"babel-preset-es2015": "6.22.0",
"babel-preset-react": "6.23.0",
"babel-runtime": "6.23.0",
"flow-bin": "0.39.0",
"flow-copy-source": "1.1.0",
"jest": "19.0.2",
"react": "15.4.2",
"react-addons-test-utils": "15.4.2",
"react-dom": "15.4.2",
"regenerator-runtime": "0.10.3"
},
"scripts": {
"test": "jest test",
"coverage": "npm test -- --coverage",
"flow": "flow",
"prepublish": "npm run flow && npm test && npm run babel-prepublish && npm run flow-prepublish",
"babel-prepublish": "babel src/ -d lib",
"flow-prepublish": "flow-copy-source src lib"
}
}
31 changes: 31 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// @flow

export type Config = {
// The dispatch function for the Redux store.
dispatch: () => void,
};

let config: Config | null = null;

/**
* Configures the FlashMessages libary.
*
* @param {Config} The new configuration
*/
export function configureFlashMessages(c: Config) {
config = c;
}

/**
* Either returns the a Config or throws an error when the
* config is not yet initialized.
*
* @returns The Config
*/
export function getConfig(): Config {
if (config === null) {
throw new Error('The flash message service is not initialized.');
} else {
return config;
}
}
47 changes: 47 additions & 0 deletions src/flash-message-reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// @flow

import type { FlashMessage } from './models';

type Action = {
type: string,
payload: any
};

export const ADD_FLASH_MESSAGE = 'ADD_FLASH_MESSAGE';
export const REMOVE_FLASH_MESSAGE = 'REMOVE_MESSAGE';

export type FlashMessageStore = {
messages: Array<FlashMessage>
};

export const initialState: FlashMessageStore = {
messages: [],
};

export function flashMessage(state: FlashMessageStore = initialState, action: Action): FlashMessageStore {
switch(action.type) {
case ADD_FLASH_MESSAGE: {
const messages = [...state.messages, action.payload.flashMessage];
return { ...state, messages};
}

case REMOVE_FLASH_MESSAGE: {
const flashMessageId = action.payload.flashMessageId;

const messages = state.messages.filter((f) => flashMessageId !== f.id);
return { ...state, messages };
}

default: {
return state;
}
}
}

export function addFlashMessage(flashMessage: FlashMessage): Action {
return { type: ADD_FLASH_MESSAGE, payload: { flashMessage } };
}

export function removeFlashMessage(flashMessageId: number): Action {
return { type: REMOVE_FLASH_MESSAGE, payload: { flashMessageId } };
}
Loading

0 comments on commit 77c7d96

Please sign in to comment.