This is an automated email from the ASF dual-hosted git repository.
garren pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb-fauxton.git
The following commit(s) were added to refs/heads/master by this push:
new 2f8de33 Update documents/designdocinfo to use redux (#1135)
2f8de33 is described below
commit 2f8de334c4251dc24985f21f86201dab914811ba
Author: Antonio Maranhao <[email protected]>
AuthorDate: Wed Oct 10 05:34:29 2018 -0400
Update documents/designdocinfo to use redux (#1135)
* Split components into separate files
* Use redux
* Updated tests
---
.../__tests__/designdocinfo-action.test.js | 18 ++---
app/addons/documents/base.js | 4 +-
app/addons/documents/designdocinfo/actions.js | 69 ++++++++++--------
.../{components.js => components/DesignDocInfo.js} | 69 +++++++++---------
.../components/DesignDocInfoContainer.js | 31 ++++++++
app/addons/documents/designdocinfo/reducers.js | 47 +++++++++++++
app/addons/documents/designdocinfo/stores.js | 82 ----------------------
app/addons/documents/layouts.js | 36 ++++++----
app/addons/documents/routes-documents.js | 8 +--
9 files changed, 188 insertions(+), 176 deletions(-)
diff --git a/app/addons/documents/__tests__/designdocinfo-action.test.js
b/app/addons/documents/__tests__/designdocinfo-action.test.js
index cb64564..f94df9f 100644
--- a/app/addons/documents/__tests__/designdocinfo-action.test.js
+++ b/app/addons/documents/__tests__/designdocinfo-action.test.js
@@ -10,10 +10,11 @@
// License for the specific language governing permissions and limitations
under
// the License.
-import FauxtonAPI from "../../../core/api";
-import Actions from "../designdocinfo/actions";
-import testUtils from "../../../../test/mocha/testUtils";
-import sinon from "sinon";
+import sinon from 'sinon';
+import FauxtonAPI from '../../../core/api';
+import testUtils from '../../../../test/mocha/testUtils';
+import Actions from '../designdocinfo/actions';
+
const {assert, restore} = testUtils;
describe('DesignDocInfo Actions', () => {
@@ -21,10 +22,10 @@ describe('DesignDocInfo Actions', () => {
describe('fetchDesignDocInfo', () => {
afterEach(() => {
- restore(Actions.monitorDesignDoc);
+ restore(window.setInterval);
});
- it('calls monitorDesignDoc on successful fetch', () => {
+ it('schedules regular updates on successful fetch', () => {
const promise = FauxtonAPI.Deferred();
promise.resolve();
const fakeDesignDocInfo = {
@@ -33,12 +34,13 @@ describe('DesignDocInfo Actions', () => {
}
};
- const spy = sinon.spy(Actions, 'monitorDesignDoc');
+ const spy = sinon.spy(window, 'setInterval');
+ const dispatch = sinon.stub();
return Actions.fetchDesignDocInfo({
ddocName: 'test-designdoc-info',
designDocInfo: fakeDesignDocInfo
- }).then(() => {
+ })(dispatch).then(() => {
assert.ok(spy.calledOnce);
});
});
diff --git a/app/addons/documents/base.js b/app/addons/documents/base.js
index 6809d5f..090a450 100644
--- a/app/addons/documents/base.js
+++ b/app/addons/documents/base.js
@@ -14,6 +14,7 @@ import app from "../../app";
import Helpers from "../../helpers";
import FauxtonAPI from "../../core/api";
import Documents from "./routes";
+import designDocInfoReducers from "./designdocinfo/reducers";
import reducers from "./index-results/reducers";
import mangoReducers from "./mango/mango.reducers";
import sidebarReducers from "./sidebar/reducers";
@@ -26,7 +27,8 @@ FauxtonAPI.addReducers({
mangoQuery: mangoReducers,
sidebar: sidebarReducers,
revisionBrowser: revisionBrowserReducers,
- partitionKey: partitionKeyReducers
+ partitionKey: partitionKeyReducers,
+ designDocInfo: designDocInfoReducers
});
function getQueryParam (query) {
diff --git a/app/addons/documents/designdocinfo/actions.js
b/app/addons/documents/designdocinfo/actions.js
index dc3920c..79004e6 100644
--- a/app/addons/documents/designdocinfo/actions.js
+++ b/app/addons/documents/designdocinfo/actions.js
@@ -10,42 +10,51 @@
// License for the specific language governing permissions and limitations
under
// the License.
-import FauxtonAPI from "../../../core/api";
import ActionTypes from "./actiontypes";
-import Stores from "./stores";
-var store = Stores.designDocInfoStore;
-export default {
- fetchDesignDocInfo ({ddocName, designDocInfo}) {
- FauxtonAPI.dispatch({
- type: ActionTypes.DESIGN_FETCHING
- });
+const fetchDesignDocInfo = ({designDocName, designDocInfo}) => (dispatch) => {
+ dispatch({
+ type: ActionTypes.DESIGN_FETCHING
+ });
- return designDocInfo.fetch().then(() => {
- this.monitorDesignDoc({
- ddocName,
- designDocInfo
- });
- });
- },
+ return designDocInfo.fetch().then(() => {
+ monitorDesignDoc({
+ designDocName,
+ designDocInfo
+ }, dispatch);
+ });
+};
- monitorDesignDoc (options) {
- options.intervalId = window.setInterval(_.bind(this.refresh, this), 5000);
- FauxtonAPI.dispatch({
- type: ActionTypes.DESIGN_DOC_MONITOR,
- options: options
- });
- },
+let intervalId;
+const monitorDesignDoc = (options, dispatch) => {
+ const refreshDDoc = () => {
+ refresh(options.designDocInfo, dispatch);
+ };
+ stopRefresh();
+ intervalId = window.setInterval(refreshDDoc, 5000);
+ dispatch({
+ type: ActionTypes.DESIGN_DOC_MONITOR,
+ options: options
+ });
+};
- refresh () {
- store.getDesignDocInfo().fetch().then(() => {
- FauxtonAPI.dispatch({
- type: ActionTypes.DESIGN_REFRESH
- });
+const refresh = (designDocInfo, dispatch) => {
+ designDocInfo.fetch().then(() => {
+ dispatch({
+ type: ActionTypes.DESIGN_REFRESH,
+ designDocInfo
});
- },
+ });
+};
- stopRefresh () {
- window.clearInterval(store.getIntervalId());
+const stopRefresh = () => {
+ if (intervalId) {
+ window.clearInterval(intervalId);
+ intervalId = undefined;
}
};
+
+export default {
+ fetchDesignDocInfo,
+ stopRefresh
+};
diff --git a/app/addons/documents/designdocinfo/components.js
b/app/addons/documents/designdocinfo/components/DesignDocInfo.js
similarity index 69%
rename from app/addons/documents/designdocinfo/components.js
rename to app/addons/documents/designdocinfo/components/DesignDocInfo.js
index 726bc93..70d6050 100644
--- a/app/addons/documents/designdocinfo/components.js
+++ b/app/addons/documents/designdocinfo/components/DesignDocInfo.js
@@ -10,38 +10,38 @@
// License for the specific language governing permissions and limitations
under
// the License.
-import FauxtonAPI from "../../../core/api";
-import React from "react";
-import Stores from "./stores";
-import Actions from "./actions";
-import ReactComponents from "../../components/react-components";
-var designDocInfoStore = Stores.designDocInfoStore;
-var LoadLines = ReactComponents.LoadLines;
-var Copy = ReactComponents.Copy;
+import PropTypes from 'prop-types';
+import React from 'react';
import uuid from 'uuid';
+import FauxtonAPI from '../../../../core/api';
+import ReactComponents from '../../../components/react-components';
+const LoadLines = ReactComponents.LoadLines;
+const Copy = ReactComponents.Copy;
-class DesignDocInfo extends React.Component {
- getStoreState = () => {
- return {
- viewIndex: designDocInfoStore.getViewIndex(),
- isLoading: designDocInfoStore.isLoading(),
- ddocName: designDocInfoStore.getDdocName()
- };
- };
+export default class DesignDocInfo extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.fetchDDocInfo();
+ }
- componentDidMount() {
- designDocInfoStore.on('change', this.onChange, this);
+ componentDidUpdate(prevProps) {
+ if (this.props.designDocInfo !== prevProps.designDocInfo) {
+ this.fetchDDocInfo();
+ }
}
componentWillUnmount() {
- designDocInfoStore.off('change', this.onChange);
- Actions.stopRefresh();
+ this.props.stopRefresh();
}
- onChange = () => {
- this.setState(this.getStoreState());
- };
+ fetchDDocInfo() {
+ this.props.fetchDesignDocInfo({
+ designDocName: this.props.designDocName,
+ designDocInfo: this.props.designDocInfo
+ });
+ }
showCopiedMessage = () => {
FauxtonAPI.addNotification({
@@ -51,22 +51,18 @@ class DesignDocInfo extends React.Component {
});
};
- state = this.getStoreState();
-
render() {
- var viewIndex = this.state.viewIndex;
-
- if (this.state.isLoading) {
+ if (this.props.isLoading) {
return <LoadLines />;
}
-
- var actualSize = (viewIndex.data_size) ?
viewIndex.data_size.toLocaleString('en') : 0;
- var dataSize = (viewIndex.disk_size) ?
viewIndex.disk_size.toLocaleString('en') : 0;
+ const viewIndex = this.props.viewIndex;
+ const actualSize = (viewIndex.data_size) ?
viewIndex.data_size.toLocaleString('en') : 0;
+ const dataSize = (viewIndex.disk_size) ?
viewIndex.disk_size.toLocaleString('en') : 0;
return (
<div className="metadata-page">
<header>
- <h2>_design/{this.state.ddocName} Metadata</h2>
+ <h2>_design/{this.props.designDocName} Metadata</h2>
</header>
<section className="container">
@@ -131,7 +127,10 @@ class DesignDocInfo extends React.Component {
}
}
-
-export default {
- DesignDocInfo: DesignDocInfo
+DesignDocInfo.propTypes = {
+ isLoading: PropTypes.bool.isRequired,
+ viewIndex: PropTypes.object,
+ designDocName: PropTypes.string.isRequired,
+ stopRefresh: PropTypes.func.isRequired,
+ fetchDesignDocInfo: PropTypes.func.isRequired
};
diff --git
a/app/addons/documents/designdocinfo/components/DesignDocInfoContainer.js
b/app/addons/documents/designdocinfo/components/DesignDocInfoContainer.js
new file mode 100644
index 0000000..2984b86
--- /dev/null
+++ b/app/addons/documents/designdocinfo/components/DesignDocInfoContainer.js
@@ -0,0 +1,31 @@
+import { connect } from 'react-redux';
+import DesignDocInfo from './DesignDocInfo';
+import Actions from '../actions';
+
+const mapStateToProps = ({ designDocInfo }, ownProps) => {
+ return {
+ isLoading: designDocInfo.isLoading,
+ viewIndex: designDocInfo.viewIndex,
+ designDocInfo: ownProps.designDocInfo,
+ designDocName: ownProps.designDocName
+ };
+};
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ fetchDesignDocInfo: (options) => {
+ dispatch(Actions.fetchDesignDocInfo(options));
+ },
+
+ stopRefresh: () => {
+ Actions.stopRefresh();
+ }
+ };
+};
+
+const DesignDocInfoContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(DesignDocInfo);
+
+export default DesignDocInfoContainer;
diff --git a/app/addons/documents/designdocinfo/reducers.js
b/app/addons/documents/designdocinfo/reducers.js
new file mode 100644
index 0000000..9b62b91
--- /dev/null
+++ b/app/addons/documents/designdocinfo/reducers.js
@@ -0,0 +1,47 @@
+// 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.
+
+import ActionTypes from './actiontypes';
+
+const initialState = {
+ isLoading: true,
+ viewIndex: undefined
+};
+
+export default function designDocInfo (state = initialState, action) {
+ const { options } = action;
+
+ switch (action.type) {
+
+ case ActionTypes.DESIGN_FETCHING:
+ return {
+ ...state,
+ isLoading: true
+ };
+
+ case ActionTypes.DESIGN_DOC_MONITOR:
+ return {
+ ...state,
+ isLoading: false,
+ viewIndex: options.designDocInfo.get('view_index')
+ };
+
+ case ActionTypes.DESIGN_DOC_REFRESH:
+ return {
+ ...state,
+ viewIndex: options.designDocInfo.get('view_index')
+ };
+
+ default:
+ return state;
+ }
+}
diff --git a/app/addons/documents/designdocinfo/stores.js
b/app/addons/documents/designdocinfo/stores.js
deleted file mode 100644
index d07f11d..0000000
--- a/app/addons/documents/designdocinfo/stores.js
+++ /dev/null
@@ -1,82 +0,0 @@
-// 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.
-
-import FauxtonAPI from "../../../core/api";
-import ActionTypes from "./actiontypes";
-var Stores = {};
-
-Stores.DesignDocInfoStore = FauxtonAPI.Store.extend({
-
- initialize: function () {
- this._isLoading = true;
- },
-
- isLoading: function () {
- return this._isLoading;
- },
-
- getDdocName: function () {
- return this._ddocName;
- },
-
- getDesignDocInfo: function () {
- return this._designDocInfo;
- },
-
- monitorDesignDoc: function (options) {
- this._isLoading = false;
- this._designDocInfo = options.designDocInfo;
- this._ddocName = options.ddocName;
- this._intervalId = options.intervalId;
- },
-
- getIntervalId: function () {
- return this._intervalId;
- },
-
- getViewIndex: function () {
- if (this._isLoading) {
- return {};
- }
-
- return this._designDocInfo.get('view_index');
- },
-
- dispatch: function (action) {
- switch (action.type) {
- case ActionTypes.DESIGN_FETCHING:
- this._isLoading = true;
- this.triggerChange();
- break;
-
- case ActionTypes.DESIGN_DOC_MONITOR:
- this.monitorDesignDoc(action.options);
- this.triggerChange();
- break;
-
- case ActionTypes.DESIGN_DOC_REFRESH:
- this.triggerChange();
- break;
-
- default:
- return;
- // do nothing
- }
- }
-
-});
-
-Stores.designDocInfoStore = new Stores.DesignDocInfoStore();
-
-Stores.designDocInfoStore.dispatchToken =
FauxtonAPI.dispatcher.register(Stores.designDocInfoStore.dispatch.bind(Stores.designDocInfoStore));
-
-export default Stores;
diff --git a/app/addons/documents/layouts.js b/app/addons/documents/layouts.js
index bd6f680..bb11d66 100644
--- a/app/addons/documents/layouts.js
+++ b/app/addons/documents/layouts.js
@@ -18,7 +18,7 @@ import SidebarControllerContainer from
"./sidebar/SidebarControllerContainer";
import HeaderDocsLeft from './components/header-docs-left';
import Changes from './changes/components';
import IndexEditorComponents from "./index-editor/components";
-import DesignDocInfoComponents from './designdocinfo/components';
+import DesignDocInfoContainer from
'./designdocinfo/components/DesignDocInfoContainer';
import RightAllDocsHeader from './components/header-docs-right';
import IndexResultsContainer from
'./index-results/containers/IndexResultsContainer';
import PaginationContainer from
'./index-results/containers/PaginationContainer';
@@ -47,13 +47,16 @@ export const TabsSidebarHeader = ({
onGlobalModeSelected,
globalMode
}) => {
- const partKeySelector = (<PartitionKeySelectorContainer
- databaseName={dbName}
- partitionKey={partitionKey}
- onPartitionKeySelected={onPartitionKeySelected}
- onGlobalModeSelected={onGlobalModeSelected}
- globalMode={globalMode}/>
- );
+ let partKeySelector = null;
+ if (showPartitionKeySelector) {
+ partKeySelector = (<PartitionKeySelectorContainer
+ databaseName={dbName}
+ partitionKey={partitionKey}
+ onPartitionKeySelected={onPartitionKeySelected}
+ onGlobalModeSelected={onGlobalModeSelected}
+ globalMode={globalMode}/>
+ );
+ }
return (
<header className="two-panel-header">
@@ -66,7 +69,7 @@ export const TabsSidebarHeader = ({
</div>
<div className="right-header-wrapper flex-layout flex-row flex-body">
<div style={{flex:1, padding: '18px 6px 12px 12px'}}>
- {showPartitionKeySelector ? partKeySelector : null}
+ {partKeySelector}
</div>
<div id="right-header" className="flex-fill">
<RightAllDocsHeader
@@ -99,9 +102,9 @@ TabsSidebarHeader.propTypes = {
database: PropTypes.object.isRequired,
queryDocs: PropTypes.func,
selectedNavItem: PropTypes.object,
- showPartitionKeySelector: PropTypes.bool,
+ showPartitionKeySelector: PropTypes.bool.isRequired,
partitionKey: PropTypes.string,
- onPartitionKeySelected: PropTypes.func.isRequired,
+ onPartitionKeySelected: PropTypes.func,
onGlobalModeSelected: PropTypes.bool,
globalMode: PropTypes.bool
};
@@ -235,9 +238,13 @@ export const ChangesSidebarLayout = ({ docURL, database,
endpoint, dbName, dropD
};
export const ViewsTabsSidebarLayout = ({showEditView, database, docURL,
endpoint,
- dbName, dropDownLinks, selectedNavItem }) => {
+ dbName, dropDownLinks, selectedNavItem, designDocInfo }) => {
- const content = showEditView ? <IndexEditorComponents.EditorController /> :
<DesignDocInfoComponents.DesignDocInfo />;
+ const content = showEditView ?
+ <IndexEditorComponents.EditorController /> :
+ <DesignDocInfoContainer
+ designDocInfo={designDocInfo}
+ designDocName={selectedNavItem.designDocName}/>;
return (
<div id="dashboard" className="with-sidebar">
<TabsSidebarHeader
@@ -268,5 +275,6 @@ ViewsTabsSidebarLayout.propTypes = {
docURL: PropTypes.string.isRequired,
endpoint: PropTypes.string,
dbName: PropTypes.string.isRequired,
- dropDownLinks: PropTypes.array.isRequired
+ dropDownLinks: PropTypes.array.isRequired,
+ designDocInfo: PropTypes.object
};
diff --git a/app/addons/documents/routes-documents.js
b/app/addons/documents/routes-documents.js
index 41c7493..e61fab7 100644
--- a/app/addons/documents/routes-documents.js
+++ b/app/addons/documents/routes-documents.js
@@ -17,7 +17,6 @@ import ChangesActions from './changes/actions';
import Databases from '../databases/base';
import Resources from './resources';
import {SidebarItemSelection} from './sidebar/helpers';
-import DesignDocInfoActions from './designdocinfo/actions';
import ComponentsActions from '../components/actions';
import {DocsTabsSidebarLayout, ViewsTabsSidebarLayout, ChangesSidebarLayout}
from './layouts';
@@ -52,11 +51,7 @@ var DocumentsRouteObject = BaseRoute.extend({
},
designDocMetadata: function (database, ddoc) {
- var designDocInfo = new Resources.DdocInfo({ _id: "_design/" + ddoc }, {
database: this.database });
- DesignDocInfoActions.fetchDesignDocInfo({
- ddocName: ddoc,
- designDocInfo: designDocInfo
- });
+ const designDocInfo = new Resources.DdocInfo({ _id: "_design/" + ddoc }, {
database: this.database });
const selectedNavItem = new SidebarItemSelection('designDoc', {
designDocName: ddoc,
designDocSection: 'metadata'
@@ -71,6 +66,7 @@ var DocumentsRouteObject = BaseRoute.extend({
dropDownLinks={dropDownLinks}
database={this.database}
selectedNavItem={selectedNavItem}
+ designDocInfo={designDocInfo}
/>;
},