diff --git a/src/actions/loading.js b/src/actions/loading.js new file mode 100644 index 000000000..6a948d34b --- /dev/null +++ b/src/actions/loading.js @@ -0,0 +1,19 @@ +import actionTypes from '../constants/actions'; + +/** + * An action to dispatch loadingStarted + * + */ +export const loadingStarted = data => ({ + data, + type: actionTypes.loadingStarted, +}); + +/** + * An action to dispatch loadingFinished + * + */ +export const loadingFinished = data => ({ + data, + type: actionTypes.loadingFinished, +}); diff --git a/src/actions/loding.test.js b/src/actions/loding.test.js new file mode 100644 index 000000000..ab9fa12ae --- /dev/null +++ b/src/actions/loding.test.js @@ -0,0 +1,33 @@ +import { expect } from 'chai'; +import actionTypes from '../constants/actions'; +import { + loadingStarted, + loadingFinished, +} from './loading'; + + +describe('actions: loading', () => { + describe('loadingStarted', () => { + it('should create an action to show loading bar', () => { + const data = 'test'; + + const expectedAction = { + data, + type: actionTypes.loadingStarted, + }; + expect(loadingStarted(data)).to.be.deep.equal(expectedAction); + }); + }); + + describe('loadingFinished', () => { + it('should create an action to hide loading bar', () => { + const data = 'test'; + + const expectedAction = { + data, + type: actionTypes.loadingFinished, + }; + expect(loadingFinished(data)).to.be.deep.equal(expectedAction); + }); + }); +}); diff --git a/src/components/app/index.js b/src/components/app/index.js index 686b95904..b22952cad 100644 --- a/src/components/app/index.js +++ b/src/components/app/index.js @@ -11,7 +11,7 @@ import styles from './app.css'; import Metronome from '../../utils/metronome'; import Dialog from '../dialog'; import Tabs from '../tabs'; - // temporary, will be deleted with #347 +import LoadingBar from '../loadingBar'; // start dispatching sync ticks const metronome = new Metronome(); @@ -33,6 +33,7 @@ const App = () => ( + ); diff --git a/src/components/loadingBar/index.js b/src/components/loadingBar/index.js new file mode 100644 index 000000000..86a17267f --- /dev/null +++ b/src/components/loadingBar/index.js @@ -0,0 +1,10 @@ + +import { connect } from 'react-redux'; +import LoadingBar from './loadingBar'; + +const mapStateToProps = state => ({ + loading: state.loading, +}); + +export default connect(mapStateToProps)(LoadingBar); + diff --git a/src/components/loadingBar/index.test.js b/src/components/loadingBar/index.test.js new file mode 100644 index 000000000..52e8e58ce --- /dev/null +++ b/src/components/loadingBar/index.test.js @@ -0,0 +1,19 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import { Provider } from 'react-redux'; +import LoadingBar from './'; +import store from '../../store'; + + +describe('LoadingBar Container', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(); + }); + + it('should render Send', () => { + expect(wrapper.find('LoadingBar')).to.have.lengthOf(1); + }); +}); diff --git a/src/components/loadingBar/loadingBar.css b/src/components/loadingBar/loadingBar.css new file mode 100644 index 000000000..537c6cc38 --- /dev/null +++ b/src/components/loadingBar/loadingBar.css @@ -0,0 +1,7 @@ +.fixedAtTop { + position: fixed; + top: -11px; + right: 0; + width: 100vw; + z-index: 201; +} diff --git a/src/components/loadingBar/loadingBar.js b/src/components/loadingBar/loadingBar.js new file mode 100644 index 000000000..99827c14d --- /dev/null +++ b/src/components/loadingBar/loadingBar.js @@ -0,0 +1,14 @@ +import React from 'react'; +import ProgressBar from 'react-toolbox/lib/progress_bar'; +import styles from './loadingBar.css'; + +const LoadingBar = props => ( +
+ {props.loading && props.loading.length ? + : + null + } +
+); + +export default LoadingBar; diff --git a/src/components/loadingBar/loadingBar.test.js b/src/components/loadingBar/loadingBar.test.js new file mode 100644 index 000000000..4f366b24d --- /dev/null +++ b/src/components/loadingBar/loadingBar.test.js @@ -0,0 +1,17 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import LoadingBar from './loadingBar'; + + +describe('LoadingBar Container', () => { + it('should show ProgresBar if props.loading.length is not 0', () => { + const wrapper = mount(); + expect(wrapper.find('ProgressBar')).to.have.lengthOf(1); + }); + + it('should not show ProgresBar if props.loading.length is 0', () => { + const wrapper = mount(); + expect(wrapper.find('ProgressBar')).to.have.lengthOf(0); + }); +}); diff --git a/src/constants/actions.js b/src/constants/actions.js index 11fdbca0f..da81e2b76 100644 --- a/src/constants/actions.js +++ b/src/constants/actions.js @@ -8,6 +8,8 @@ const actionTypes = { dialogHidden: 'DIALOG_HIDDEN', forgedBlocksUpdated: 'FORGED_BLOCKS_UPDATED', forgingStatsUpdated: 'FORGING_STATS_UPDATED', + loadingStarted: 'LOADING_STARTED', + loadingFinished: 'LOADING_FINISHED', }; export default actionTypes; diff --git a/src/store/reducers/index.js b/src/store/reducers/index.js index 9dca54638..d2d18cd81 100644 --- a/src/store/reducers/index.js +++ b/src/store/reducers/index.js @@ -2,4 +2,5 @@ export { default as account } from './account'; export { default as peers } from './peers'; export { default as dialog } from './dialog'; export { default as forging } from './forging'; +export { default as loading } from './loading'; diff --git a/src/store/reducers/loading.js b/src/store/reducers/loading.js new file mode 100644 index 000000000..2d3ab5bdd --- /dev/null +++ b/src/store/reducers/loading.js @@ -0,0 +1,19 @@ +import actionTypes from '../../constants/actions'; + +/** + * + * @param {Array} state + * @param {Object} action + */ +const dialog = (state = [], action) => { + switch (action.type) { + case actionTypes.loadingStarted: + return [...state, action.data]; + case actionTypes.loadingFinished: + return state.filter(item => item !== action.data); + default: + return state; + } +}; + +export default dialog; diff --git a/src/store/reducers/loding.test.js b/src/store/reducers/loding.test.js new file mode 100644 index 000000000..2b13bda28 --- /dev/null +++ b/src/store/reducers/loding.test.js @@ -0,0 +1,34 @@ +import chai, { expect } from 'chai'; +import sinonChai from 'sinon-chai'; +import loading from './loading'; +import actionTypes from '../../constants/actions'; + + +chai.use(sinonChai); + +describe('Reducer: loading(state, action)', () => { + let state; + + beforeEach(() => { + state = ['test1', 'test2']; + }); + + it('should return loading array with the new loading if action.type = actionTypes.loadingStarted', () => { + const action = { + type: actionTypes.loadingStarted, + data: 'test3', + }; + const changedState = loading(state, action); + expect(changedState).to.deep.equal([...state, action.data]); + }); + + it('should return loading array without action.data if action.type = actionTypes.loadingFinished', () => { + const action = { + type: actionTypes.loadingFinished, + data: 'test1', + }; + const changedState = loading(state, action); + expect(changedState).to.deep.equal(['test2']); + }); +}); + diff --git a/src/utils/api/peers.js b/src/utils/api/peers.js index fd94c4018..76d32f0bb 100644 --- a/src/utils/api/peers.js +++ b/src/utils/api/peers.js @@ -1,12 +1,16 @@ +import { loadingStarted, loadingFinished } from '../../utils/loading'; + /* eslint-disable */ export const requestToActivePeer = (activePeer, path, urlParams) => new Promise((resolve, reject) => { + loadingStarted(path); activePeer.sendRequest(path, urlParams, (data) => { if (data.success) { resolve(data); } else { reject(data); } + loadingFinished(path); }); }); /* eslint-enable */ diff --git a/src/utils/loading.js b/src/utils/loading.js new file mode 100644 index 000000000..793699a2e --- /dev/null +++ b/src/utils/loading.js @@ -0,0 +1,7 @@ +import { loadingStarted as loadingStartedAction, loadingFinished as loadingFinishedAction } from '../actions/loading'; +import store from '../store'; + + +export const loadingStarted = data => store.dispatch(loadingStartedAction(data)); + +export const loadingFinished = data => store.dispatch(loadingFinishedAction(data));