Skip to content

Commit

Permalink
Merge pull request #1443 from onaio/issue-1441
Browse files Browse the repository at this point in the history
Fix planning tool auto-selection
  • Loading branch information
ciremusyoka authored Dec 8, 2020
2 parents 6b30d3e + 908301a commit ed79a7a
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 31 deletions.
4 changes: 4 additions & 0 deletions src/configs/lang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,10 @@ export const NUMBER_OF_STRUCTURES_IN_JURISDICTIONS = translate(
'NUMBER_OF_STRUCTURES_IN_JURISDICTIONS',
'NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS'
);
export const JURISDICTIONS_SELECTED = translate(
'JURISDICTIONS_SELECTED',
'jurisdiction(s) selected'
);
export const ADJUST_SLIDER_MESSAGE = translate(
'ADJUST_SLIDER_MESSAGE',
'Adjust slider to auto-target jurisdictions'
Expand Down
4 changes: 4 additions & 0 deletions src/configs/strings/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1556,6 +1556,10 @@
"message": "NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS",
"description": "(in caps) comes before a number that represents a structure count "
},
"JURISDICTIONS_SELECTED": {
"message": "jurisdiction(s) selected",
"description": "comes after a number that represents a jurisdiction count "
},
"ADJUST_SLIDER_MESSAGE": {
"message": "Adjust slider to auto-target jurisdictions",
"description": "Label of progress-bar form element"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,9 @@ export const AutoSelectView = (props: JurisdictionAssignmentViewFullProps) => {
<h3 className="mb-3 page-title">{pageTitle}</h3>
<TimelineSlider {...timelineSliderProps} />
<hr />
{/* each of this components is a step in the auto selection journey, we start
{/* each of these components is a step in the auto selection journey, we start
at the slider and go through a few tables and we should ideally end at plan assignment
each of this components gets a callback that is called that modifies the state of this
each of this components gets a callback that is called that modifies the state of this
container to know what is the next component to be rendered in the below section */}
{step !== TIMELINE_SLIDER_STEP1 && (
<div className="map-resource-widget">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ exports[`e2e test auto-selection flow from slider auto-selection to refine juris

exports[`e2e test auto-selection flow from slider auto-selection to refine jurisdictions: after third click 6`] = `"Total178"`;

exports[`e2e test auto-selection flow from slider auto-selection to refine jurisdictions: general page snapshot 1`] = `"Planning toolA2-Lusaka Akros Test Focus 2A2-Lusaka Akros Test Focus 2Auto target jurisdictions by riskRefine selected jurisdictionsAdjust slider to auto-target jurisdictionsRisk  0%00100NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS178Continue to next step"`;
exports[`e2e test auto-selection flow from slider auto-selection to refine jurisdictions: general page snapshot 1`] = `"Planning toolA2-Lusaka Akros Test Focus 2A2-Lusaka Akros Test Focus 2Auto target jurisdictions by riskRefine selected jurisdictionsAdjust slider to auto-target jurisdictionsRisk  0%00100NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS1781 jurisdiction(s) selectedContinue to next step"`;

exports[`e2e test auto-selection flow from slider auto-selection to refine jurisdictions: refine selected jurisdictions component 1`] = `"Planning toolA2-Lusaka Akros Test Focus 2A2-Lusaka Akros Test Focus 2Auto target jurisdictions by riskRefine selected jurisdictionsAssignment WrapperResource Estimate for Chiwuyu6 daysat a rate of structures per team per day with Teams....ZambiaEasternSindaChiwuyuNameStructures CountRiskTarget StatusStatus setting by...Selected JurisdictionsCWY_2517880TargetedAuto-selection1CWY_3241--Not Targeted0CWY_14336--Not Targeted0CWY_2297--Not Targeted0CWY_829--Not Targeted0CWY_15483--Not Targeted0CWY_715--Not Targeted0CWY_18266--Not Targeted0CWY_1126--Not Targeted0CWY_12269--Not Targeted0CWY_19304--Not Targeted0CWY_13251--Not Targeted0CWY_19015--Not Targeted0CWY_24185--Not Targeted0CWY_4299--Not Targeted0CWY_1017--Not Targeted0CWY_613--Not Targeted0CWY_26139--Not Targeted0CWY_16320--Not Targeted0CWY_11266--Not Targeted0CWY_17167--Not Targeted0CWY_23290--Not Targeted0CWY_5168--Not Targeted0save draftSave & Activate"`;

Expand All @@ -46,7 +46,7 @@ exports[`e2e test auto-selection flow from slider auto-selection to refine juris

exports[`e2e test auto-selection flow when there are no selected jurisdictions(without metaData): full page rendered text 1`] = `"Planning toolA2-Lusaka Akros Test Focus 2A2-Lusaka Akros Test Focus 2Auto target jurisdictions by riskRefine selected jurisdictionsAssignment WrapperZambia: There are no descendant jurisdictions that are targeted.....NameStructures CountRiskTarget StatusStatus setting by...Selected JurisdictionsZambia575280--0save draftSave & Activate"`;

exports[`e2e test auto-selection flow when there are no selected jurisdictions(without metaData): general page snapshot 1`] = `"Planning toolA2-Lusaka Akros Test Focus 2A2-Lusaka Akros Test Focus 2Auto target jurisdictions by riskRefine selected jurisdictionsAdjust slider to auto-target jurisdictionsRisk  0%00100NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS0Continue to next step"`;
exports[`e2e test auto-selection flow when there are no selected jurisdictions(without metaData): general page snapshot 1`] = `"Planning toolA2-Lusaka Akros Test Focus 2A2-Lusaka Akros Test Focus 2Auto target jurisdictions by riskRefine selected jurisdictionsAdjust slider to auto-target jurisdictionsRisk  0%00100NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS00 jurisdiction(s) selectedContinue to next step"`;

exports[`e2e test auto-selection flow when there are no selected jurisdictions(without metaData): structure summary step snapshot 1`] = `"Planning toolA2-Lusaka Akros Test Focus 2A2-Lusaka Akros Test Focus 2Auto target jurisdictions by riskRefine selected jurisdictionsAssignment WrapperZambia: There are no descendant jurisdictions that are targeted.....NameStructures CountThere are no descendant jurisdictions that are targeted.Continue to next step"`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,18 @@ describe('e2e test auto-selection flow', () => {
*/
// CWY_25 (178 structures) is selected by default during mount since it has a threshold of 80 > 0
expect(wrapper.find('div.slider-section').text()).toMatchInlineSnapshot(`"Risk  0%00100"`);
expect(wrapper.find('div.info-section').text()).toMatchInlineSnapshot(
`"NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS178"`
);
expect(
wrapper
.find('div.info-section')
.first()
.text()
).toMatchInlineSnapshot(`"NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS178"`);
expect(
wrapper
.find('div.info-section')
.last()
.text()
).toMatchInlineSnapshot(`"1 jurisdiction(s) selected"`);

// click next to proceed to the structureSummary view
wrapper.find('JurisdictionSelectionsSlider button.btn-success').simulate('click');
Expand Down Expand Up @@ -364,9 +373,18 @@ describe('e2e test auto-selection flow', () => {
*/
// CWY_25 (178 structures) is selected by default during mount since it has a threshold of 80 > 0
expect(wrapper.find('div.slider-section').text()).toMatchInlineSnapshot(`"Risk  0%00100"`);
expect(wrapper.find('div.info-section').text()).toMatchInlineSnapshot(
`"NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS0"`
);
expect(
wrapper
.find('div.info-section')
.first()
.text()
).toMatchInlineSnapshot(`"NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS0"`);
expect(
wrapper
.find('div.info-section')
.last()
.text()
).toMatchInlineSnapshot(`"0 jurisdiction(s) selected"`);

// click next to proceed to the structureSummary view
wrapper.find('JurisdictionSelectionsSlider button.btn-success').simulate('click');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import { ActionCreator, Store } from 'redux';
import {
ADJUST_SLIDER_MESSAGE,
CONTINUE_TO_NEXT_STEP,
JURISDICTIONS_SELECTED,
NUMBER_OF_STRUCTURES_IN_JURISDICTIONS,
} from '../../../../../configs/lang';
import { PlanDefinition } from '../../../../../configs/settings';
import { RISK_LABEL } from '../../../../../constants';
import hierarchyReducer, {
autoSelectNodes,
fetchTree,
getAllSelectedNodes,
getStructuresCount,
getTreeById,
reducerName as hierarchyReducerName,
Expand All @@ -44,7 +46,8 @@ reducerRegistry.register(hierarchyReducerName, hierarchyReducer);
interface Props {
rootJurisdictionId: string;
tree?: TreeNode;
structuresCount: number;
jurisdictionsCount: number /** number of selected jurisdictions */;
structuresCount: number /** number of selected structures */;
fetchJurisdictionsMetadataCreator: ActionCreator<FetchJurisdictionsMetadataAction>;
jurisdictionsMetadata: JurisdictionsMetadata[];
autoSelectCreator: typeof autoSelectNodes;
Expand All @@ -57,6 +60,7 @@ const defaultProps = {
autoSelectCreator: autoSelectNodes,
fetchJurisdictionsMetadataCreator: fetchJurisdictionsMetadata,
fetchTreeCreator: fetchTree,
jurisdictionsCount: 0,
jurisdictionsMetadata: [],
onClickNext: () => {
return;
Expand All @@ -75,6 +79,7 @@ export const JurisdictionSelectionsSlider = (props: Props) => {
rootJurisdictionId,
structuresCount,
autoSelectCreator,
jurisdictionsCount,
jurisdictionsMetadata,
plan,
tree,
Expand All @@ -84,7 +89,7 @@ export const JurisdictionSelectionsSlider = (props: Props) => {

const onChangeComplete = (val: number | Range) => {
const metaJurOfInterest = jurisdictionsMetadata.filter(
metaObject => parseInt(metaObject.value, 10) > val
metaObject => parseInt(metaObject.value || '0', 10) >= val
);
const jurisdictionsIdsMeta = metaJurOfInterest.map(meta => meta.key);
const callback = (node: TreeNode) => {
Expand All @@ -110,7 +115,7 @@ export const JurisdictionSelectionsSlider = (props: Props) => {
<Row>
<Col xs="12" md={{ size: 8, offset: 2 }} style={{ textAlign: 'center' }}>
<h4 className="mb-5 font-weight-bold">{ADJUST_SLIDER_MESSAGE}</h4>
<Row className="auto-target-row mb-3">
<Row className="auto-target-row">
<Col xs="12" md={6} className="slider-section py-3 px-5">
<p>
{RISK_LABEL}&nbsp;&nbsp;<span className="risk-label">{`${value}%`}</span>
Expand All @@ -131,6 +136,13 @@ export const JurisdictionSelectionsSlider = (props: Props) => {
<p>{structuresCount}</p>
</Col>
</Row>
<Row className="auto-target-row mb-3">
<Col xs="12" md="12" className="info-section py-0 px-5">
<p className="text-center mt-3">
{jurisdictionsCount} {JURISDICTIONS_SELECTED}
</p>
</Col>
</Row>
</Col>
</Row>
<hr />
Expand All @@ -144,14 +156,18 @@ export const JurisdictionSelectionsSlider = (props: Props) => {
JurisdictionSelectionsSlider.defaultProps = defaultProps;

/** map state to props */
export type MapStateToProps = Pick<Props, 'structuresCount' | 'tree' | 'jurisdictionsMetadata'>;
export type MapStateToProps = Pick<
Props,
'structuresCount' | 'tree' | 'jurisdictionsCount' | 'jurisdictionsMetadata'
>;
/** map dispatch to action creators */
export type MapDispatchToProps = Pick<
Props,
'autoSelectCreator' | 'fetchJurisdictionsMetadataCreator' | 'fetchTreeCreator'
>;

const structureCountSelector = getStructuresCount();
const selectedJurisdictionSelector = getAllSelectedNodes();
const treeSelector = getTreeById();

const mapStateToProps = (state: Partial<Store>, ownProps: Props): MapStateToProps => {
Expand All @@ -160,9 +176,18 @@ const mapStateToProps = (state: Partial<Store>, ownProps: Props): MapStateToProp
rootJurisdictionId: ownProps.rootJurisdictionId,
};
const structuresCount = structureCountSelector(state, filters);
const selectedJurisdictions = selectedJurisdictionSelector(state, {
leafNodesOnly: true,
...filters,
});
const tree = treeSelector(state, filters);
const jurisdictionsMetadata = getJurisdictionsMetadata(state);
return { structuresCount, tree, jurisdictionsMetadata };
return {
jurisdictionsCount: selectedJurisdictions.length,
jurisdictionsMetadata,
structuresCount,
tree,
};
};

const mapDispatchToProps: MapDispatchToProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { act } from 'react-dom/test-utils';
import { Provider } from 'react-redux';
import { ConnectedJurisdictionSelectionsSlider } from '..';
import store from '../../../../../../store';
import { fetchTree } from '../../../../../../store/ducks/opensrp/hierarchies';
import { deforest, fetchTree } from '../../../../../../store/ducks/opensrp/hierarchies';
import { sampleHierarchy } from '../../../../../../store/ducks/opensrp/hierarchies/tests/fixtures';
import { fetchJurisdictionsMetadata } from '../../../../../../store/ducks/opensrp/jurisdictionsMetadata';
import { plans } from '../../../../../../store/ducks/opensrp/PlanDefinition/tests/fixtures';
Expand All @@ -17,12 +17,40 @@ import { jurisdictionsMetadataArray } from '../../../../../../store/ducks/tests/
*/

describe('JurisdictionAssignment/Slider', () => {
beforeEach(() => {
deforest();
});

it('slider works correctly', () => {
store.dispatch(fetchTree(sampleHierarchy));
const hierarchy = cloneDeep(sampleHierarchy);

// Add a node to the hierarchy
(hierarchy as any).locationsHierarchy.map['2942'].children['3019'].children['1337'] = {
id: '1337',
label: 'Ona office',
node: {
attributes: {
geographicLevel: 2,
structureCount: 10,
},
locationId: '1337',
name: 'Ona office',
parentLocation: {
locationId: '3019',
voided: false,
},
voided: false,
},
parent: '3019',
};

store.dispatch(fetchTree(hierarchy));
// prepare fixtures jurisdiction metadata so that it works with mock hierarchy
// it will be so that we select one leaf node in sampleHierarchy i.e Akros_1 3951
// we are specifically setting the jurisdiction ids so that they match what we
// have in the hierarchy
const metaData = cloneDeep(jurisdictionsMetadataArray);
metaData[0].key = '3951';
metaData[1].key = '1337';

store.dispatch(fetchJurisdictionsMetadata(metaData));

Expand All @@ -39,31 +67,111 @@ describe('JurisdictionAssignment/Slider', () => {

wrapper.update();

// Akros_1 is selected by default during mount since it has a threshold of 80 > 0
// Akros_1 and Ona office are selected by default, since threshold of both > 0
expect(wrapper.find('div.slider-section').text()).toMatchInlineSnapshot(`"Risk  0%00100"`);
expect(wrapper.find('div.info-section').text()).toMatchInlineSnapshot(
`"NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS159"`
);
expect(wrapper.find('div.info-section').length).toEqual(2);
expect(
wrapper
.find('div.info-section')
.first()
.text()
).toMatchInlineSnapshot(`"NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS169"`);
expect(
wrapper
.find('div.info-section')
.last()
.text()
).toMatchInlineSnapshot(`"2 jurisdiction(s) selected"`);

// simulate a change on the input slider. will set a thresh-hold value
// that will not select any of the jurisdictions
// that will deselect one of the jurisdictions

/** we really should not be doing this, but I am currently unable to simulate
* the change event
*/
act(() => {
(wrapper.find('InputRange').props() as any).onChange(81);
(wrapper.find('InputRange').props() as any).onChange(71);
});

// the risk value should be now different 2
expect(wrapper.find('.risk-label').text()).toMatchInlineSnapshot(`"81%"`);
// the risk value should be now different 71%
expect(wrapper.find('.risk-label').text()).toMatchInlineSnapshot(`"71%"`);

// invoke the method that now actually causes the auto-selection to happen
(wrapper.find('InputRange').props() as any).onChangeComplete(71);

// the structure and jurisdiction counts should now reflect just Akros_1 being selected
expect(
wrapper
.find('div.info-section')
.first()
.text()
).toMatchInlineSnapshot(`"NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS159"`);
expect(
wrapper
.find('div.info-section')
.last()
.text()
).toMatchInlineSnapshot(`"1 jurisdiction(s) selected"`);

// simulate another change to deselect all jurisdictions
/** we really should not be doing this, but I am currently unable to simulate
* the change event
*/
act(() => {
(wrapper.find('InputRange').props() as any).onChange(81);
});
expect(wrapper.find('.risk-label').text()).toMatchInlineSnapshot(`"81%"`);
(wrapper.find('InputRange').props() as any).onChangeComplete(81);
// we should now have no jurisdictions selected
expect(
wrapper
.find('div.info-section')
.first()
.text()
).toMatchInlineSnapshot(`"NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS0"`);
expect(
wrapper
.find('div.info-section')
.last()
.text()
).toMatchInlineSnapshot(`"0 jurisdiction(s) selected"`);
});

// the structure count should now be zero
expect(wrapper.find('div.info-section').text()).toMatchInlineSnapshot(
`"NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS0"`
it('works when no value from API', () => {
store.dispatch(fetchTree(sampleHierarchy));
// set value to undefined
const metaData = cloneDeep(jurisdictionsMetadataArray);
metaData[0].key = '3951';
(metaData[0].value as any) = undefined;

store.dispatch(fetchJurisdictionsMetadata([metaData[0]]));

const props = {
plan: plans[1],
rootJurisdictionId: '2942',
};

const wrapper = mount(
<Provider store={store}>
<ConnectedJurisdictionSelectionsSlider {...props} />
</Provider>
);

wrapper.update();

expect(wrapper.find('div.info-section').length).toEqual(2);

expect(
wrapper
.find('div.info-section')
.first()
.text()
).toMatchInlineSnapshot(`"NUMBER OF STRUCTURES IN SELECTED JURISDICTIONS159"`);
expect(
wrapper
.find('div.info-section')
.last()
.text()
).toMatchInlineSnapshot(`"1 jurisdiction(s) selected"`);
});
});
Loading

0 comments on commit ed79a7a

Please sign in to comment.