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 { routerReducer } from 'react-router-redux';
|
||||||
import homeReducer from '../features/home/redux/reducer';
|
import homeReducer from '../features/home/redux/reducer';
|
||||||
import commonReducer from '../features/common/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.
|
// 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.
|
// This is used for Rekit cmds to register new features, remove features, etc.
|
||||||
@ -13,7 +12,6 @@ const reducerMap = {
|
|||||||
router: routerReducer,
|
router: routerReducer,
|
||||||
home: homeReducer,
|
home: homeReducer,
|
||||||
common: commonReducer,
|
common: commonReducer,
|
||||||
examples: examplesReducer,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default combineReducers(reducerMap);
|
export default combineReducers(reducerMap);
|
||||||
|
@ -2,7 +2,6 @@ import { App } from '../features/home';
|
|||||||
import { PageNotFound } from '../features/common';
|
import { PageNotFound } from '../features/common';
|
||||||
import homeRoute from '../features/home/route';
|
import homeRoute from '../features/home/route';
|
||||||
import commonRoute from '../features/common/route';
|
import commonRoute from '../features/common/route';
|
||||||
import examplesRoute from '../features/examples/route';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
// NOTE: DO NOT CHANGE the 'childRoutes' name and the declaration pattern.
|
// NOTE: DO NOT CHANGE the 'childRoutes' name and the declaration pattern.
|
||||||
@ -10,7 +9,6 @@ import _ from 'lodash';
|
|||||||
const childRoutes = [
|
const childRoutes = [
|
||||||
homeRoute,
|
homeRoute,
|
||||||
commonRoute,
|
commonRoute,
|
||||||
examplesRoute,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const routes = [{
|
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 './global';
|
||||||
@import '../features/home/style';
|
@import '../features/home/style';
|
||||||
@import '../features/common/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