diff --git a/src/common/rootReducer.js b/src/common/rootReducer.js index fc71e73..ddc73c2 100644 --- a/src/common/rootReducer.js +++ b/src/common/rootReducer.js @@ -2,6 +2,7 @@ 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 userAdminReducer from '../features/user-admin/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. @@ -12,6 +13,7 @@ const reducerMap = { router: routerReducer, home: homeReducer, common: commonReducer, + userAdmin: userAdminReducer, }; export default combineReducers(reducerMap); diff --git a/src/common/routeConfig.js b/src/common/routeConfig.js index 9f3a5c5..ed85646 100644 --- a/src/common/routeConfig.js +++ b/src/common/routeConfig.js @@ -1,14 +1,16 @@ import { App } from '../features/home'; import { PageNotFound } from '../features/common'; -import homeRoute from '../features/home/route'; -import commonRoute from '../features/common/route'; import _ from 'lodash'; +import commonRoute from '../features/common/route'; +import homeRoute from '../features/home/route'; +import userAdminRoute from '../features/user-admin/route'; // NOTE: DO NOT CHANGE the 'childRoutes' name and the declaration pattern. // This is used for Rekit cmds to register routes config for new features, and remove config when remove features, etc. const childRoutes = [ homeRoute, commonRoute, + userAdminRoute, ]; const routes = [{ diff --git a/src/features/user-admin/DefaultPage.js b/src/features/user-admin/DefaultPage.js new file mode 100644 index 0000000..cdfb83c --- /dev/null +++ b/src/features/user-admin/DefaultPage.js @@ -0,0 +1,39 @@ +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 DefaultPage extends Component { + static propTypes = { + userAdmin: PropTypes.object.isRequired, + actions: PropTypes.object.isRequired, + }; + + render() { + return ( +
+ Page Content: user-admin/DefaultPage +
+ ); + } +} + +/* istanbul ignore next */ +function mapStateToProps(state) { + return { + userAdmin: state.userAdmin, + }; +} + +/* istanbul ignore next */ +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ ...actions }, dispatch) + }; +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(DefaultPage); diff --git a/src/features/user-admin/DefaultPage.scss b/src/features/user-admin/DefaultPage.scss new file mode 100644 index 0000000..8bcdafb --- /dev/null +++ b/src/features/user-admin/DefaultPage.scss @@ -0,0 +1,5 @@ +@import '../../styles/mixins'; + +.user-admin-default-page { + +} diff --git a/src/features/user-admin/index.js b/src/features/user-admin/index.js new file mode 100644 index 0000000..33522c8 --- /dev/null +++ b/src/features/user-admin/index.js @@ -0,0 +1 @@ +export { default as DefaultPage } from './DefaultPage'; diff --git a/src/features/user-admin/redux/actions.js b/src/features/user-admin/redux/actions.js new file mode 100644 index 0000000..e69de29 diff --git a/src/features/user-admin/redux/constants.js b/src/features/user-admin/redux/constants.js new file mode 100644 index 0000000..e69de29 diff --git a/src/features/user-admin/redux/initialState.js b/src/features/user-admin/redux/initialState.js new file mode 100644 index 0000000..9570e38 --- /dev/null +++ b/src/features/user-admin/redux/initialState.js @@ -0,0 +1,11 @@ +// 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 = { +}; + +export default initialState; diff --git a/src/features/user-admin/redux/reducer.js b/src/features/user-admin/redux/reducer.js new file mode 100644 index 0000000..c9b022b --- /dev/null +++ b/src/features/user-admin/redux/reducer.js @@ -0,0 +1,23 @@ +// 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'; + +const reducers = [ +]; + +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); +} diff --git a/src/features/user-admin/route.js b/src/features/user-admin/route.js new file mode 100644 index 0000000..cfeeb7c --- /dev/null +++ b/src/features/user-admin/route.js @@ -0,0 +1,14 @@ +// 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 { + DefaultPage, +} from './'; + +export default { + path: 'user-admin', + name: 'User admin', + childRoutes: [ + { path: 'default-page', name: 'Default page', component: DefaultPage, isIndex: true }, + ], +}; diff --git a/src/features/user-admin/style.scss b/src/features/user-admin/style.scss new file mode 100644 index 0000000..743e0a3 --- /dev/null +++ b/src/features/user-admin/style.scss @@ -0,0 +1,2 @@ +@import '../../styles/mixins'; +@import './DefaultPage'; diff --git a/src/styles/index.scss b/src/styles/index.scss index 451a46d..fd4a49a 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -2,3 +2,4 @@ @import './global'; @import '../features/home/style'; @import '../features/common/style'; +@import '../features/user-admin/style'; diff --git a/tests/features/user-admin/DefaultPage.test.js b/tests/features/user-admin/DefaultPage.test.js new file mode 100644 index 0000000..1bd50f7 --- /dev/null +++ b/tests/features/user-admin/DefaultPage.test.js @@ -0,0 +1,19 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { DefaultPage } from '../../../src/features/user-admin/DefaultPage'; + +describe('user-admin/DefaultPage', () => { + it('renders node with correct class name', () => { + const props = { + userAdmin: {}, + actions: {}, + }; + const renderedComponent = shallow( + + ); + + expect( + renderedComponent.find('.user-admin-default-page').length + ).toBe(1); + }); +}); diff --git a/tests/features/user-admin/redux/reducer.test.js b/tests/features/user-admin/redux/reducer.test.js new file mode 100644 index 0000000..5c40f67 --- /dev/null +++ b/tests/features/user-admin/redux/reducer.test.js @@ -0,0 +1,14 @@ +import reducer from '../../../../src/features/user-admin/redux/reducer'; + +describe('user-admin/redux/reducer', () => { + it('does nothing if no matched action', () => { + const prevState = {}; + const state = reducer( + prevState, + { type: '__UNKNOWN_ACTION_TYPE__' } + ); + expect(state).toBe(prevState); + }); + + // TODO: add global reducer test if needed. +});