Remove examples feature
Change-Id: Id347a0a8b62c3f9df7e5b2e06a0daf4e989626dd
This commit is contained in:
parent
deae3535bc
commit
3749005995
@ -2,7 +2,6 @@ import { combineReducers } from 'redux';
|
||||
import { routerReducer } from 'react-router-redux';
|
||||
import homeReducer from '../features/home/redux/reducer';
|
||||
import commonReducer from '../features/common/redux/reducer';
|
||||
import examplesReducer from '../features/examples/redux/reducer';
|
||||
|
||||
// NOTE 1: DO NOT CHANGE the 'reducerMap' name and the declaration pattern.
|
||||
// This is used for Rekit cmds to register new features, remove features, etc.
|
||||
@ -13,7 +12,6 @@ const reducerMap = {
|
||||
router: routerReducer,
|
||||
home: homeReducer,
|
||||
common: commonReducer,
|
||||
examples: examplesReducer,
|
||||
};
|
||||
|
||||
export default combineReducers(reducerMap);
|
||||
|
@ -2,7 +2,6 @@ import { App } from '../features/home';
|
||||
import { PageNotFound } from '../features/common';
|
||||
import homeRoute from '../features/home/route';
|
||||
import commonRoute from '../features/common/route';
|
||||
import examplesRoute from '../features/examples/route';
|
||||
import _ from 'lodash';
|
||||
|
||||
// NOTE: DO NOT CHANGE the 'childRoutes' name and the declaration pattern.
|
||||
@ -10,7 +9,6 @@ import _ from 'lodash';
|
||||
const childRoutes = [
|
||||
homeRoute,
|
||||
commonRoute,
|
||||
examplesRoute,
|
||||
];
|
||||
|
||||
const routes = [{
|
||||
|
@ -1,45 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import * as actions from './redux/actions';
|
||||
|
||||
export class CounterPage extends Component {
|
||||
static propTypes = {
|
||||
examples: PropTypes.object.isRequired,
|
||||
actions: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="examples-counter-page">
|
||||
<h1>Counter</h1>
|
||||
<p>This is simple counter demo to show how Redux sync actions work.</p>
|
||||
<button className="btn-minus-one" onClick={this.props.actions.counterMinusOne} disabled={this.props.examples.count === 0}>
|
||||
-
|
||||
</button>
|
||||
<span>{this.props.examples.count}</span>
|
||||
<button className="btn-plus-one" onClick={this.props.actions.counterPlusOne}>+</button>
|
||||
<button className="btn-reset" onClick={this.props.actions.counterReset}>
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
examples: state.examples,
|
||||
};
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({ ...actions }, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(CounterPage);
|
@ -1,18 +0,0 @@
|
||||
@import '../../styles/mixins';
|
||||
|
||||
.examples-counter-page {
|
||||
color: #555;
|
||||
h1 {
|
||||
margin-top: 0px;
|
||||
font-size: 28px;
|
||||
}
|
||||
span {
|
||||
padding: 0 10px;
|
||||
display: inline-block;
|
||||
min-width: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
button.btn-reset {
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { SidePanel } from './';
|
||||
|
||||
export default class Layout extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="examples-layout">
|
||||
<SidePanel />
|
||||
<div className="examples-page-container">
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
@import '../../styles/mixins';
|
||||
|
||||
.examples-layout {
|
||||
.examples-page-container {
|
||||
padding: 40px 30px 0 300px;
|
||||
min-width: 400px;
|
||||
max-width: 800px;
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { fetchRedditList } from './redux/actions';
|
||||
|
||||
export class RedditListPage extends Component {
|
||||
static propTypes = {
|
||||
examples: PropTypes.object.isRequired,
|
||||
actions: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { fetchRedditListPending, redditList, fetchRedditListError } = this.props.examples;
|
||||
const { fetchRedditList } = this.props.actions;
|
||||
|
||||
return (
|
||||
<div className="examples-reddit-list-page">
|
||||
<h1>Reddit API Usage</h1>
|
||||
<p>This demo shows how to use Redux async actions to fetch data from Reddit's REST API.</p>
|
||||
<button className="btn-fetch-reddit" disabled={fetchRedditListPending} onClick={fetchRedditList}>
|
||||
{fetchRedditListPending ? 'Fetching...' : 'Fetch reactjs topics'}
|
||||
</button>
|
||||
{fetchRedditListError && (
|
||||
<div className="fetch-list-error">Failed to load: {fetchRedditListError.toString()}</div>
|
||||
)}
|
||||
{redditList.length > 0 ? (
|
||||
<ul className="examples-reddit-list">
|
||||
{redditList.map(item => (
|
||||
<li key={item.data.id}>
|
||||
<a href={item.data.url}>{item.data.title}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<div className="no-items-tip">No items yet.</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
examples: state.examples,
|
||||
};
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({ fetchRedditList }, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(RedditListPage);
|
@ -1,24 +0,0 @@
|
||||
@import '../../styles/mixins';
|
||||
|
||||
.examples-reddit-list-page {
|
||||
color: #555;
|
||||
h1 {
|
||||
margin-top: 0px;
|
||||
font-size: 28px;
|
||||
}
|
||||
a {
|
||||
color: #2db7f5;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
color: #f90;
|
||||
}
|
||||
}
|
||||
.fetch-list-error {
|
||||
display: block;
|
||||
margin-top: 20px;
|
||||
color: red;
|
||||
}
|
||||
.no-items-tip {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import * as actions from './redux/actions';
|
||||
|
||||
export class SidePanel extends Component {
|
||||
static propTypes = {
|
||||
examples: PropTypes.object.isRequired,
|
||||
actions: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="examples-side-panel">
|
||||
<ul>
|
||||
<li>
|
||||
<Link to="/examples">Welcome</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/examples/counter">Counter Demo</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/examples/reddit">Reddit API Demo</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/">Back to start page</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="memo">
|
||||
This is a Rekit feature that contains some examples for you to quick learn how Rekit works. To remove it just
|
||||
delete the feature.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
examples: state.examples,
|
||||
};
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({ ...actions }, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SidePanel);
|
@ -1,46 +0,0 @@
|
||||
@import '../../styles/mixins';
|
||||
|
||||
.examples-side-panel {
|
||||
position: fixed;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
padding: 40px;
|
||||
width: 260px;
|
||||
height: 100%;
|
||||
background-color: #f7f7f7;
|
||||
ul,
|
||||
li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
li {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #2db7f5;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
color: #f90;
|
||||
}
|
||||
}
|
||||
|
||||
.memo {
|
||||
&:before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
height: 2px;
|
||||
width: 60px;
|
||||
margin-bottom: 10px;
|
||||
background-color: #ddd;
|
||||
}
|
||||
font-size: 13px;
|
||||
margin-top: 40px;
|
||||
line-height: 150%;
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import rekitLogo from '../../images/rekit-logo.svg';
|
||||
import * as actions from './redux/actions';
|
||||
|
||||
export class WelcomePage extends Component {
|
||||
static propTypes = {
|
||||
examples: PropTypes.object.isRequired,
|
||||
actions: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="examples-welcome-page">
|
||||
<a href="http://github.com/supnate/rekit">
|
||||
<img src={rekitLogo} className="app-logo" alt="logo" />
|
||||
</a>
|
||||
<h1>Welcome to Rekit!</h1>
|
||||
<p>
|
||||
Contratulations! You have created your Rekit app successfully! Seeing this page means everything works well
|
||||
now.
|
||||
</p>
|
||||
<p>
|
||||
By default <a href="https://github.com/supnate/rekit">Rekit Studio</a> is also started at{' '}
|
||||
<a href="http://localhost:6076">http://localhost:6076</a> to manage the project.
|
||||
</p>
|
||||
<p>
|
||||
This is an example feature showing about how to layout the container, how to use Redux and React Router. If
|
||||
you want to remove all sample code, just delete the feature from Rekit Studio. Alternatively you can run
|
||||
<code>rekit remove feature examples</code> by command line under the project folder.
|
||||
</p>
|
||||
<p>
|
||||
To learn more about how to get started, you can visit:{' '}
|
||||
<a href="http://rekit.js.org/docs/get-started.html">Get started</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
examples: state.examples,
|
||||
};
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({ ...actions }, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(WelcomePage);
|
@ -1,36 +0,0 @@
|
||||
@import '../../styles/mixins';
|
||||
|
||||
.examples-welcome-page {
|
||||
line-height: 160%;
|
||||
position: relative;
|
||||
color: #555;
|
||||
|
||||
a {
|
||||
color: #2db7f5;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
color: #f90;
|
||||
}
|
||||
}
|
||||
|
||||
.app-logo {
|
||||
position: absolute;
|
||||
top: -14px;
|
||||
left: 0px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding-left: 60px;
|
||||
margin-bottom: 40px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export { default as SidePanel } from './SidePanel';
|
||||
export { default as WelcomePage } from './WelcomePage';
|
||||
export { default as CounterPage } from './CounterPage';
|
||||
export { default as RedditListPage } from './RedditListPage';
|
||||
export { default as Layout } from './Layout';
|
@ -1,4 +0,0 @@
|
||||
export { counterPlusOne } from './counterPlusOne';
|
||||
export { counterMinusOne } from './counterMinusOne';
|
||||
export { counterReset } from './counterReset';
|
||||
export { fetchRedditList, dismissFetchRedditListError } from './fetchRedditList';
|
@ -1,7 +0,0 @@
|
||||
export const EXAMPLES_COUNTER_PLUS_ONE = 'EXAMPLES_COUNTER_PLUS_ONE';
|
||||
export const EXAMPLES_COUNTER_MINUS_ONE = 'EXAMPLES_COUNTER_MINUS_ONE';
|
||||
export const EXAMPLES_COUNTER_RESET = 'EXAMPLES_COUNTER_RESET';
|
||||
export const EXAMPLES_FETCH_REDDIT_LIST_BEGIN = 'EXAMPLES_FETCH_REDDIT_LIST_BEGIN';
|
||||
export const EXAMPLES_FETCH_REDDIT_LIST_SUCCESS = 'EXAMPLES_FETCH_REDDIT_LIST_SUCCESS';
|
||||
export const EXAMPLES_FETCH_REDDIT_LIST_FAILURE = 'EXAMPLES_FETCH_REDDIT_LIST_FAILURE';
|
||||
export const EXAMPLES_FETCH_REDDIT_LIST_DISMISS_ERROR = 'EXAMPLES_FETCH_REDDIT_LIST_DISMISS_ERROR';
|
@ -1,24 +0,0 @@
|
||||
// Rekit uses a new approach to organizing actions and reducers. That is
|
||||
// putting related actions and reducers in one file. See more at:
|
||||
// https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da
|
||||
|
||||
import { EXAMPLES_COUNTER_MINUS_ONE } from './constants';
|
||||
|
||||
export function counterMinusOne() {
|
||||
return {
|
||||
type: EXAMPLES_COUNTER_MINUS_ONE,
|
||||
};
|
||||
}
|
||||
|
||||
export function reducer(state, action) {
|
||||
switch (action.type) {
|
||||
case EXAMPLES_COUNTER_MINUS_ONE:
|
||||
return {
|
||||
...state,
|
||||
count: state.count - 1,
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
// Rekit uses a new approach to organizing actions and reducers. That is
|
||||
// putting related actions and reducers in one file. See more at:
|
||||
// https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da
|
||||
|
||||
import { EXAMPLES_COUNTER_PLUS_ONE } from './constants';
|
||||
|
||||
export function counterPlusOne() {
|
||||
return {
|
||||
type: EXAMPLES_COUNTER_PLUS_ONE,
|
||||
};
|
||||
}
|
||||
|
||||
export function reducer(state, action) {
|
||||
switch (action.type) {
|
||||
case EXAMPLES_COUNTER_PLUS_ONE:
|
||||
return {
|
||||
...state,
|
||||
count: state.count + 1,
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
// Rekit uses a new approach to organizing actions and reducers. That is
|
||||
// putting related actions and reducers in one file. See more at:
|
||||
// https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da
|
||||
|
||||
import { EXAMPLES_COUNTER_RESET } from './constants';
|
||||
|
||||
export function counterReset() {
|
||||
return {
|
||||
type: EXAMPLES_COUNTER_RESET,
|
||||
};
|
||||
}
|
||||
|
||||
export function reducer(state, action) {
|
||||
switch (action.type) {
|
||||
case EXAMPLES_COUNTER_RESET:
|
||||
return {
|
||||
...state,
|
||||
count: 0,
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
import axios from 'axios';
|
||||
import {
|
||||
EXAMPLES_FETCH_REDDIT_LIST_BEGIN,
|
||||
EXAMPLES_FETCH_REDDIT_LIST_SUCCESS,
|
||||
EXAMPLES_FETCH_REDDIT_LIST_FAILURE,
|
||||
EXAMPLES_FETCH_REDDIT_LIST_DISMISS_ERROR,
|
||||
} from './constants';
|
||||
|
||||
// Rekit uses redux-thunk for async actions by default: https://github.com/gaearon/redux-thunk
|
||||
// If you prefer redux-saga, you can use rekit-plugin-redux-saga: https://github.com/supnate/rekit-plugin-redux-saga
|
||||
export function fetchRedditList(args = {}) {
|
||||
return dispatch => {
|
||||
// optionally you can have getState as the second argument
|
||||
dispatch({
|
||||
type: EXAMPLES_FETCH_REDDIT_LIST_BEGIN,
|
||||
});
|
||||
|
||||
// Return a promise so that you could control UI flow without states in the store.
|
||||
// For example: after submit a form, you need to redirect the page to another when succeeds or show some errors message if fails.
|
||||
// It's hard to use state to manage it, but returning a promise allows you to easily achieve it.
|
||||
// e.g.: handleSubmit() { this.props.actions.submitForm(data).then(()=> {}).catch(() => {}); }
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
// doRequest is a placeholder Promise. You should replace it with your own logic.
|
||||
// See the real-word example at: https://github.com/supnate/rekit/blob/master/src/features/home/redux/fetchRedditReactjsList.js
|
||||
// args.error here is only for test coverage purpose.
|
||||
const doRequest = axios.get('http://www.reddit.com/r/reactjs.json');
|
||||
|
||||
doRequest.then(
|
||||
res => {
|
||||
dispatch({
|
||||
type: EXAMPLES_FETCH_REDDIT_LIST_SUCCESS,
|
||||
data: res.data,
|
||||
});
|
||||
resolve(res);
|
||||
},
|
||||
// Use rejectHandler as the second argument so that render errors won't be caught.
|
||||
err => {
|
||||
dispatch({
|
||||
type: EXAMPLES_FETCH_REDDIT_LIST_FAILURE,
|
||||
data: { error: err },
|
||||
});
|
||||
reject(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
}
|
||||
|
||||
// Async action saves request error by default, this method is used to dismiss the error info.
|
||||
// If you don't want errors to be saved in Redux store, just ignore this method.
|
||||
export function dismissFetchRedditListError() {
|
||||
return {
|
||||
type: EXAMPLES_FETCH_REDDIT_LIST_DISMISS_ERROR,
|
||||
};
|
||||
}
|
||||
|
||||
export function reducer(state, action) {
|
||||
switch (action.type) {
|
||||
case EXAMPLES_FETCH_REDDIT_LIST_BEGIN:
|
||||
// Just after a request is sent
|
||||
return {
|
||||
...state,
|
||||
fetchRedditListPending: true,
|
||||
fetchRedditListError: null,
|
||||
};
|
||||
|
||||
case EXAMPLES_FETCH_REDDIT_LIST_SUCCESS:
|
||||
// The request is success
|
||||
return {
|
||||
...state,
|
||||
redditList: action.data.data.children,
|
||||
|
||||
fetchRedditListPending: false,
|
||||
fetchRedditListError: null,
|
||||
};
|
||||
|
||||
case EXAMPLES_FETCH_REDDIT_LIST_FAILURE:
|
||||
// The request is failed
|
||||
return {
|
||||
...state,
|
||||
fetchRedditListPending: false,
|
||||
fetchRedditListError: action.data.error,
|
||||
};
|
||||
|
||||
case EXAMPLES_FETCH_REDDIT_LIST_DISMISS_ERROR:
|
||||
// Dismiss the request failure error
|
||||
return {
|
||||
...state,
|
||||
fetchRedditListError: null,
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
// Initial state is the place you define all initial values for the Redux store of the feature.
|
||||
// In the 'standard' way, initialState is defined in reducers: http://redux.js.org/docs/basics/Reducers.html
|
||||
// But when application grows, there will be multiple reducers files, it's not intuitive what data is managed by the whole store.
|
||||
// So Rekit extracts the initial state definition into a separate module so that you can have
|
||||
// a quick view about what data is used for the feature, at any time.
|
||||
|
||||
// NOTE: initialState constant is necessary so that Rekit could auto add initial state when creating async actions.
|
||||
const initialState = {
|
||||
count: 0,
|
||||
redditList: [],
|
||||
fetchRedditListPending: false,
|
||||
fetchRedditListError: null,
|
||||
};
|
||||
|
||||
export default initialState;
|
@ -1,31 +0,0 @@
|
||||
// This is the root reducer of the feature. It is used for:
|
||||
// 1. Load reducers from each action in the feature and process them one by one.
|
||||
// Note that this part of code is mainly maintained by Rekit, you usually don't need to edit them.
|
||||
// 2. Write cross-topic reducers. If a reducer is not bound to some specific action.
|
||||
// Then it could be written here.
|
||||
// Learn more from the introduction of this approach:
|
||||
// https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da.
|
||||
|
||||
import initialState from './initialState';
|
||||
import { reducer as counterPlusOneReducer } from './counterPlusOne';
|
||||
import { reducer as counterMinusOneReducer } from './counterMinusOne';
|
||||
import { reducer as counterResetReducer } from './counterReset';
|
||||
import { reducer as fetchRedditListReducer } from './fetchRedditList';
|
||||
|
||||
const reducers = [
|
||||
counterPlusOneReducer,
|
||||
counterMinusOneReducer,
|
||||
counterResetReducer,
|
||||
fetchRedditListReducer,
|
||||
];
|
||||
|
||||
export default function reducer(state = initialState, action) {
|
||||
let newState;
|
||||
switch (action.type) {
|
||||
// Handle cross-topic actions here
|
||||
default:
|
||||
newState = state;
|
||||
break;
|
||||
}
|
||||
return reducers.reduce((s, r) => r(s, action), newState);
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
// This is the JSON way to define React Router rules in a Rekit app.
|
||||
// Learn more from: http://rekit.js.org/docs/routing.html
|
||||
|
||||
import {
|
||||
WelcomePage,
|
||||
CounterPage,
|
||||
RedditListPage,
|
||||
Layout,
|
||||
} from './';
|
||||
|
||||
export default {
|
||||
path: 'examples',
|
||||
name: 'Examples',
|
||||
component: Layout,
|
||||
childRoutes: [
|
||||
{ path: '', name: 'Welcome page', component: WelcomePage },
|
||||
{ path: 'counter', name: 'Counter page', component: CounterPage },
|
||||
{ path: 'reddit', name: 'Reddit list page', component: RedditListPage },
|
||||
],
|
||||
};
|
@ -1,6 +0,0 @@
|
||||
@import '../../styles/mixins';
|
||||
@import './SidePanel';
|
||||
@import './WelcomePage';
|
||||
@import './CounterPage';
|
||||
@import './RedditListPage';
|
||||
@import './Layout';
|
@ -2,4 +2,3 @@
|
||||
@import './global';
|
||||
@import '../features/home/style';
|
||||
@import '../features/common/style';
|
||||
@import '../features/examples/style';
|
||||
|
@ -1,35 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { CounterPage } from '../../../src/features/examples/CounterPage';
|
||||
|
||||
describe('examples/CounterPage', () => {
|
||||
it('renders node with correct class name', () => {
|
||||
const props = {
|
||||
examples: {},
|
||||
actions: {},
|
||||
};
|
||||
const renderedComponent = shallow(<CounterPage {...props} />);
|
||||
|
||||
expect(renderedComponent.find('.examples-counter-page').length).toBe(1);
|
||||
});
|
||||
|
||||
it('counter actions are called when buttons clicked', () => {
|
||||
const pageProps = {
|
||||
examples: {},
|
||||
actions: {
|
||||
counterPlusOne: jest.fn(),
|
||||
counterMinusOne: jest.fn(),
|
||||
counterReset: jest.fn(),
|
||||
},
|
||||
};
|
||||
const renderedComponent = shallow(
|
||||
<CounterPage {...pageProps} />
|
||||
);
|
||||
renderedComponent.find('.btn-plus-one').simulate('click');
|
||||
renderedComponent.find('.btn-minus-one').simulate('click');
|
||||
renderedComponent.find('.btn-reset').simulate('click');
|
||||
expect(pageProps.actions.counterPlusOne.mock.calls.length).toBe(1);
|
||||
expect(pageProps.actions.counterMinusOne.mock.calls.length).toBe(1);
|
||||
expect(pageProps.actions.counterReset.mock.calls.length).toBe(1);
|
||||
});
|
||||
});
|
@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { Layout } from '../../../src/features/examples';
|
||||
|
||||
describe('examples/Layout', () => {
|
||||
it('renders node with correct class name', () => {
|
||||
const renderedComponent = shallow(<Layout />);
|
||||
|
||||
expect(renderedComponent.find('.examples-layout').length).toBe(1);
|
||||
});
|
||||
});
|
@ -1,51 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { RedditListPage } from '../../../src/features/examples/RedditListPage';
|
||||
|
||||
describe('examples/RedditListPage', () => {
|
||||
it('renders node with correct class name', () => {
|
||||
const props = {
|
||||
examples: { redditList: [] },
|
||||
actions: {},
|
||||
};
|
||||
const renderedComponent = shallow(<RedditListPage {...props} />);
|
||||
|
||||
expect(renderedComponent.find('.examples-reddit-list-page').length).toBe(1);
|
||||
expect(renderedComponent.find('.no-items-tip').length).toBe(1);
|
||||
});
|
||||
it("renders list items when there's data", () => {
|
||||
const props = {
|
||||
examples: { redditList: [{ data: { id: 'id', title: 'title', url: 'url' } }] },
|
||||
actions: {},
|
||||
};
|
||||
const renderedComponent = shallow(<RedditListPage {...props} />);
|
||||
|
||||
expect(renderedComponent.find('.examples-reddit-list-page').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should disable fetch button when fetching reddit', () => {
|
||||
const pageProps = {
|
||||
examples: {
|
||||
redditList: [],
|
||||
fetchRedditListPending: true,
|
||||
},
|
||||
actions: {},
|
||||
};
|
||||
const renderedComponent = shallow(<RedditListPage {...pageProps} />);
|
||||
|
||||
expect(renderedComponent.find('.btn-fetch-reddit[disabled]').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should show error if fetch failed', () => {
|
||||
const pageProps = {
|
||||
examples: {
|
||||
redditList: [],
|
||||
fetchRedditListError: new Error('server error'),
|
||||
},
|
||||
actions: {},
|
||||
};
|
||||
const renderedComponent = shallow(<RedditListPage {...pageProps} />);
|
||||
|
||||
expect(renderedComponent.find('.fetch-list-error').length).toBe(1);
|
||||
});
|
||||
});
|
@ -1,15 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { SidePanel } from '../../../src/features/examples/SidePanel';
|
||||
|
||||
describe('examples/SidePanel', () => {
|
||||
it('renders node with correct class name', () => {
|
||||
const props = {
|
||||
examples: {},
|
||||
actions: {},
|
||||
};
|
||||
const renderedComponent = shallow(<SidePanel {...props} />);
|
||||
|
||||
expect(renderedComponent.find('.examples-side-panel').length).toBe(1);
|
||||
});
|
||||
});
|
@ -1,15 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { WelcomePage } from '../../../src/features/examples/WelcomePage';
|
||||
|
||||
describe('examples/WelcomePage', () => {
|
||||
it('renders node with correct class name', () => {
|
||||
const props = {
|
||||
examples: {},
|
||||
actions: {},
|
||||
};
|
||||
const renderedComponent = shallow(<WelcomePage {...props} />);
|
||||
|
||||
expect(renderedComponent.find('.examples-welcome-page').length).toBe(1);
|
||||
});
|
||||
});
|
@ -1,28 +0,0 @@
|
||||
import {
|
||||
EXAMPLES_COUNTER_MINUS_ONE,
|
||||
} from '../../../../src/features/examples/redux/constants';
|
||||
|
||||
import {
|
||||
counterMinusOne,
|
||||
reducer,
|
||||
} from '../../../../src/features/examples/redux/counterMinusOne';
|
||||
|
||||
describe('examples/redux/counterMinusOne', () => {
|
||||
it('returns correct action by counterMinusOne', () => {
|
||||
expect(counterMinusOne()).toHaveProperty('type', EXAMPLES_COUNTER_MINUS_ONE);
|
||||
});
|
||||
|
||||
it('handles action type EXAMPLES_COUNTER_MINUS_ONE correctly', () => {
|
||||
const prevState = { count: 3 };
|
||||
// TODO: use real expected state.
|
||||
const expectedState = { count: 2 };
|
||||
|
||||
const state = reducer(
|
||||
prevState,
|
||||
{ type: EXAMPLES_COUNTER_MINUS_ONE }
|
||||
);
|
||||
// Should be immutable
|
||||
expect(state).not.toBe(prevState);
|
||||
expect(state).toEqual(expectedState);
|
||||
});
|
||||
});
|
@ -1,25 +0,0 @@
|
||||
import {
|
||||
EXAMPLES_COUNTER_PLUS_ONE,
|
||||
} from '../../../../src/features/examples/redux/constants';
|
||||
|
||||
import {
|
||||
counterPlusOne,
|
||||
reducer,
|
||||
} from '../../../../src/features/examples/redux/counterPlusOne';
|
||||
|
||||
describe('examples/redux/counterPlusOne', () => {
|
||||
it('returns correct action by counterPlusOne', () => {
|
||||
expect(counterPlusOne()).toHaveProperty('type', EXAMPLES_COUNTER_PLUS_ONE);
|
||||
});
|
||||
|
||||
it('handles action type EXAMPLES_COUNTER_PLUS_ONE correctly', () => {
|
||||
const prevState = { count: 0 };
|
||||
const expectedState = { count: 1 };
|
||||
const state = reducer(
|
||||
prevState,
|
||||
{ type: EXAMPLES_COUNTER_PLUS_ONE }
|
||||
);
|
||||
expect(state).not.toBe(prevState); // should be immutable
|
||||
expect(state).toEqual(expectedState); // TODO: replace this line with real case.
|
||||
});
|
||||
});
|
@ -1,25 +0,0 @@
|
||||
import {
|
||||
EXAMPLES_COUNTER_RESET,
|
||||
} from '../../../../src/features/examples/redux/constants';
|
||||
|
||||
import {
|
||||
counterReset,
|
||||
reducer,
|
||||
} from '../../../../src/features/examples/redux/counterReset';
|
||||
|
||||
describe('examples/redux/counterReset', () => {
|
||||
it('returns correct action by counterReset', () => {
|
||||
expect(counterReset()).toHaveProperty('type', EXAMPLES_COUNTER_RESET);
|
||||
});
|
||||
|
||||
it('handles action type EXAMPLES_COUNTER_RESET correctly', () => {
|
||||
const prevState = { count: 10 };
|
||||
const expectedState = { count: 0 };
|
||||
const state = reducer(
|
||||
prevState,
|
||||
{ type: EXAMPLES_COUNTER_RESET }
|
||||
);
|
||||
expect(state).not.toBe(prevState); // should be immutable
|
||||
expect(state).toEqual(expectedState); // TODO: replace this line with real case.
|
||||
});
|
||||
});
|
@ -1,102 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import nock from 'nock';
|
||||
|
||||
import {
|
||||
EXAMPLES_FETCH_REDDIT_LIST_BEGIN,
|
||||
EXAMPLES_FETCH_REDDIT_LIST_SUCCESS,
|
||||
EXAMPLES_FETCH_REDDIT_LIST_FAILURE,
|
||||
EXAMPLES_FETCH_REDDIT_LIST_DISMISS_ERROR,
|
||||
} from '../../../../src/features/examples/redux/constants';
|
||||
|
||||
import {
|
||||
fetchRedditList,
|
||||
dismissFetchRedditListError,
|
||||
reducer,
|
||||
} from '../../../../src/features/examples/redux/fetchRedditList';
|
||||
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureMockStore(middlewares);
|
||||
|
||||
describe('examples/redux/fetchRedditList', () => {
|
||||
afterEach(() => {
|
||||
nock.cleanAll();
|
||||
});
|
||||
|
||||
it('dispatches success action when fetchRedditList succeeds', () => {
|
||||
const list = _.times(2, i => ({
|
||||
data: {
|
||||
id: `id${i}`,
|
||||
title: `test${i}`,
|
||||
url: `http://example.com/test${i}`,
|
||||
},
|
||||
}));
|
||||
nock('http://www.reddit.com/')
|
||||
.get('/r/reactjs.json')
|
||||
.reply(200, { data: { children: list } });
|
||||
const store = mockStore({ redditReactjsList: [] });
|
||||
|
||||
return store.dispatch(fetchRedditList()).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0]).toHaveProperty('type', EXAMPLES_FETCH_REDDIT_LIST_BEGIN);
|
||||
expect(actions[1]).toHaveProperty('type', EXAMPLES_FETCH_REDDIT_LIST_SUCCESS);
|
||||
});
|
||||
});
|
||||
|
||||
it('dispatches failure action when fetchRedditList fails', () => {
|
||||
nock('http://www.reddit.com/')
|
||||
.get('/r/reactjs.json')
|
||||
.reply(500, null);
|
||||
const store = mockStore({ redditReactjsList: [] });
|
||||
|
||||
return store.dispatch(fetchRedditList({ error: true })).catch(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0]).toHaveProperty('type', EXAMPLES_FETCH_REDDIT_LIST_BEGIN);
|
||||
expect(actions[1]).toHaveProperty('type', EXAMPLES_FETCH_REDDIT_LIST_FAILURE);
|
||||
expect(actions[1]).toHaveProperty('data.error', expect.anything());
|
||||
});
|
||||
});
|
||||
|
||||
it('returns correct action by dismissFetchRedditListError', () => {
|
||||
const expectedAction = {
|
||||
type: EXAMPLES_FETCH_REDDIT_LIST_DISMISS_ERROR,
|
||||
};
|
||||
expect(dismissFetchRedditListError()).toEqual(expectedAction);
|
||||
});
|
||||
|
||||
it('handles action type EXAMPLES_FETCH_REDDIT_LIST_BEGIN correctly', () => {
|
||||
const prevState = { fetchRedditListPending: false };
|
||||
const state = reducer(prevState, { type: EXAMPLES_FETCH_REDDIT_LIST_BEGIN });
|
||||
expect(state).not.toBe(prevState); // should be immutable
|
||||
expect(state.fetchRedditListPending).toBe(true);
|
||||
});
|
||||
|
||||
it('handles action type EXAMPLES_FETCH_REDDIT_LIST_SUCCESS correctly', () => {
|
||||
const prevState = { fetchRedditListPending: true };
|
||||
const state = reducer(prevState, {
|
||||
type: EXAMPLES_FETCH_REDDIT_LIST_SUCCESS,
|
||||
data: { data: { children: [] } },
|
||||
});
|
||||
expect(state).not.toBe(prevState); // should be immutable
|
||||
expect(state.fetchRedditListPending).toBe(false);
|
||||
});
|
||||
|
||||
it('handles action type EXAMPLES_FETCH_REDDIT_LIST_FAILURE correctly', () => {
|
||||
const prevState = { fetchRedditListPending: true };
|
||||
const state = reducer(prevState, {
|
||||
type: EXAMPLES_FETCH_REDDIT_LIST_FAILURE,
|
||||
data: { error: new Error('some error') },
|
||||
});
|
||||
expect(state).not.toBe(prevState); // should be immutable
|
||||
expect(state.fetchRedditListPending).toBe(false);
|
||||
expect(state.fetchRedditListError).toEqual(expect.anything());
|
||||
});
|
||||
|
||||
it('handles action type EXAMPLES_FETCH_REDDIT_LIST_DISMISS_ERROR correctly', () => {
|
||||
const prevState = { fetchRedditListError: new Error('some error') };
|
||||
const state = reducer(prevState, { type: EXAMPLES_FETCH_REDDIT_LIST_DISMISS_ERROR });
|
||||
expect(state).not.toBe(prevState); // should be immutable
|
||||
expect(state.fetchRedditListError).toBe(null);
|
||||
});
|
||||
});
|
@ -1,14 +0,0 @@
|
||||
import reducer from '../../../../src/features/examples/redux/reducer';
|
||||
|
||||
describe('examples/redux/reducer', () => {
|
||||
it('does nothing if no matched action', () => {
|
||||
const prevState = {};
|
||||
const state = reducer(
|
||||
prevState,
|
||||
{ type: '__UNKNOWN_ACTION_TYPE__' }
|
||||
);
|
||||
expect(state).toEqual(prevState);
|
||||
});
|
||||
|
||||
// TODO: add global reducer test if needed.
|
||||
});
|
Loading…
Reference in New Issue
Block a user