From ddeab0a478c246b1900176b3ca9b0ce37aa10919 Mon Sep 17 00:00:00 2001 From: Tunmise Ogunniyi Date: Mon, 6 Aug 2018 18:52:26 +0100 Subject: [PATCH] Render lab-tests dynamically (#133) --- .../components/labOrderEntry/LabEntryForm.jsx | 22 +++++++++++-- .../labOrderEntry/LabTestFieldSet.jsx | 31 ++++++++++--------- app/js/components/labOrderEntry/styles.scss | 11 ++++++- app/js/reducers/initialState.js | 1 + .../reducers/labOrders/labConceptsReducer.js | 25 +++++++++++++-- .../labOrderEntry/LabEntryForm.test.jsx | 9 ++++++ .../labOrderEntry/LabTestFieldSet.test.jsx | 15 +++++++-- .../labConceptsReducer.test.js | 31 +++++++++++++++++-- 8 files changed, 121 insertions(+), 24 deletions(-) diff --git a/app/js/components/labOrderEntry/LabEntryForm.jsx b/app/js/components/labOrderEntry/LabEntryForm.jsx index acced607..80301ac9 100644 --- a/app/js/components/labOrderEntry/LabEntryForm.jsx +++ b/app/js/components/labOrderEntry/LabEntryForm.jsx @@ -22,6 +22,7 @@ import '../../../css/grid.scss'; import './styles.scss'; import fetchLabOrders from '../../actions/labOrders/fetchLabOrders'; +import { fetchLabConcepts } from '../../actions/labOrders/labConceptsAction'; export class LabEntryForm extends PureComponent { static propTypes = { @@ -51,6 +52,7 @@ export class LabEntryForm extends PureComponent { inpatientCareSetting: PropTypes.shape({ uuid: PropTypes.string, }), + conceptsAsTests: PropTypes.array, session: PropTypes.shape({ currentProvider: PropTypes.shape({ person: PropTypes.shape({ @@ -77,6 +79,7 @@ export class LabEntryForm extends PureComponent { inpatientCareSetting: { uuid: '', }, + conceptsAsTests: [], session: { currentProvider: { person: { @@ -97,6 +100,9 @@ export class LabEntryForm extends PureComponent { componentDidMount() { this.props.dispatch(fetchLabOrders(null, 5, this.props.patient.uuid)); + if (this.state.categoryUUID) { + this.props.dispatch(fetchLabConcepts(`${this.state.categoryUUID}?v=full`)); + } } componentWillReceiveProps(nextProps) { @@ -110,7 +116,7 @@ export class LabEntryForm extends PureComponent { }); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps, prevState) { const { status: { added, error }, errorMessage, @@ -123,6 +129,9 @@ export class LabEntryForm extends PureComponent { if (error) { errorToast(errorMessage); } + if (this.state.categoryUUID !== prevState.categoryUUID) { + this.props.dispatch(fetchLabConcepts(`${this.state.categoryUUID}?v=full`)); + } } handleTestSelection = (item, type) => { @@ -153,6 +162,10 @@ export class LabEntryForm extends PureComponent { } } + handleUrgencyChange = (order) => { + this.props.dispatch(toggleDraftLabOrdersUgency(order)); + } + discardTestsInDraft = (test, action) => { const { dispatch } = this.props; if (action === 'single') return dispatch(removeTestFromDraft(test)); @@ -170,6 +183,7 @@ export class LabEntryForm extends PureComponent { handleTestSelection={this.handleTestSelection} draftLabOrders={this.props.draftLabOrders} selectedTests={this.props.selectedTests} + tests={this.props.conceptsAsTests} /> ); @@ -247,7 +261,7 @@ export class LabEntryForm extends PureComponent { renderLabDraftOrder = () => (
this.props.dispatch(toggleDraftLabOrdersUgency(order))} + toggleDraftLabOrdersUgency={this.handleUrgencyChange} handleDraftDiscard={this.discardTestsInDraft} draftLabOrders={this.props.draftLabOrders} panelTests={this.state.selectedPanelTestIds} @@ -305,6 +319,9 @@ export const mapStateToProps = ({ selectedTests, }, dateFormatReducer: { dateFormat }, + labConceptsReducer: { + conceptsAsTests, + }, openmrs: { session }, fetchLabOrderReducer: { labOrders }, patientReducer: { patient }, @@ -316,6 +333,7 @@ export const mapStateToProps = ({ }) => ({ draftLabOrders, dateFormat, + conceptsAsTests, selectedLabPanels, defaultTests, selectedTests, diff --git a/app/js/components/labOrderEntry/LabTestFieldSet.jsx b/app/js/components/labOrderEntry/LabTestFieldSet.jsx index 7391674b..eaa14f59 100644 --- a/app/js/components/labOrderEntry/LabTestFieldSet.jsx +++ b/app/js/components/labOrderEntry/LabTestFieldSet.jsx @@ -1,24 +1,26 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { testsData } from './labData'; import '../../../css/grid.scss'; -const LabTestFieldSet = ({ selectedTests, handleTestSelection }) => { - const selectedTestIds = selectedTests.map(test => test.id); +const LabTestFieldSet = ({ selectedTests, handleTestSelection, tests }) => { + const selectedTestIds = selectedTests.map(test => test.uuid); return (
Tests -
- {testsData.map(item => ( - - ))} +
+ { + tests.map(test => ( + + )) + }
); @@ -27,6 +29,7 @@ const LabTestFieldSet = ({ selectedTests, handleTestSelection }) => { LabTestFieldSet.propTypes = { selectedTests: PropTypes.array.isRequired, handleTestSelection: PropTypes.func.isRequired, + tests: PropTypes.array.isRequired, }; export default LabTestFieldSet; diff --git a/app/js/components/labOrderEntry/styles.scss b/app/js/components/labOrderEntry/styles.scss index 36ea7c92..5d15a677 100644 --- a/app/js/components/labOrderEntry/styles.scss +++ b/app/js/components/labOrderEntry/styles.scss @@ -85,10 +85,19 @@ margin-bottom: 10px; } - .lab-tests-btn{ + .panel-box, .tests-box{ + display: flex; + flex-wrap: wrap; + } + + .lab-tests-btn { width: 145px; + font-size: 13px; + font-weight: bold; margin-right: 10px; margin-bottom: 10px; + flex-basis: 31.7%; + text-transform: capitalize; } .draft-lab-wrapper{ diff --git a/app/js/reducers/initialState.js b/app/js/reducers/initialState.js index 815f95d4..94b95ec8 100644 --- a/app/js/reducers/initialState.js +++ b/app/js/reducers/initialState.js @@ -121,5 +121,6 @@ export default { error: null, loading: false, concepts: [], + conceptsAsTests: [], }, }; diff --git a/app/js/reducers/labOrders/labConceptsReducer.js b/app/js/reducers/labOrders/labConceptsReducer.js index 2ea03523..86e1ad37 100644 --- a/app/js/reducers/labOrders/labConceptsReducer.js +++ b/app/js/reducers/labOrders/labConceptsReducer.js @@ -5,15 +5,36 @@ import { } from '../../actions/actionTypes'; import initialState from '../initialState'; + +const removeDuplicateTests = (tests) => { + const uniqueTestIds = Array.from(new Set(tests.map(test => test.uuid))); + const uniqueTests = []; + tests.forEach((test) => { + const testIndex = uniqueTestIds.indexOf(test.uuid); + if (testIndex !== -1) { + uniqueTests.push(test); + uniqueTestIds.splice(testIndex, 1); + } + }); + return uniqueTests; +}; + export default (state = initialState.labConcepts, action) => { switch (action.type) { - case FETCH_LAB_CONCEPTS_SUCCESS: + case FETCH_LAB_CONCEPTS_SUCCESS: { + const concepts = [...action.payload.data.setMembers]; + const panels = concepts.filter(concept => concept.set); + const panelTests = [].concat(...panels.map(panel => panel.setMembers)); + const standAloneTests = concepts.filter(concept => !concept.set); + const allTests = [...standAloneTests, ...panelTests]; return { ...state, - concepts: [...action.payload.data.setMembers], + concepts, + conceptsAsTests: removeDuplicateTests(allTests), loading: false, error: null, }; + } case FETCH_LAB_CONCEPTS_LOADING: return { ...state, diff --git a/tests/components/labOrderEntry/LabEntryForm.test.jsx b/tests/components/labOrderEntry/LabEntryForm.test.jsx index 1d64f8b8..932b8eed 100644 --- a/tests/components/labOrderEntry/LabEntryForm.test.jsx +++ b/tests/components/labOrderEntry/LabEntryForm.test.jsx @@ -59,6 +59,7 @@ props = { patient: { uuid: 'jfgfhfgf', }, + conceptsAsTests: [], encounterType: { uuid: 'fhhfgfh9998', }, @@ -104,6 +105,7 @@ describe('Component: LabEntryForm', () => { dateFormatReducer: { dateFormat: 'DD-MM-YYYY HH:mm' }, patientReducer: { patient: {} }, fetchLabOrderReducer: { labOrders: [] }, + labConceptsReducer: { conceptsAsTests:[] }, openmrs: { session: {} }, encounterRoleReducer: { encounterRole: {} }, encounterReducer: { encounterType: {} }, @@ -175,6 +177,13 @@ describe('Component: LabEntryForm', () => { expect(dispatch).toBeCalled(); }); + it('should toggle the urgency state of a test', () => { + const instance = getComponent().instance(); + const dispatch = jest.spyOn(props, 'dispatch'); + instance.handleUrgencyChange(); + expect(dispatch).toBeCalled(); + }) + it(`should change the default lab form's tests category by toggling component state`, () => { const component = getComponent(); const instance = component.instance(); diff --git a/tests/components/labOrderEntry/LabTestFieldSet.test.jsx b/tests/components/labOrderEntry/LabTestFieldSet.test.jsx index a9743f84..5f7a0c15 100644 --- a/tests/components/labOrderEntry/LabTestFieldSet.test.jsx +++ b/tests/components/labOrderEntry/LabTestFieldSet.test.jsx @@ -6,10 +6,14 @@ let mountedComponent; props = { handleTestSelection: jest.fn(), selectedTests: [ - { id: 1, test: 'Hemoglobin' }, - { id: 2, test: 'Hematocrit' }, - { id: 3, test: 'blood' }, + { uuid: 'asampleduuid1-234', display: 'sampleA' }, + { uuid: 'defmpleduuid1-566', display: 'sampleB' }, ], + tests: [ + { uuid: 'asampleduuid1-234', display: 'sampleA' }, + { uuid: 'defmpleduuid1-566', display: 'sampleB' }, + { uuid: '5sampleduuid2-788', display: 'sampleC' } + ] }; const getComponent = () => { @@ -37,4 +41,9 @@ describe('Component: LabTestFieldSet', () => { expect(handleTestSelection).toBeCalled(); expect(component).toMatchSnapshot(); }); + + it('should add a class of `active` to the selected test', () => { + const component = getComponent(); + expect(component.find('.lab-tests-btn.active').length).toEqual(2); // 2 tests are in the selectedTests array + }) }); diff --git a/tests/reducers/labOrderReducers/labConceptsReducer.test.js b/tests/reducers/labOrderReducers/labConceptsReducer.test.js index e3f1f36c..4f283e08 100644 --- a/tests/reducers/labOrderReducers/labConceptsReducer.test.js +++ b/tests/reducers/labOrderReducers/labConceptsReducer.test.js @@ -6,6 +6,21 @@ import { import initialState from '../../../app/js/reducers/initialState'; import labConceptsReducer from '../../../app/js/reducers/labOrders/labConceptsReducer'; +const mockConcepts = [ + { uuid: '123Abc-456', name: 'Concept A', set: false }, + { + name: 'Concept B', + set: true, + setMembers: [ + { uuid: '456Abc-123', name: 'Concept D', set: false }, + { uuid: '138Abc-466', name: 'Concept E', set: false }, + { uuid: '123Def-456', name: 'Concept F', set: false }, + ] + }, + { uuid: '321Abc-146', name: 'Concept C', set: false }, + { uuid: '456Abc-123', name: 'Concept D', set: false }, +]; + describe('Lab Concepts reducer', () => { it('should return the initial state', () => { const expectedState = labConceptsReducer(initialState.labConcepts, {}); @@ -15,12 +30,20 @@ describe('Lab Concepts reducer', () => { it('should handle `FETCH_LAB_CONCEPTS_SUCCESS`', () => { const mockAction = { type: FETCH_LAB_CONCEPTS_SUCCESS, - payload: { data: { setMembers: [{ name: 'I am not a true concept' }] } } + payload: { data: { setMembers: mockConcepts } } } const expectedState = { + ...initialState.labConcepts, error: null, loading: false, - concepts: [{ name: 'I am not a true concept' }], + concepts: mockConcepts, + conceptsAsTests: [ + { uuid: '123Abc-456', name: 'Concept A', set: false }, + { uuid: '321Abc-146', name: 'Concept C', set: false }, + { uuid: '456Abc-123', name: 'Concept D', set: false }, + { uuid: '138Abc-466', name: 'Concept E', set: false }, + { uuid: '123Def-456', name: 'Concept F', set: false }, + ], }; const actualState = labConceptsReducer(initialState.labConcepts, mockAction); expect(actualState).toEqual(expectedState); @@ -29,9 +52,11 @@ describe('Lab Concepts reducer', () => { it('should handle `FETCH_LAB_CONCEPTS_LOADING`', () => { const mockAction = { type: FETCH_LAB_CONCEPTS_LOADING }; const expectedState = { + ...initialState.labConcepts, error: null, loading: true, concepts: [], + conceptsAsTests: [], }; const actualState = labConceptsReducer(initialState.labConcepts, mockAction); expect(actualState).toEqual(expectedState); @@ -44,9 +69,11 @@ describe('Lab Concepts reducer', () => { payload: error }; const expectedState = { + ...initialState.labConcepts, error: error, loading: false, concepts: [], + conceptsAsTests: [], }; const actualState = labConceptsReducer(initialState.labConcepts, mockAction); expect(actualState).toEqual(expectedState);