From 73503ab9dfa20ad261c27ccdd430dd46c71c927d Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Wed, 27 Sep 2023 09:56:38 +0200 Subject: [PATCH 1/3] chore: update the toolbar feature commit with a fix for interpretationId url parameter (#2987) With the changes from the toolbar update, the Interpretations panel might not be loaded by default. So when the url included the interpretationId, no component was listening for it, resulting in the interpretation modal not being opened. The url param listener is thus moved to App. Jest and cypress tests added --- cypress/integration/interpretations.cy.js | 29 ++++ cypress/integration/smoke.cy.js | 42 ++++++ src/components/app/App.js | 10 ++ src/components/app/DetailsPanel.js | 11 +- .../app/__tests__/Detailspanel.spec.js | 142 ++++++++++++++++++ .../__snapshots__/Detailspanel.spec.js.snap | 89 +++++++++++ .../interpretations/Interpretations.js | 12 +- 7 files changed, 317 insertions(+), 18 deletions(-) create mode 100644 src/components/app/__tests__/Detailspanel.spec.js create mode 100644 src/components/app/__tests__/__snapshots__/Detailspanel.spec.js.snap diff --git a/cypress/integration/interpretations.cy.js b/cypress/integration/interpretations.cy.js index 7662150da..4a1e54abe 100644 --- a/cypress/integration/interpretations.cy.js +++ b/cypress/integration/interpretations.cy.js @@ -67,4 +67,33 @@ context('Interpretations', () => { deleteMap() }) + + it('opens and closes the interpretation panel', () => { + cy.intercept({ method: 'POST', url: /dataStatistics/ }).as( + 'postDataStatistics' + ) + cy.visit( + '/?id=ZBjCfSaLSqD&interpretationId=yKqhXZdeJ6a', + EXTENDED_TIMEOUT + ) //ANC: LLITN coverage district and facility + + cy.wait('@postDataStatistics') + .its('response.statusCode') + .should('eq', 201) + + cy.getByDataTest('interpretation-modal') + .find('h1') + .contains( + 'Viewing interpretation: ANC: LLITN coverage district and facility' + ) + .should('be.visible') + + cy.getByDataTest('interpretation-modal') + .findByDataTest('dhis2-modal-close-button') + .click() + + cy.getByDataTest('interpretation-modal').should('not.exist') + + cy.getByDataTest('interpretations-list').should('be.visible') + }) }) diff --git a/cypress/integration/smoke.cy.js b/cypress/integration/smoke.cy.js index 5a7ad16e3..be0d0f82d 100644 --- a/cypress/integration/smoke.cy.js +++ b/cypress/integration/smoke.cy.js @@ -38,4 +38,46 @@ context('Smoke Test', () => { Layer.validateCardTitle('ANC 1 Coverage') cy.get('canvas.maplibregl-canvas').should('be.visible') }) + + it('loads with map id and interpretationid lowercase', () => { + cy.intercept({ method: 'POST', url: /dataStatistics/ }).as( + 'postDataStatistics' + ) + cy.visit( + '/?id=ZBjCfSaLSqD&interpretationid=yKqhXZdeJ6a', + EXTENDED_TIMEOUT + ) //ANC: LLITN coverage district and facility + + cy.wait('@postDataStatistics') + .its('response.statusCode') + .should('eq', 201) + + cy.getByDataTest('interpretation-modal') + .find('h1') + .contains( + 'Viewing interpretation: ANC: LLITN coverage district and facility' + ) + .should('be.visible') + }) + + it('loads with map id and interpretationId uppercase', () => { + cy.intercept({ method: 'POST', url: /dataStatistics/ }).as( + 'postDataStatistics' + ) + cy.visit( + '/?id=ZBjCfSaLSqD&interpretationId=yKqhXZdeJ6a', + EXTENDED_TIMEOUT + ) //ANC: LLITN coverage district and facility + + cy.wait('@postDataStatistics') + .its('response.statusCode') + .should('eq', 201) + + cy.getByDataTest('interpretation-modal') + .find('h1') + .contains( + 'Viewing interpretation: ANC: LLITN coverage district and facility' + ) + .should('be.visible') + }) }) diff --git a/src/components/app/App.js b/src/components/app/App.js index 762d13b8a..7e8f8ca55 100644 --- a/src/components/app/App.js +++ b/src/components/app/App.js @@ -7,6 +7,7 @@ import { useDispatch } from 'react-redux' import { tSetAnalyticalObject } from '../../actions/analyticalObject.js' import { removeBingBasemaps, setBingMapsApiKey } from '../../actions/basemap.js' import { tSetExternalLayers } from '../../actions/externalLayers.js' +import { setInterpretation } from '../../actions/interpretations.js' import { tOpenMap } from '../../actions/map.js' import { CURRENT_AO_KEY } from '../../util/analyticalObject.js' import { getUrlParameter } from '../../util/requests.js' @@ -33,6 +34,15 @@ const App = () => { } else if (getUrlParameter('currentAnalyticalObject') === 'true') { await dispatch(tSetAnalyticalObject(currentAO)) } + + // analytics interpretation component uses camelcase + const interpretationId = + getUrlParameter('interpretationid') || + getUrlParameter('interpretationId') + + if (interpretationId) { + dispatch(setInterpretation(interpretationId)) + } } if (!isEmpty(systemSettings)) { diff --git a/src/components/app/DetailsPanel.js b/src/components/app/DetailsPanel.js index ddb88d10e..a5dee646a 100644 --- a/src/components/app/DetailsPanel.js +++ b/src/components/app/DetailsPanel.js @@ -9,17 +9,14 @@ import styles from './styles/DetailsPanel.module.css' const DetailsPanel = ({ interpretationsRenderCount }) => { const detailsPanelOpen = useSelector((state) => state.ui.rightPanelOpen) const viewOrgUnitProfile = useSelector((state) => state.orgUnitProfile) + const interpretationId = useSelector((state) => state.interpretation?.id) const getContent = () => { - if (!detailsPanelOpen) { - return null + if (interpretationId || (detailsPanelOpen && !viewOrgUnitProfile)) { + return } - return viewOrgUnitProfile ? ( - - ) : ( - - ) + return detailsPanelOpen ? : null } return ( diff --git a/src/components/app/__tests__/Detailspanel.spec.js b/src/components/app/__tests__/Detailspanel.spec.js new file mode 100644 index 000000000..6d6110eef --- /dev/null +++ b/src/components/app/__tests__/Detailspanel.spec.js @@ -0,0 +1,142 @@ +import { render } from '@testing-library/react' +import React from 'react' +import { Provider } from 'react-redux' +import configureMockStore from 'redux-mock-store' +import DetailsPanel from '../DetailsPanel.js' + +const mockStore = configureMockStore() + +jest.mock( + '../../interpretations/Interpretations.js', + () => + function MockInterpretations() { + return
Interpretations
+ } +) + +jest.mock( + '../../orgunits/OrgUnitProfile.js', + () => + function MockOrgUnitProfile() { + return
Org Unit Profile
+ } +) + +describe('DetailsPanel', () => { + test('renders InterpretationsPanel when has interpretationId, has orgUnitProfile and panel is open', () => { + const store = { + interpretation: { id: 'abc123' }, + orgUnitProfile: 'xyzpdq', + ui: { rightPanelOpen: true }, + } + + const { container } = render( + + + + ) + expect(container).toMatchSnapshot() + }) + test('renders InterpretationsPanel when has interpretationId, has orgUnitProfile and panel is closed', () => { + const store = { + interpretation: { id: 'abc123' }, + orgUnitProfile: 'xyzpdq', + ui: { rightPanelOpen: false }, + } + + const { container } = render( + + + + ) + expect(container).toMatchSnapshot() + }) + test('renders InterpretationsPanel when has interpretationId, no orgUnitProfile and panel is open', () => { + const store = { + interpretation: { id: 'abc123' }, + orgUnitProfile: null, + ui: { rightPanelOpen: true }, + } + + const { container } = render( + + + + ) + expect(container).toMatchSnapshot() + }) + + test('renders InterpretationsPanel when has interpretationId, no orgUnitProfile and panel is closed', () => { + const store = { + interpretation: { id: 'abc123' }, + orgUnitProfile: null, + ui: { rightPanelOpen: false }, + } + + const { container } = render( + + + + ) + expect(container).toMatchSnapshot() + }) + + test('renders OrgUnitProfile when no interpretationId, has orgUnitProfile and panel is open', () => { + const store = { + interpretation: {}, + orgUnitProfile: 'xyzpdq', + ui: { rightPanelOpen: true }, + } + + const { container } = render( + + + + ) + expect(container).toMatchSnapshot() + }) + + test('renders null when no interpretationId, has orgUnitProfile, and panel closed', () => { + const store = { + interpretation: {}, + orgUnitProfile: 'xyzpdq', + ui: { rightPanelOpen: false }, + } + + const { container } = render( + + + + ) + expect(container).toMatchSnapshot() + }) + test('renders InterpretationsPanel when no interpretationId, no orgUnitProfile and panel open', () => { + const store = { + interpretation: {}, + orgUnitProfile: null, + ui: { rightPanelOpen: true }, + } + + const { container } = render( + + + + ) + expect(container).toMatchSnapshot() + }) + + test('renders null when no interpretationId, no orgUnitProfile, and panel closed', () => { + const store = { + interpretation: {}, + orgUnitProfile: null, + ui: { rightPanelOpen: false }, + } + + const { container } = render( + + + + ) + expect(container).toMatchSnapshot() + }) +}) diff --git a/src/components/app/__tests__/__snapshots__/Detailspanel.spec.js.snap b/src/components/app/__tests__/__snapshots__/Detailspanel.spec.js.snap new file mode 100644 index 000000000..b45d254ce --- /dev/null +++ b/src/components/app/__tests__/__snapshots__/Detailspanel.spec.js.snap @@ -0,0 +1,89 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DetailsPanel renders InterpretationsPanel when has interpretationId, has orgUnitProfile and panel is closed 1`] = ` +
+ +
+`; + +exports[`DetailsPanel renders InterpretationsPanel when has interpretationId, has orgUnitProfile and panel is open 1`] = ` +
+
+
+ Interpretations +
+
+
+`; + +exports[`DetailsPanel renders InterpretationsPanel when has interpretationId, no orgUnitProfile and panel is closed 1`] = ` +
+ +
+`; + +exports[`DetailsPanel renders InterpretationsPanel when has interpretationId, no orgUnitProfile and panel is open 1`] = ` +
+
+
+ Interpretations +
+
+
+`; + +exports[`DetailsPanel renders InterpretationsPanel when no interpretationId, no orgUnitProfile and panel open 1`] = ` +
+
+
+ Interpretations +
+
+
+`; + +exports[`DetailsPanel renders OrgUnitProfile when no interpretationId, has orgUnitProfile and panel is open 1`] = ` +
+
+
+ Org Unit Profile +
+
+
+`; + +exports[`DetailsPanel renders null when no interpretationId, has orgUnitProfile, and panel closed 1`] = ` +
+ +`; + +exports[`DetailsPanel renders null when no interpretationId, no orgUnitProfile, and panel closed 1`] = ` +
+ +`; diff --git a/src/components/interpretations/Interpretations.js b/src/components/interpretations/Interpretations.js index c937e5ef9..f3cf14087 100644 --- a/src/components/interpretations/Interpretations.js +++ b/src/components/interpretations/Interpretations.js @@ -1,9 +1,7 @@ import PropTypes from 'prop-types' import React, { useEffect } from 'react' import { useSelector, useDispatch } from 'react-redux' -import { setInterpretation } from '../../actions/interpretations.js' import { openInterpretationsPanel } from '../../actions/ui.js' -import { getUrlParameter } from '../../util/requests.js' import InterpretationsPanel from './InterpretationsPanel.js' const Interpretations = ({ renderCount }) => { @@ -15,15 +13,7 @@ const Interpretations = ({ renderCount }) => { useEffect(() => { if (isMapLoaded) { - // analytics interpretation component uses camelcase - const urlInterpretationId = - getUrlParameter('interpretationid') || - getUrlParameter('interpretationId') - - if (urlInterpretationId) { - dispatch(setInterpretation(urlInterpretationId)) - dispatch(openInterpretationsPanel()) - } + dispatch(openInterpretationsPanel()) } }, [isMapLoaded, dispatch]) From 5d33ca545721ac9631f5b8d90a9953cc4864cc05 Mon Sep 17 00:00:00 2001 From: "@dhis2-bot" Date: Wed, 27 Sep 2023 10:36:15 +0000 Subject: [PATCH 2/3] chore(release): cut 100.2.0 [skip release] # [100.2.0](https://github.com/dhis2/maps-app/compare/v100.1.7...v100.2.0) (2023-09-27) ### Bug Fixes * **translations:** sync translations from transifex (dev) ([af3183c](https://github.com/dhis2/maps-app/commit/af3183cb28928d5a2f862b13a57c45377d136b8b)) * accept both lowercase and camelCase interpretationId in url([#2937](https://github.com/dhis2/maps-app/issues/2937)) ([c171b3d](https://github.com/dhis2/maps-app/commit/c171b3d26189206e546361911e31f887082ee136)) * **translations:** sync translations from transifex (dev) ([1ec1b5c](https://github.com/dhis2/maps-app/commit/1ec1b5cac53d2f36a80201a258fe99757523ab15)) * **translations:** sync translations from transifex (dev) ([bfff4ab](https://github.com/dhis2/maps-app/commit/bfff4ab2a4036c3afce74bda58e24bc77504edbd)) * **translations:** sync translations from transifex (dev) ([2a77caa](https://github.com/dhis2/maps-app/commit/2a77caafdd6d8ee42db14d35a9ab3034e5e8897a)) ### Features * upgrade toolbar (DHIS2-15667) ([#2936](https://github.com/dhis2/maps-app/issues/2936)) ([e1bda37](https://github.com/dhis2/maps-app/commit/e1bda3702edc5c254bce6bacb573da5dd9f54305)) ### Reverts * "chore: fixed period select refactor ([#2958](https://github.com/dhis2/maps-app/issues/2958))" ([#2984](https://github.com/dhis2/maps-app/issues/2984)) ([f532a81](https://github.com/dhis2/maps-app/commit/f532a81463e1842a3e3aa938497505d290be028a)) --- CHANGELOG.md | 21 +++++++++++++++++++++ package.json | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0139b16b..05501fa7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +# [100.2.0](https://github.com/dhis2/maps-app/compare/v100.1.7...v100.2.0) (2023-09-27) + + +### Bug Fixes + +* **translations:** sync translations from transifex (dev) ([af3183c](https://github.com/dhis2/maps-app/commit/af3183cb28928d5a2f862b13a57c45377d136b8b)) +* accept both lowercase and camelCase interpretationId in url([#2937](https://github.com/dhis2/maps-app/issues/2937)) ([c171b3d](https://github.com/dhis2/maps-app/commit/c171b3d26189206e546361911e31f887082ee136)) +* **translations:** sync translations from transifex (dev) ([1ec1b5c](https://github.com/dhis2/maps-app/commit/1ec1b5cac53d2f36a80201a258fe99757523ab15)) +* **translations:** sync translations from transifex (dev) ([bfff4ab](https://github.com/dhis2/maps-app/commit/bfff4ab2a4036c3afce74bda58e24bc77504edbd)) +* **translations:** sync translations from transifex (dev) ([2a77caa](https://github.com/dhis2/maps-app/commit/2a77caafdd6d8ee42db14d35a9ab3034e5e8897a)) + + +### Features + +* upgrade toolbar (DHIS2-15667) ([#2936](https://github.com/dhis2/maps-app/issues/2936)) ([e1bda37](https://github.com/dhis2/maps-app/commit/e1bda3702edc5c254bce6bacb573da5dd9f54305)) + + +### Reverts + +* "chore: fixed period select refactor ([#2958](https://github.com/dhis2/maps-app/issues/2958))" ([#2984](https://github.com/dhis2/maps-app/issues/2984)) ([f532a81](https://github.com/dhis2/maps-app/commit/f532a81463e1842a3e3aa938497505d290be028a)) + ## [100.1.7](https://github.com/dhis2/maps-app/compare/v100.1.6...v100.1.7) (2023-09-07) diff --git a/package.json b/package.json index 92113ed13..124445fee 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maps-app", - "version": "100.1.7", + "version": "100.2.0", "description": "DHIS2 Maps", "license": "BSD-3-Clause", "author": "Bjørn Sandvik", From 51f0760fbbeb556625f15c841ac6eb493248fab5 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Wed, 27 Sep 2023 16:26:52 +0200 Subject: [PATCH 3/3] chore: only send bot messages on the actual release commit (#2988) Only messages on the actual release should be sent to the slack channel This can be identified by the chore(release) in the commit message. --- .github/workflows/dhis2-verify-app.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dhis2-verify-app.yml b/.github/workflows/dhis2-verify-app.yml index 72c88d2d3..6a9ce9443 100644 --- a/.github/workflows/dhis2-verify-app.yml +++ b/.github/workflows/dhis2-verify-app.yml @@ -124,7 +124,8 @@ jobs: if: | failure() && !cancelled() && - github.ref == 'refs/heads/master' + github.ref == 'refs/heads/master' && + contains(github.event.head_commit.message, 'chore(release)') steps: - name: Checkout code uses: actions/checkout@master @@ -140,13 +141,13 @@ jobs: channel-id: ${{ secrets.SLACK_CHANNEL_ID }} payload: | { - "text": ":small_red_triangle_down: :maps-app: Maps version ${{ steps.extract_version.outputs.version }} release ", + "text": ":small_red_triangle_down: :maps-app: Maps release ", "blocks": [ { "type": "section", "text": { "type": "mrkdwn", - "text": ":small_red_triangle_down: :maps-app: Maps version ${{ steps.extract_version.outputs.version }} release " + "text": ":small_red_triangle_down: :maps-app: Maps release " } } ] @@ -160,7 +161,8 @@ jobs: if: | success() && !cancelled() && - github.ref == 'refs/heads/master' + github.ref == 'refs/heads/master' && + contains(github.event.head_commit.message, 'chore(release)') steps: - name: Checkout code uses: actions/checkout@master