From 366a69b2caefdd7e2f2d1b0d5e7d241a20f58670 Mon Sep 17 00:00:00 2001 From: Ezeoke Onyekachi Samuel Date: Wed, 19 Jun 2019 13:08:40 +0100 Subject: [PATCH] [##165785921] implement feature to enable super user view office blocks in different centres (#243) * [##165785921] implement a feature to enable superuser view office blocks in different locations - add dropdown component - add action to fetch office blocks in a particular center - add action test - implement feedback on dropdown styling [finishes ##165785921] --- .../actions/officeLocations.actions.test.js | 13 ++++++- .../components/OfficeBlocksComponent.test.js | 39 +++++++++++++++++++ src/_actions/officeLocations.actions.js | 8 ++++ .../OfficeBlocks/OfficeBlocksContainer.jsx | 6 ++- src/_css/officeBlocksComponent.scss | 7 ++++ src/_mock/officeBlocks.js | 17 ++++++++ .../OfficeBlocks/OfficeBlocksComponent.jsx | 34 +++++++++++++++- 7 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 src/_css/officeBlocksComponent.scss create mode 100644 src/_mock/officeBlocks.js diff --git a/src/__test__/actions/officeLocations.actions.test.js b/src/__test__/actions/officeLocations.actions.test.js index fcb261f8..36aa21a4 100644 --- a/src/__test__/actions/officeLocations.actions.test.js +++ b/src/__test__/actions/officeLocations.actions.test.js @@ -4,9 +4,10 @@ import axios from 'axios'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; -import { loadOfficeLocations, loadOfficeBlocks, createOfficeBlock } from '../../_actions/officeLocations.actions'; +import { loadOfficeLocations, loadOfficeBlocks, createOfficeBlock, loadCentreOfficeBlocks } from '../../_actions/officeLocations.actions'; import officeLocations from '../../_mock/officeLocations'; +import officeBlocks from '../../_mock/officeBlocks'; import constants from '../../_constants'; @@ -73,6 +74,16 @@ describe('Centres Action tests', () => { }); }); + it('should dispatch LOAD_OFFICE_BLOCK_SUCCESS when loadCentreOfficeBlocks is called successfully', () => { + mock.onGet().reply(200, officeBlocks.results); + return store.dispatch(loadCentreOfficeBlocks(2)).then(() => { + expect(store.getActions()).toContainEqual({ + payload: officeBlocks.results, + type: LOAD_OFFICE_BLOCK_SUCCESS + }); + }); + }); + it('should dispatch LOAD_OFFICE_BLOCK_FAILURE when loadOfficeBlocks is unsuccessful', () => { mock.onGet('office-blocks/?page=1&page_size=10').reply(400, 'Could not office blocks'); return store.dispatch(loadOfficeBlocks(1, 10)).then(() => { diff --git a/src/__test__/components/OfficeBlocksComponent.test.js b/src/__test__/components/OfficeBlocksComponent.test.js index 4d980b2e..a242160b 100644 --- a/src/__test__/components/OfficeBlocksComponent.test.js +++ b/src/__test__/components/OfficeBlocksComponent.test.js @@ -8,8 +8,10 @@ describe('Renders correctly', () => { const props = { isLoading: false, blockCount: 2, + locationList: [{ name: 'Lagos', id: 23 }, { name: 'Nairobi', id: 45 }], loadOfficeBlocks: jest.fn(), loadOfficeLocations: jest.fn(), + loadCentreOfficeBlocks: jest.fn(), loadCountries: jest.fn(), resetMessage: jest.fn() }; @@ -36,6 +38,43 @@ describe('Renders correctly', () => { expect(handlePaginationChangeSpy.mock.calls.length).toEqual(1); }); + it('calls handleToggleModal when the modal is toggled', () => { + const handleToggleModalSpy = jest.spyOn(wrapper.instance(), 'handleToggleModal'); + + wrapper.setState({ modalOpen: true }); + + const event = {}; + const data = {}; + + wrapper.instance().handleToggleModal(event, data); + expect(handleToggleModalSpy.mock.calls.length).toEqual(1); + expect(wrapper.state('modalOpen')).toEqual(false); + }); + + it('calls handleEditToggleModal when modal is clicked', () => { + const handleEditToggleModalSpy = jest.spyOn(wrapper.instance(), 'handleEditToggleModal'); + + wrapper.setState({ modalOpen: true }); + + const event = {}; + const data = {}; + + wrapper.instance().handleEditToggleModal(event, data); + expect(handleEditToggleModalSpy.mock.calls.length).toEqual(1); + expect(wrapper.state('modalOpen')).toEqual(true); + }); + + + it('calls handleDropdownChange the dropdown item is selected', () => { + const handleDropdownChangeSpy = jest.spyOn(wrapper.instance(), 'handleDropdownChange'); + + const event = {}; + const data = {}; + + wrapper.instance().handleDropdownChange(event, data); + expect(handleDropdownChangeSpy.mock.calls.length).toEqual(1); + }); + it('calls the getTotalPages function when the next button is clicked', () => { const getTotalPagesSpy = jest.spyOn(wrapper.instance(), 'getTotalPages'); wrapper.instance().getTotalPages(); diff --git a/src/_actions/officeLocations.actions.js b/src/_actions/officeLocations.actions.js index bdddadc7..634af914 100644 --- a/src/_actions/officeLocations.actions.js +++ b/src/_actions/officeLocations.actions.js @@ -104,6 +104,14 @@ export const loadOfficeBlocks = (pageNumber, limit) => (dispatch) => { }); }; +export const loadCentreOfficeBlocks = centreId => (dispatch) => { + dispatch({ type: LOAD_OFFICE_BLOCK_REQUEST }); + return axios + .get(`andela-centres/${centreId}/office_blocks/`) + .then(response => dispatch(loadOfficeBlocksSuccess(response.data))) + .catch(error => dispatch(loadOfficeBlocksFailure(error))); +}; + export const loadOfficeBlocksSuccess = blocks => ({ type: LOAD_OFFICE_BLOCK_SUCCESS, payload: blocks diff --git a/src/_components/OfficeBlocks/OfficeBlocksContainer.jsx b/src/_components/OfficeBlocks/OfficeBlocksContainer.jsx index eaa0a6f1..7f19bc9e 100644 --- a/src/_components/OfficeBlocks/OfficeBlocksContainer.jsx +++ b/src/_components/OfficeBlocks/OfficeBlocksContainer.jsx @@ -2,7 +2,8 @@ import { connect } from 'react-redux'; import { loadOfficeBlocks, resetMessage, - loadOfficeLocations + loadOfficeLocations, + loadCentreOfficeBlocks } from '../../_actions/officeLocations.actions'; import OfficeBlocksComponent from '../../components/OfficeBlocks/OfficeBlocksComponent'; @@ -32,5 +33,6 @@ export const mapStateToProps = ({ officeLocations }) => { export default connect(mapStateToProps, { loadOfficeBlocks, resetMessage, - loadOfficeLocations + loadOfficeLocations, + loadCentreOfficeBlocks })(OfficeBlocksComponent); diff --git a/src/_css/officeBlocksComponent.scss b/src/_css/officeBlocksComponent.scss new file mode 100644 index 00000000..b621ad55 --- /dev/null +++ b/src/_css/officeBlocksComponent.scss @@ -0,0 +1,7 @@ +.center-filter { + .ui.fluid.dropdown { + margin-top: 0; + height: 100%; + width: 160px; + } +} diff --git a/src/_mock/officeBlocks.js b/src/_mock/officeBlocks.js new file mode 100644 index 00000000..bce23aaa --- /dev/null +++ b/src/_mock/officeBlocks.js @@ -0,0 +1,17 @@ +export default { + count: 2, + next: null, + previous: null, + results: [ + { + id: 4, + name: 'Epic Tower', + location: 'Lagos' + }, + { + id: 5, + name: 'Dojo', + location: 'Nairobi' + } + ] +}; diff --git a/src/components/OfficeBlocks/OfficeBlocksComponent.jsx b/src/components/OfficeBlocks/OfficeBlocksComponent.jsx index 0af205ae..4b131cb3 100644 --- a/src/components/OfficeBlocks/OfficeBlocksComponent.jsx +++ b/src/components/OfficeBlocks/OfficeBlocksComponent.jsx @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { isEmpty } from 'lodash'; +import { Dropdown } from 'semantic-ui-react'; import NavBarComponent from '../../_components/NavBarContainer'; import LoaderComponent from '../../components/LoaderComponent'; import Cards from '../common/Card/Card'; @@ -10,6 +11,9 @@ import Paginator from '../common/PaginationComponent'; import StatusMessageComponent from '../common/StatusComponent'; import PageHeader from '../common/PageHeader'; import OfficeBlocksModal from '../../_components/OfficeBlocks/OfficeBlocksModal'; +import verifySuperAdmin from '../../_utils/verifySuperAdmin'; + +import '../../_css/officeBlocksComponent.scss'; class OfficeBlocksComponent extends React.Component { state = { @@ -48,14 +52,27 @@ class OfficeBlocksComponent extends React.Component { this.props.resetMessage(); }; + handleDropdownChange = (e, data) => { + this.props.loadCentreOfficeBlocks(data.value); + }; + getTotalPages = () => Math.ceil(this.props.blockCount / this.state.limit); render() { - const { isLoading, blockList, error, resetMessage, entity } = this.props; + const { isLoading, blockList, error, resetMessage, entity, locationList } = this.props; const hasLocations = !isEmpty(blockList); const showStatus = error; const showAction = entity === 'office-blocks'; const showNotFound = !isLoading && !hasLocations && !showStatus; + const options = [{ key: -1, text: 'Select Center', value: 0 }]; + locationList.forEach((location, index) => { + const locObj = { + key: index, + text: location.name, + value: location.id + }; + options.push(locObj); + }); return ( @@ -69,6 +86,19 @@ class OfficeBlocksComponent extends React.Component { onToggle={this.handleToggleModal} open={this.state.modalOpen} /> + { + verifySuperAdmin() && ( +
+ +
+ ) + } @@ -128,8 +158,10 @@ OfficeBlocksComponent.propTypes = { resetMessage: PropTypes.func, blockCount: PropTypes.number, blockList: PropTypes.array, + locationList: PropTypes.array, error: PropTypes.string, loadOfficeLocations: PropTypes.func, + loadCentreOfficeBlocks: PropTypes.func, entity: PropTypes.string };