From 4b1076ce61866da09de14e8dbb178d3c82717a6c Mon Sep 17 00:00:00 2001 From: Bjorn Sandvik Date: Tue, 5 Mar 2024 13:59:56 +0100 Subject: [PATCH 1/5] feat: when rendering for push analytics, disable download button until map is rendered (#3072) * feat: disable download button until map is rendered * chore: code cleaning * chore: code cleaning * chore: code comment * fix: isPushAnalytics url param * fix: isPushAnalytics url param * chore: update @dhis2/analytics and deduplicate deps * chore: read single url param * fix: ensure isDownload is a bool to avoid prop-types error * fix: add class-names for push-analytics * fix: check download param when navigating to new * fix: prevent enabling download button while loading mask is showing * fix: add class to map container when no map id is set * fix: improve hover states and add consistent spacing [UX-161] (#3121) * fix: make `dhis2-map-new` class independent of downloadMode * feat: add push analytics instructions * chore: upgrade @dhis2/maps-gl --------- Co-authored-by: HendrikThePendric Co-authored-by: Jen Jones Arnesen Co-authored-by: Joseph John Aas Cooper --- package.json | 4 +- public/push-analytics.json | 24 ++++++++++ src/components/download/DownloadButton.js | 9 +++- src/components/download/DownloadSettings.js | 53 +++++++++++++++++++-- src/components/map/MapLoadingMask.js | 5 +- src/components/map/MapPosition.js | 1 + src/util/history.js | 6 +++ yarn.lock | 16 +++---- 8 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 public/push-analytics.json diff --git a/package.json b/package.json index 397b16808e..9e3741499c 100644 --- a/package.json +++ b/package.json @@ -39,13 +39,13 @@ "start-server-and-test": "^2.0.3" }, "dependencies": { - "@dhis2/analytics": "^26.2.0", + "@dhis2/analytics": "^26.3.0", "@dhis2/app-runtime": "3.9.4", "@dhis2/app-runtime-adapter-d2": "^1.1.0", "@dhis2/app-service-alerts": "3.9.4", "@dhis2/app-service-datastore": "^1.0.0-beta.3", "@dhis2/d2-i18n": "^1.1.3", - "@dhis2/maps-gl": "^3.8.6", + "@dhis2/maps-gl": "^3.9.0", "@dhis2/ui": "^9.2.0", "@krakenjs/post-robot": "^11.0.0", "@reportportal/agent-js-cypress": "git+https://github.com/dhis2/agent-js-cypress.git#develop", diff --git a/public/push-analytics.json b/public/push-analytics.json new file mode 100644 index 0000000000..f94685d0db --- /dev/null +++ b/public/push-analytics.json @@ -0,0 +1,24 @@ +{ + "version": "0.0.1", + "showVisualization": { + "strategy": "navigateToUrl", + "steps": [ + { "goto": "{{appUrl}}/#/{{id}}/download?isPushAnalytics=true" }, + { "waitForSelector": ".push-analytics-download-button:enabled" } + ] + }, + "triggerDownload": { + "strategy": "useUiElements", + "steps": [{ "click": ".push-analytics-download-button" }] + }, + "obtainDownloadArtifact": { + "strategy": "interceptFileDownload" + }, + "clearVisualization": { + "strategy": "navigateToUrl", + "steps": [ + { "goto": "{{appUrl}}/#/" }, + { "waitForSelector": ".dhis2-map-new .dhis2-map-rendered" } + ] + } +} diff --git a/src/components/download/DownloadButton.js b/src/components/download/DownloadButton.js index 971d76e9f8..63f3b3d785 100644 --- a/src/components/download/DownloadButton.js +++ b/src/components/download/DownloadButton.js @@ -1,11 +1,18 @@ import i18n from '@dhis2/d2-i18n' +import cx from 'classnames' import React from 'react' import { openDownloadMode } from '../../util/history.js' import styles from './styles/DownloadButton.module.css' const DownloadButton = () => { return ( - ) diff --git a/src/components/download/DownloadSettings.js b/src/components/download/DownloadSettings.js index b6efedb222..a6bfd81e7c 100644 --- a/src/components/download/DownloadSettings.js +++ b/src/components/download/DownloadSettings.js @@ -6,15 +6,23 @@ import { setDownloadConfig } from '../../actions/download.js' import { standardizeFilename } from '../../util/dataDownload.js' import { downloadMapImage, downloadSupport } from '../../util/export-image.js' import { getSplitViewLayer } from '../../util/helpers.js' -import { closeDownloadMode } from '../../util/history.js' +import { closeDownloadMode, getHashUrlParam } from '../../util/history.js' import { getMapName } from '../app/FileMenu.js' import Drawer from '../core/Drawer.js' import { Checkbox, Help } from '../core/index.js' +import { loadingMaskClass } from '../map/MapLoadingMask.js' import LegendLayers from './LegendLayers.js' import NorthArrowPosition from './NorthArrowPosition.js' import styles from './styles/DownloadSettings.module.css' +const mapContainerId = 'dhis2-map-container' +const mapClass = 'dhis2-map' +const renderedClass = 'dhis2-map-rendered' +const downloadingClass = 'dhis2-map-downloading' + const DownloadSettings = () => { + const isPushAnalytics = getHashUrlParam('isPushAnalytics') + const [isRendered, setIsRendered] = useState(false) const [error, setError] = useState(null) const dispatch = useDispatch() @@ -38,17 +46,17 @@ const DownloadSettings = () => { const onDownload = useCallback(() => { const filename = standardizeFilename(getMapName(name), 'png') - let mapEl = document.getElementById('dhis2-map-container') + let mapEl = document.getElementById(mapContainerId) if (includeMargins) { mapEl = mapEl.parentNode } // Temporary added to remove close 'x' from map popups - mapEl.classList.add('dhis2-map-downloading') + mapEl.classList.add(downloadingClass) downloadMapImage(mapEl, filename) - .then(() => mapEl.classList.remove('dhis2-map-downloading')) + .then(() => mapEl.classList.remove(downloadingClass)) .catch(setError) }, [name, includeMargins]) @@ -67,6 +75,36 @@ const DownloadSettings = () => { ) }, [name, description, legendLayers, hasLayers, dispatch]) + useEffect(() => { + if (isPushAnalytics) { + // Multiple map elements if split view + const mapElements = document.getElementsByClassName(mapClass) + + // Observe is rendered class is added to map element + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.attributeName == 'class') { + setIsRendered( + !document.querySelector(`.${loadingMaskClass}`) && + mutation.target.classList.contains( + renderedClass + ) + ) + } + }) + }) + + for (const mapEl of mapElements) { + mapEl.classList.remove(renderedClass) + observer.observe(mapEl, { attributes: true }) + } + + return () => { + observer.disconnect() + } + } + }, [isPushAnalytics]) + const isSupported = downloadSupport() && !error const isSplitView = !!getSplitViewLayer(mapViews) const showMarginsCheckbox = false // Not in use @@ -205,7 +243,12 @@ const DownloadSettings = () => { : i18n.t('Close')} {isSupported && ( - )} diff --git a/src/components/map/MapLoadingMask.js b/src/components/map/MapLoadingMask.js index ce43dd3ecd..7cc7812608 100644 --- a/src/components/map/MapLoadingMask.js +++ b/src/components/map/MapLoadingMask.js @@ -1,11 +1,14 @@ import { ComponentCover, CenteredContent, CircularLoader } from '@dhis2/ui' +import cx from 'classnames' import React from 'react' import styles from './styles/MapLoadingMask.module.css' +export const loadingMaskClass = 'dhis2-map-loading-mask' + const MapLoadingMask = () => ( diff --git a/src/components/map/MapPosition.js b/src/components/map/MapPosition.js index 0c202f95a3..8adc3bd309 100644 --- a/src/components/map/MapPosition.js +++ b/src/components/map/MapPosition.js @@ -98,6 +98,7 @@ const MapPosition = () => { className={cx(styles.mapContainer, { [styles.download]: downloadMode, 'dhis2-map-download': downloadMode, + 'dhis2-map-new': !mapId, })} > diff --git a/src/util/history.js b/src/util/history.js index 52a26f358f..ed49ecf896 100644 --- a/src/util/history.js +++ b/src/util/history.js @@ -34,6 +34,11 @@ const getHashUrlParams = (loc) => { return params } +const getHashUrlParam = (key) => { + const params = getHashUrlParams(history.location) + return params[key] +} + const openDownloadMode = () => { if (history.location.pathname === '/') { history.push(`/${DOWNLOAD}`) @@ -53,6 +58,7 @@ const closeDownloadMode = () => { export { getHashUrlParams, + getHashUrlParam, defaultHashUrlParams, openDownloadMode, closeDownloadMode, diff --git a/yarn.lock b/yarn.lock index 079360a625..f2e8054ac3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2016,10 +2016,10 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2/analytics@^26.2.0": - version "26.2.0" - resolved "https://registry.yarnpkg.com/@dhis2/analytics/-/analytics-26.2.0.tgz#36a7f258ac96ddab90f4001e62257e2cc64f202e" - integrity sha512-YcJu6EHnor6pbHmwXKYumLRVy/9TxuLtBDv9JIzjt9/APZa8kbak6sT2/53pnWDnbUjzDwR8EV1UIz24vAX+ig== +"@dhis2/analytics@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@dhis2/analytics/-/analytics-26.3.0.tgz#ada2fe27442f19704fa704e334546416c85ea1d6" + integrity sha512-B/pUh8K8wyivL4yBiwqPoQ94pMWwCqh0xu3Uak4jmJqS+jO0slUlyDLtAmXU/jqRlRgRg1nR4u18npjd511Q7A== dependencies: "@dhis2/d2-ui-rich-text" "^7.4.1" "@dhis2/multi-calendar-dates" "1.0.0" @@ -2243,10 +2243,10 @@ markdown-it "^8.4.2" prop-types "^15.6.2" -"@dhis2/maps-gl@^3.8.6": - version "3.8.6" - resolved "https://registry.yarnpkg.com/@dhis2/maps-gl/-/maps-gl-3.8.6.tgz#eace04fca9bb5d4cc24ca3fcebb4c13e3f8a6422" - integrity sha512-9jVfPczgT9MZrU/Cm5ksSjVnUV8/sPOdcaRVLj/nusDMOUVJ23Bt6/U6Gd6SOu/XPk8pWAi9dwyOF1p5pB8L7g== +"@dhis2/maps-gl@^3.9.0": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@dhis2/maps-gl/-/maps-gl-3.9.0.tgz#124f560eaa2107c7b0555b47d7b88533ebdca9e5" + integrity sha512-bGxBsiTRktUihyM4epcYGl1joJe5RG/r0HOHicU4UaxJLU7t8f1b9VVW18hp9TWyinjMUMmaorIu+dwOgFg/MA== dependencies: "@mapbox/sphericalmercator" "^1.2.0" "@turf/area" "^6.5.0" From fbdf0b04442b127823f1a5ec8fb80b564eaf21fe Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Tue, 5 Mar 2024 19:48:27 +0100 Subject: [PATCH 2/5] feat: add ability to add GeoJSON URL external layers (#3127) Implements: https://dhis2.atlassian.net/browse/DHIS2-15981 If geojson external overlay layers are configured for instance, they can now be added to a map. The data table will display the layer data as long as the collection is of homogenous geo types. User can also click on a feature and the right panel will open, showing the feature data. fix: check full instanceUrl and handle not response.ok in geojson loader (#3142) In production, the baseUrl is ".." and therefore the comparison will always be false. So check the instanceBaseUrl instead Also, a failed response from fetch, like 400, will return !response.ok, so that situation needed to be handled as well. fix: rename error to loadError to avoid name clash with building footprint ee layer (#3144) The building footprints earth engine layer has a property named error. That was getting picked up by the new notice box in the Card that was added to report geojson loading errors. Solution was to rename the error property in geojsonloader to loadError. fix: show no data message when geojson feature has no data (#3145) Geojson layers may or may not have data associated with them. Show an appropriate message if there is no data. fix: improve geojson layer error responses and cypress tests (#3149) fix: reduce the padding to make room for the scrollbar (#3148) fix: prevent data table effects from running code when no table (#3147) fix: map plugin - do not load async layers multiple times (#3143) fix: various fixes after release testing (#3151) * fix: limit stroke width to 0-10 * fix: tab length should only take the space of the title * fix: reset error when switching which layer shows data table * fix: highlight features when data table has filter * chore: improve legend for geojson layers * fix: set point radius to size that was set in the style settings * fix: the feature.id is set in maps-gl so use the properties.id instead find correct data * chore: legend item styling - set max line weight and rename to Point radius * fix: use more understandable error messages * fix: set minimum point size of 1 fix: final fixes for geojson (#3154) * fix: upgrade maps-gl for the rounded line join and caps * fix: add tooltip on layer thumbnails * chore: update i18n * fix: set map bounds after all layers are added to the map * fix: values that are strings but numeric in quality were not filtering * fix: onLayerAdded wasnt defined for SplitViews * Revert "fix: onLayerAdded wasnt defined for SplitViews" This reverts commit deeb327b446616006e1064ae5b7d18eafc84198b. * Revert "fix: set map bounds after all layers are added to the map" This reverts commit e1c0a58d6b4791a9cc2facb64608d53a4b3b3ad7. * fix: make logic match prev code * fix: position tooltip right over the thumbnail title --- .eslintignore | 1 + .github/workflows/dhis2-verify-app.yml | 4 +- .prettierignore | 2 +- cypress.config.js | 2 +- .../externalMapLayersWithGeojson.json | 70 ++ cypress/fixtures/feature.geojson | 702 ++++++++++++++++++ cypress/integration/dataTable.cy.js | 2 +- cypress/integration/layers/geojsonlayer.cy.js | 219 ++++++ cypress/integration/systemsettings.cy.js | 31 - i18n/en.pot | 80 +- package.json | 2 +- public/images/featurelayer.png | Bin 0 -> 13967 bytes src/actions/dataTable.js | 5 - src/actions/feature.js | 9 + src/actions/layerEdit.js | 6 + src/components/app/DetailsPanel.js | 19 +- src/components/app/FileMenu.js | 8 - .../app/__tests__/Detailspanel.spec.js | 202 +++-- .../__snapshots__/Detailspanel.spec.js.snap | 37 +- src/components/core/IconButton.js | 30 +- src/components/core/NumberField.js | 6 + src/components/core/Tabs.js | 2 +- .../core/styles/IconButton.module.css | 24 +- src/components/datatable/BottomPanel.js | 3 +- src/components/datatable/DataTable.js | 55 +- .../datatable/styles/DataTable.module.css | 3 +- src/components/datatable/useTableData.js | 134 +++- src/components/edit/LayerEdit.js | 11 +- src/components/edit/geoJson/GeoJsonDialog.js | 45 ++ src/components/edit/shared/FeatureStyle.js | 144 ++++ src/components/feature/FeatureProfile.js | 65 ++ .../feature/styles/FeatureProfile.module.css | 66 ++ src/components/layers/overlays/Layer.js | 25 +- src/components/layers/overlays/OverlayCard.js | 31 +- .../layers/overlays/styles/Layer.module.css | 3 + .../overlays/styles/LayerList.module.css | 2 +- .../overlays/styles/OverlayCard.module.css | 4 + .../layers/styles/LayerCard.module.css | 2 +- src/components/layers/toolbar/LayerToolbar.js | 52 +- .../layers/toolbar/LayerToolbarMoreMenu.js | 19 +- .../__snapshots__/LayerToolbar.spec.js.snap | 25 +- .../toolbar/styles/LayerToolbar.module.css | 20 +- .../styles/LayerToolbarMore.module.css | 5 - .../toolbar/styles/OpacitySlider.module.css | 3 - src/components/legend/LegendItem.js | 7 +- src/components/loaders/GeoJsonUrlLoader.js | 41 + src/components/loaders/LayerLoader.js | 3 + src/components/loaders/LayersLoader.js | 10 +- src/components/map/Map.js | 5 + src/components/map/MapContainer.js | 13 +- src/components/map/MapView.js | 8 +- src/components/map/layers/GeoJsonLayer.js | 75 ++ .../map/styles/FeatureButton.module.css | 4 + src/components/plugin/Map.js | 58 +- src/components/plugin/getBasemapConfig.js | 12 +- src/constants/actionTypes.js | 4 +- src/constants/layers.js | 6 + src/loaders/eventLoader.js | 2 +- src/loaders/geoJsonUrlLoader.js | 130 ++++ src/reducers/dataTable.js | 3 - src/reducers/featureProfile.js | 20 + src/reducers/index.js | 2 + src/reducers/layerEdit.js | 9 + src/reducers/map.js | 7 +- src/reducers/orgUnitProfile.js | 1 + src/reducers/ui.js | 2 + src/util/__tests__/external.spec.js | 143 +++- src/util/__tests__/favorites.spec.js | 345 +++++++++ src/util/__tests__/filter.spec.js | 50 ++ src/util/__tests__/geojson.spec.js | 366 +++++++++ src/util/__tests__/uid.spec.js | 65 ++ src/util/analytics.js | 2 +- src/util/app.js | 21 +- src/util/external.js | 39 +- src/util/favorites.js | 103 +-- src/util/filter.js | 4 +- src/util/geojson.js | 104 +++ src/util/helpers.js | 4 - src/util/uid.js | 28 + yarn.lock | 8 +- 80 files changed, 3408 insertions(+), 476 deletions(-) create mode 100644 cypress/fixtures/externalMapLayersWithGeojson.json create mode 100644 cypress/fixtures/feature.geojson create mode 100644 cypress/integration/layers/geojsonlayer.cy.js create mode 100644 public/images/featurelayer.png create mode 100644 src/components/edit/geoJson/GeoJsonDialog.js create mode 100644 src/components/edit/shared/FeatureStyle.js create mode 100644 src/components/feature/FeatureProfile.js create mode 100644 src/components/feature/styles/FeatureProfile.module.css create mode 100644 src/components/loaders/GeoJsonUrlLoader.js create mode 100644 src/components/map/layers/GeoJsonLayer.js create mode 100644 src/components/map/styles/FeatureButton.module.css create mode 100644 src/loaders/geoJsonUrlLoader.js create mode 100644 src/reducers/featureProfile.js create mode 100644 src/util/__tests__/filter.spec.js create mode 100644 src/util/__tests__/uid.spec.js create mode 100644 src/util/uid.js diff --git a/.eslintignore b/.eslintignore index 56a151075e..c32059fff5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,6 +2,7 @@ node_modules/ build/ webpack.config.js src/locales +cypress/fixtures/* # ide diff --git a/.github/workflows/dhis2-verify-app.yml b/.github/workflows/dhis2-verify-app.yml index 12e9c2d654..cf5fa157e8 100644 --- a/.github/workflows/dhis2-verify-app.yml +++ b/.github/workflows/dhis2-verify-app.yml @@ -7,6 +7,8 @@ on: branches: - 'master' - 'dev' + tags: + - '*' env: GIT_AUTHOR_NAME: '@dhis2-bot' @@ -98,7 +100,7 @@ jobs: if: | !github.event.push.repository.fork && github.actor != 'dependabot[bot]' && - github.ref == 'refs/heads/master' + (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev') steps: - uses: actions/checkout@v3 with: diff --git a/.prettierignore b/.prettierignore index d8051bbebe..c51c13ccca 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,7 +2,7 @@ node_modules .d2 src/locales build -cypress/fixtures/network +cypress/fixtures/* cypress.env.json docs colorbrewer.js diff --git a/cypress.config.js b/cypress.config.js index 0717507a89..d1e2ac60db 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -67,7 +67,7 @@ module.exports = defineConfig({ // Enabled to reduce the risk of out-of-memory issues experimentalMemoryManagement: true, // Set to a low number to reduce the risk of out-of-memory issues - numTestsKeptInMemory: 4, + numTestsKeptInMemory: 3, /* When allowing 1 retry on CI, the test suite will pass if * it's flaky. And/but we also get to identify flaky tests on the * Cypress Dashboard. */ diff --git a/cypress/fixtures/externalMapLayersWithGeojson.json b/cypress/fixtures/externalMapLayersWithGeojson.json new file mode 100644 index 0000000000..03e6e3fed4 --- /dev/null +++ b/cypress/fixtures/externalMapLayersWithGeojson.json @@ -0,0 +1,70 @@ +{ + "externalMapLayers": [ + { + "mapService": "XYZ", + "url": "https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}.png", + "attribution": "© OpenStreetMap, CARTO", + "imageFormat": "PNG", + "mapLayerPosition": "BASEMAP", + "id": "LOw2p0kPwua", + "name": " Dark basemap" + }, + { + "mapService": "GEOJSON_URL", + "url": "https://test.e2e.dhis2.org/dev-kfmt/api/routes/aa42a120003/run", + "imageFormat": "PNG", + "mapLayerPosition": "OVERLAY", + "id": "JYJjKIa0NsI", + "name": "Bo Catchment geojson" + }, + { + "mapService": "GEOJSON_URL", + "url": "https://test.e2e.dhis2.org/dev-kfmt/api/routes/xyz99910rst/run", + "imageFormat": "PNG", + "mapLayerPosition": "OVERLAY", + "id": "ZAxxTmtINN8", + "name": "CC Bo expired" + }, + { + "mapService": "GEOJSON_URL", + "url": "https://dhis2.github.io/maps-app/public/temp/BandajumaClinic.geojson", + "imageFormat": "PNG", + "mapLayerPosition": "OVERLAY", + "id": "ICSoBynB7s0", + "name": "Feature geojson" + }, + { + "mapService": "XYZ", + "url": "https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}.png", + "attribution": "© OpenStreetMap, CARTO", + "imageFormat": "PNG", + "mapLayerPosition": "OVERLAY", + "id": "suB1SFdc6RD", + "name": "Labels overlay" + }, + { + "mapService": "GEOJSON_URL", + "url": "https://dhis2.github.io/maps-app/public/temp/point-linestring-polygon.geojson", + "imageFormat": "PNG", + "mapLayerPosition": "OVERLAY", + "id": "dpV5peTqmNT", + "name": "Mixed geojson" + }, + { + "mapService": "GEOJSON_URL", + "url": "https://test.e2e.dhis2.org/dev-kfmt/api/routes/abc456789rt/run", + "imageFormat": "PNG", + "mapLayerPosition": "OVERLAY", + "id": "Z58y0pce12o", + "name": "Non-existing geojson" + }, + { + "mapService": "GEOJSON_URL", + "url": "https://dhis2.github.io/maps-app/public/temp/GbongbomaMCHP.geojson", + "imageFormat": "PNG", + "mapLayerPosition": "OVERLAY", + "id": "LYmVmzRxVAU", + "name": "Polygon only geojson" + } + ] +} \ No newline at end of file diff --git a/cypress/fixtures/feature.geojson b/cypress/fixtures/feature.geojson new file mode 100644 index 0000000000..92344ab0e4 --- /dev/null +++ b/cypress/fixtures/feature.geojson @@ -0,0 +1,702 @@ +{ + "geometry": { + "coordinates": [ + [ + [ + -11.738399, + 7.575499 + ], + [ + -11.731, + 7.5715 + ], + [ + -11.727199, + 7.5681 + ], + [ + -11.721199, + 7.5697 + ], + [ + -11.7136, + 7.571099 + ], + [ + -11.7078, + 7.573099 + ], + [ + -11.704999, + 7.5737 + ], + [ + -11.702099, + 7.573999 + ], + [ + -11.6978, + 7.5739 + ], + [ + -11.693599, + 7.573099 + ], + [ + -11.686, + 7.5695 + ], + [ + -11.683099, + 7.567199 + ], + [ + -11.680699, + 7.564299 + ], + [ + -11.6699, + 7.5479 + ], + [ + -11.665999, + 7.543799 + ], + [ + -11.662599, + 7.541199 + ], + [ + -11.653699, + 7.535399 + ], + [ + -11.649699, + 7.533699 + ], + [ + -11.6454, + 7.5329 + ], + [ + -11.641, + 7.5327 + ], + [ + -11.636599, + 7.5328 + ], + [ + -11.6323, + 7.533299 + ], + [ + -11.629499, + 7.5341 + ], + [ + -11.625799, + 7.5363 + ], + [ + -11.6178, + 7.542299 + ], + [ + -11.6144, + 7.546499 + ], + [ + -11.6108, + 7.549599 + ], + [ + -11.603799, + 7.5529 + ], + [ + -11.6011, + 7.5552 + ], + [ + -11.598799, + 7.559 + ], + [ + -11.594699, + 7.5687 + ], + [ + -11.590999, + 7.581 + ], + [ + -11.588899, + 7.5845 + ], + [ + -11.586099, + 7.5876 + ], + [ + -11.581699, + 7.591 + ], + [ + -11.5661, + 7.596799 + ], + [ + -11.5589, + 7.5963 + ], + [ + -11.554, + 7.5968 + ], + [ + -11.551, + 7.5982 + ], + [ + -11.5485, + 7.601 + ], + [ + -11.5475, + 7.603899 + ], + [ + -11.5471, + 7.606899 + ], + [ + -11.5472, + 7.6166 + ], + [ + -11.547699, + 7.623199 + ], + [ + -11.548299, + 7.627999 + ], + [ + -11.551199, + 7.639099 + ], + [ + -11.551399, + 7.643599 + ], + [ + -11.5507, + 7.647299 + ], + [ + -11.5491, + 7.650499 + ], + [ + -11.5461, + 7.653599 + ], + [ + -11.541199, + 7.656399 + ], + [ + -11.535899, + 7.6576 + ], + [ + -11.5211, + 7.665099 + ], + [ + -11.511399, + 7.6728 + ], + [ + -11.498799, + 7.6801 + ], + [ + -11.4902, + 7.686999 + ], + [ + -11.482899, + 7.6921 + ], + [ + -11.4744, + 7.699199 + ], + [ + -11.4691, + 7.702099 + ], + [ + -11.4644, + 7.703899 + ], + [ + -11.4578, + 7.703999 + ], + [ + -11.453899, + 7.702599 + ], + [ + -11.450599, + 7.700099 + ], + [ + -11.4477, + 7.6971 + ], + [ + -11.441099, + 7.6885 + ], + [ + -11.437799, + 7.6867 + ], + [ + -11.4344, + 7.6866 + ], + [ + -11.4315, + 7.687699 + ], + [ + -11.428899, + 7.6901 + ], + [ + -11.4256, + 7.6927 + ], + [ + -11.427799, + 7.6969 + ], + [ + -11.428299, + 7.699799 + ], + [ + -11.428499, + 7.7042 + ], + [ + -11.4281, + 7.708599 + ], + [ + -11.426999, + 7.7125 + ], + [ + -11.4199, + 7.724999 + ], + [ + -11.418499, + 7.7286 + ], + [ + -11.417799, + 7.7326 + ], + [ + -11.4174, + 7.740899 + ], + [ + -11.4169, + 7.744999 + ], + [ + -11.4147, + 7.754399 + ], + [ + -11.4134, + 7.762299 + ], + [ + -11.4112, + 7.769499 + ], + [ + -11.4106, + 7.775 + ], + [ + -11.411099, + 7.781099 + ], + [ + -11.412899, + 7.786899 + ], + [ + -11.4171, + 7.7954 + ], + [ + -11.421099, + 7.802699 + ], + [ + -11.424299, + 7.808 + ], + [ + -11.4253, + 7.812799 + ], + [ + -11.429899, + 7.814899 + ], + [ + -11.433599, + 7.815999 + ], + [ + -11.438299, + 7.815699 + ], + [ + -11.4419, + 7.814299 + ], + [ + -11.448899, + 7.8097 + ], + [ + -11.4564, + 7.806099 + ], + [ + -11.4592, + 7.805299 + ], + [ + -11.474299, + 7.8025 + ], + [ + -11.476499, + 7.802 + ], + [ + -11.4844, + 7.798299 + ], + [ + -11.4864, + 7.796799 + ], + [ + -11.4882, + 7.794999 + ], + [ + -11.4902, + 7.792299 + ], + [ + -11.492999, + 7.787 + ], + [ + -11.4952, + 7.784199 + ], + [ + -11.5064, + 7.773099 + ], + [ + -11.509499, + 7.7707 + ], + [ + -11.513199, + 7.7688 + ], + [ + -11.516, + 7.766899 + ], + [ + -11.5189, + 7.763899 + ], + [ + -11.5203, + 7.761799 + ], + [ + -11.5221, + 7.7581 + ], + [ + -11.524799, + 7.7555 + ], + [ + -11.5281, + 7.753899 + ], + [ + -11.5317, + 7.751899 + ], + [ + -11.5355, + 7.750199 + ], + [ + -11.5384, + 7.748099 + ], + [ + -11.543799, + 7.7431 + ], + [ + -11.546499, + 7.741 + ], + [ + -11.550399, + 7.739 + ], + [ + -11.5533, + 7.736999 + ], + [ + -11.5574, + 7.733099 + ], + [ + -11.575799, + 7.7145 + ], + [ + -11.5788, + 7.711099 + ], + [ + -11.5814, + 7.7072 + ], + [ + -11.5853, + 7.704499 + ], + [ + -11.591999, + 7.6984 + ], + [ + -11.594799, + 7.6963 + ], + [ + -11.598599, + 7.6943 + ], + [ + -11.6016, + 7.692199 + ], + [ + -11.6049, + 7.689099 + ], + [ + -11.613499, + 7.6804 + ], + [ + -11.6163, + 7.678099 + ], + [ + -11.625199, + 7.6733 + ], + [ + -11.6284, + 7.671999 + ], + [ + -11.633499, + 7.6695 + ], + [ + -11.6387, + 7.668399 + ], + [ + -11.644699, + 7.6658 + ], + [ + -11.6474, + 7.665399 + ], + [ + -11.655699, + 7.665 + ], + [ + -11.657799, + 7.6646 + ], + [ + -11.663499, + 7.6621 + ], + [ + -11.668599, + 7.661 + ], + [ + -11.674599, + 7.6584 + ], + [ + -11.678999, + 7.6575 + ], + [ + -11.681399, + 7.6567 + ], + [ + -11.685, + 7.654699 + ], + [ + -11.692499, + 7.6511 + ], + [ + -11.696, + 7.650499 + ], + [ + -11.7033, + 7.650299 + ], + [ + -11.706799, + 7.6495 + ], + [ + -11.709, + 7.648299 + ], + [ + -11.7131, + 7.644799 + ], + [ + -11.716899, + 7.640999 + ], + [ + -11.7175, + 7.637699 + ], + [ + -11.7193, + 7.631699 + ], + [ + -11.7197, + 7.627499 + ], + [ + -11.7201, + 7.614399 + ], + [ + -11.7206, + 7.611599 + ], + [ + -11.722699, + 7.6059 + ], + [ + -11.7261, + 7.601299 + ], + [ + -11.735799, + 7.5918 + ], + [ + -11.738699, + 7.5886 + ], + [ + -11.740799, + 7.584899 + ], + [ + -11.741399, + 7.580899 + ], + [ + -11.740799, + 7.5789 + ], + [ + -11.738399, + 7.575499 + ] + ] + ], + "type": "Polygon" + }, + "properties": { + "cc:admin:id": [ + "14" + ], + "cc:oBld:total": 667, + "cc:pop:fifteen-to-twenty-four": 3265.791450741782, + "cc:pop:men": 8636.58069940597, + "cc:pop:sixty-plus": 1326.3116628199657, + "cc:pop:total": 17901.467244250038, + "cc:pop:under-five": 3059.1391947507077, + "cc:pop:women": 9264.886544844068, + "cc:pop:women-fiften-to-forty-nine": 4404.74534986399, + "cc:id": "1", + "cc:Name": "Bandajuma Clinic CHC", + "cc:site": [ + -11.6522, + 7.5724 + ], + "user:parentName": "Sowa", + "user:code": "OU_260387", + "user:orgUnitId": "FNnj3jKGS7i", + "user:level": "4", + "user:parentId": "NqWaKXcg01b" + }, + "type": "Feature" +} \ No newline at end of file diff --git a/cypress/integration/dataTable.cy.js b/cypress/integration/dataTable.cy.js index 7f5717f498..8649e6b18b 100644 --- a/cypress/integration/dataTable.cy.js +++ b/cypress/integration/dataTable.cy.js @@ -133,7 +133,7 @@ describe('data table', () => { cy.getByDataTest('org-unit-profile').should('be.visible') }) - it('opens the data table for an Event layer', () => { + it.skip('opens the data table for an Event layer', () => { cy.visit('/', EXTENDED_TIMEOUT) const Layer = new EventLayer() diff --git a/cypress/integration/layers/geojsonlayer.cy.js b/cypress/integration/layers/geojsonlayer.cy.js new file mode 100644 index 0000000000..eae18cefc1 --- /dev/null +++ b/cypress/integration/layers/geojsonlayer.cy.js @@ -0,0 +1,219 @@ +// values for these constants comes from the externalMapLayersWithGeojson.json fixture +const OVERLAY_TITLE = 'Feature geojson' +const GEOJSON_URL = + 'https://dhis2.github.io/maps-app/public/temp/BandajumaClinic.geojson' + +Cypress.on('uncaught:exception', (err) => { + if ( + err.message.includes( + 'ResizeObserver loop completed with undelivered notifications.' + ) + ) { + return false + } +}) + +describe('GeoJSON URL Layer', () => { + it('adds a geojson url layer', () => { + cy.intercept('externalMapLayers?*', { + fixture: 'externalMapLayersWithGeojson.json', + }).as('externalMapLayers') + + cy.visit('/') + + cy.wait('@externalMapLayers') + + // add a geojson layer (provided by the fixture) + cy.getByDataTest('add-layer-button').click() + + const dataTest = `addlayeritem-${`${OVERLAY_TITLE}` + .toLowerCase() + .replace(/\s/g, '_')}` + + cy.getByDataTest(dataTest).click() + + cy.getByDataTest('dhis2-uicore-modaltitle') + .contains('Add new feature layer') + .should('be.visible') + + cy.intercept(GEOJSON_URL, { + fixture: 'feature.geojson', + }).as('geojson') + + cy.getByDataTest('layeredit').contains('Add layer').click() + + cy.wait('@geojson') + + // check that loading completes and the layer card is present + cy.getByDataTest('map-loading-mask').should('not.exist') + + cy.getByDataTest('layercard') + .contains(OVERLAY_TITLE) + .should('be.visible') + + cy.getByDataTest('load-error-noticebox').should('not.exist') + + // open the data table + cy.getByDataTest('moremenubutton').first().click() + + cy.getByDataTest('more-menu') + .find('li') + .not('.disabled') + .should('have.length', 5) + + cy.getByDataTest('more-menu') + .find('li') + .contains('Show data table') + .click() + + // check that the data table looks correct + cy.getByDataTest('bottom-panel').should('be.visible') + + cy.getByDataTest('bottom-panel') + .find('tbody') + .find('tr') + .should('have.length', 1) + + // open the feature panel by clicking on the row + cy.getByDataTest('bottom-panel') + .find('tbody') + .find('tr') + .find('td') + .first() + .click() + + // check that Feature profile is displayed + cy.getByDataTest('details-panel') + .findByDataTest('feature-profile-header') + .contains(OVERLAY_TITLE) + .should('be.visible') + }) + + it('handles a 404 error', () => { + cy.intercept('externalMapLayers?*', { + fixture: 'externalMapLayersWithGeojson.json', + }).as('externalMapLayers') + + cy.visit('/') + + cy.wait('@externalMapLayers') + + // add a geojson layer (provided by the fixture) + cy.getByDataTest('add-layer-button').click() + + const dataTest = `addlayeritem-${`${OVERLAY_TITLE}` + .toLowerCase() + .replace(/\s/g, '_')}` + + cy.getByDataTest(dataTest).click() + + cy.getByDataTest('dhis2-uicore-modaltitle') + .contains('Add new feature layer') + .should('be.visible') + + cy.intercept(GEOJSON_URL, { statusCode: 404 }).as('geojson') + + cy.getByDataTest('layeredit').contains('Add layer').click() + + cy.wait('@geojson') + + // check that loading completes and the layer card is present + cy.getByDataTest('map-loading-mask').should('not.exist') + + cy.getByDataTest('layercard') + .contains(OVERLAY_TITLE) + .should('be.visible') + + // check that an error is displayed in the layer card + cy.getByDataTest('load-error-noticebox').should('be.visible') + cy.getByDataTest('load-error-noticebox') + .find('h6') + .contains('Failed to load layer') + .should('be.visible') + cy.getByDataTest('dhis2-uicore-noticebox-content-message') + .contains( + 'There was a problem with this layer. Contact a system administrator.' + ) + .should('be.visible') + + // remove the layer + cy.getByDataTest('moremenubutton').first().click() + + cy.getByDataTest('more-menu') + .find('li') + .not('.disabled') + .should('have.length', 2) + + cy.getByDataTest('more-menu') + .find('li') + .contains('Remove layer') + .click() + + cy.getByDataTest('layercard').should('not.exist') + }) + + it('handles a 400 error', () => { + cy.intercept('externalMapLayers?*', { + fixture: 'externalMapLayersWithGeojson.json', + }).as('externalMapLayers') + + cy.visit('/') + + cy.wait('@externalMapLayers') + + // add a geojson layer (provided by the fixture) + cy.getByDataTest('add-layer-button').click() + + const dataTest = `addlayeritem-${`${OVERLAY_TITLE}` + .toLowerCase() + .replace(/\s/g, '_')}` + + cy.getByDataTest(dataTest).click() + + cy.getByDataTest('dhis2-uicore-modaltitle') + .contains('Add new feature layer') + .should('be.visible') + + cy.intercept(GEOJSON_URL, { + statusCode: 400, + }).as('geojson') + + cy.getByDataTest('layeredit').contains('Add layer').click() + + cy.wait('@geojson') + + // check that loading completes and the layer card is present + cy.getByDataTest('map-loading-mask').should('not.exist') + + cy.getByDataTest('layercard') + .contains(OVERLAY_TITLE) + .should('be.visible') + + // check that an error is displayed in the layer card + cy.getByDataTest('load-error-noticebox').should('be.visible') + cy.getByDataTest('load-error-noticebox') + .find('h6') + .contains('Failed to load layer') + .should('be.visible') + cy.getByDataTest('dhis2-uicore-noticebox-content-message') + .contains( + 'There was a problem with this layer. Contact a system administrator.' + ) + .should('be.visible') + + // remove the layer + cy.getByDataTest('moremenubutton').first().click() + + cy.getByDataTest('more-menu') + .find('li') + .not('.disabled') + .should('have.length', 2) + + cy.getByDataTest('more-menu') + .find('li') + .contains('Remove layer') + .click() + + cy.getByDataTest('layercard').should('not.exist') + }) +}) diff --git a/cypress/integration/systemsettings.cy.js b/cypress/integration/systemsettings.cy.js index 723101f334..52fd458858 100644 --- a/cypress/integration/systemsettings.cy.js +++ b/cypress/integration/systemsettings.cy.js @@ -63,37 +63,6 @@ describe('systemSettings', () => { .should('be.visible') }) - it('does not include Bing basemaps if no Bing api key', () => { - cy.intercept(SYSTEM_SETTINGS_ENDPOINT, (req) => { - delete req.headers['if-none-match'] - req.continue((res) => { - delete res.body.keyBingMapsApiKey - - res.send({ - body: res.body, - }) - }) - }) - - cy.visit('/') - - cy.getByDataTest('basemaplistitem-name') - .contains('Bing Road') - .should('not.exist') - }) - - it('includes Bing basemaps when Bing api key present', () => { - cy.visit('/') - - cy.getByDataTest('basemaplist', EXTENDED_TIMEOUT) - .children() - .should('have.length.greaterThan', 5) - - cy.getByDataTest('basemaplistitem-name') - .contains('Bing Road') - .should('be.visible') - }) - it('uses Last 6 months as default relative period', () => { // set relative period to 6 months cy.intercept(SYSTEM_SETTINGS_ENDPOINT, (req) => { diff --git a/i18n/en.pot b/i18n/en.pot index d94bc0c2a8..f94749c03a 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-02-21T10:12:14.838Z\n" -"PO-Revision-Date: 2024-02-21T10:12:14.838Z\n" +"POT-Creation-Date: 2024-03-15T10:46:33.764Z\n" +"PO-Revision-Date: 2024-03-15T10:46:33.764Z\n" msgid "Untitled map, {{date}}" msgstr "Untitled map, {{date}}" @@ -143,6 +143,22 @@ msgstr "Something went wrong" msgid "Search" msgstr "Search" +msgid "Data table is not supported when events are grouped on the server." +msgstr "Data table is not supported when events are grouped on the server." + +msgid "No valid data was found for the current layer configuration." +msgstr "No valid data was found for the current layer configuration." + +msgid "" +"Data table is not supported when there is more than one geometry type in " +"the dataset." +msgstr "" +"Data table is not supported when there is more than one geometry type in " +"the dataset." + +msgid "No valid data fields were found for this layer." +msgstr "No valid data fields were found for this layer." + msgid "Index" msgstr "Index" @@ -170,15 +186,6 @@ msgstr "Org unit" msgid "Event time" msgstr "Event time" -msgid "Data table is not supported when events are grouped on the server." -msgstr "Data table is not supported when events are grouped on the server." - -msgid "No valid data was found for the current layer configuration." -msgstr "No valid data was found for the current layer configuration." - -msgid "Data table is not supported for this layer type." -msgstr "Data table is not supported for this layer type." - msgid "Items" msgstr "Items" @@ -263,6 +270,9 @@ msgstr "org unit" msgid "Earth Engine" msgstr "Earth Engine" +msgid "feature" +msgstr "feature" + msgid "Edit {{name}} layer" msgstr "Edit {{name}} layer" @@ -394,6 +404,18 @@ msgstr "Radius in meters" msgid "Buffer can't be combined with associated geometry." msgstr "Buffer can't be combined with associated geometry." +msgid "Fill color" +msgstr "Fill color" + +msgid "Line/stroke color" +msgstr "Line/stroke color" + +msgid "Line/stroke width" +msgstr "Line/stroke width" + +msgid "Line/stroke width must be between 0-10." +msgstr "Line/stroke width must be between 0-10." + msgid "Labels" msgstr "Labels" @@ -502,6 +524,12 @@ msgstr "No relationship types were found for tracked entity type {{type}}" msgid "Relationship type" msgstr "Relationship type" +msgid "No data to show for this feature." +msgstr "No data to show for this feature." + +msgid "Feature profile" +msgstr "Feature profile" + msgid "Remove filter" msgstr "Remove filter" @@ -557,6 +585,9 @@ msgstr "External layer" msgid "Split view can not be combined with other layer types." msgstr "Split view can not be combined with other layer types." +msgid "Failed to load layer" +msgstr "Failed to load layer" + msgid "Loading layer" msgstr "Loading layer" @@ -569,15 +600,18 @@ msgstr "Edit" msgid "Toggle visibility" msgstr "Toggle visibility" +msgid "Layer is invalid" +msgstr "Layer is invalid" + msgid "Set layer opacity" msgstr "Set layer opacity" -msgid "Toggle layer menu" -msgstr "Toggle layer menu" - msgid "More actions" msgstr "More actions" +msgid "Toggle layer menu" +msgstr "Toggle layer menu" + msgid "Hide data table" msgstr "Hide data table" @@ -605,6 +639,9 @@ msgstr "Group" msgid "Filters" msgstr "Filters" +msgid "{{layername}}: {{message}}" +msgstr "{{layername}}: {{message}}" + msgid "Drill up one level" msgstr "Drill up one level" @@ -1301,6 +1338,21 @@ msgstr "Facilities" msgid "Facilities: No coordinates found" msgstr "Facilities: No coordinates found" +msgid "There was a problem with this layer. Contact a system administrator." +msgstr "There was a problem with this layer. Contact a system administrator." + +msgid "Feature" +msgstr "Feature" + +msgid "Polygon" +msgstr "Polygon" + +msgid "Line" +msgstr "Line" + +msgid "Point" +msgstr "Point" + msgid "Organisation units" msgstr "Organisation units" diff --git a/package.json b/package.json index 9e3741499c..40b65c4fd1 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "@dhis2/app-service-alerts": "3.9.4", "@dhis2/app-service-datastore": "^1.0.0-beta.3", "@dhis2/d2-i18n": "^1.1.3", - "@dhis2/maps-gl": "^3.9.0", + "@dhis2/maps-gl": "^3.9.1", "@dhis2/ui": "^9.2.0", "@krakenjs/post-robot": "^11.0.0", "@reportportal/agent-js-cypress": "git+https://github.com/dhis2/agent-js-cypress.git#develop", diff --git a/public/images/featurelayer.png b/public/images/featurelayer.png new file mode 100644 index 0000000000000000000000000000000000000000..05f634685584f053207278bda7a87ad27b6ffcea GIT binary patch literal 13967 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 literal 0 HcmV?d00001 diff --git a/src/actions/dataTable.js b/src/actions/dataTable.js index e904d455c5..392ebadf48 100644 --- a/src/actions/dataTable.js +++ b/src/actions/dataTable.js @@ -1,10 +1,5 @@ import * as types from '../constants/actionTypes.js' -export const openDataTable = (id) => ({ - type: types.DATA_TABLE_OPEN, - id, -}) - export const closeDataTable = () => ({ type: types.DATA_TABLE_CLOSE, }) diff --git a/src/actions/feature.js b/src/actions/feature.js index 5d217d969d..4c54bdc3ca 100644 --- a/src/actions/feature.js +++ b/src/actions/feature.js @@ -4,3 +4,12 @@ export const highlightFeature = (payload) => ({ type: types.FEATURE_HIGHLIGHT, payload, }) + +export const setFeatureProfile = (payload) => ({ + type: types.FEATURE_PROFILE_SET, + payload, +}) + +export const closeFeatureProfile = () => ({ + type: types.FEATURE_PROFILE_CLOSE, +}) diff --git a/src/actions/layerEdit.js b/src/actions/layerEdit.js index 80037f5485..27799293c3 100644 --- a/src/actions/layerEdit.js +++ b/src/actions/layerEdit.js @@ -358,3 +358,9 @@ export const setNoDataColor = (color) => ({ type: types.LAYER_EDIT_NO_DATA_COLOR_SET, payload: color, }) + +// Set feature style +export const setFeatureStyle = (payload) => ({ + type: types.LAYER_EDIT_FEATURE_STYLE_SET, + payload, +}) diff --git a/src/components/app/DetailsPanel.js b/src/components/app/DetailsPanel.js index a5dee646a4..89d5c5e33c 100644 --- a/src/components/app/DetailsPanel.js +++ b/src/components/app/DetailsPanel.js @@ -2,6 +2,7 @@ import cx from 'classnames' import PropTypes from 'prop-types' import React from 'react' import { useSelector } from 'react-redux' +import FeatureProfile from '../feature/FeatureProfile.js' import Interpretations from '../interpretations/Interpretations.js' import OrgUnitProfile from '../orgunits/OrgUnitProfile.js' import styles from './styles/DetailsPanel.module.css' @@ -9,18 +10,32 @@ import styles from './styles/DetailsPanel.module.css' const DetailsPanel = ({ interpretationsRenderCount }) => { const detailsPanelOpen = useSelector((state) => state.ui.rightPanelOpen) const viewOrgUnitProfile = useSelector((state) => state.orgUnitProfile) + const viewFeatureProfile = useSelector((state) => !!state.featureProfile) const interpretationId = useSelector((state) => state.interpretation?.id) const getContent = () => { - if (interpretationId || (detailsPanelOpen && !viewOrgUnitProfile)) { + if ( + interpretationId || + (detailsPanelOpen && !viewOrgUnitProfile && !viewFeatureProfile) + ) { return } - return detailsPanelOpen ? : null + if (detailsPanelOpen) { + if (viewOrgUnitProfile) { + return + } + if (viewFeatureProfile) { + return + } + } + + return null } return (
{ defaultBasemapId: defaultBasemap, }) - if (config.mapViews) { - config.mapViews.forEach((view) => delete view.id) - } - await putMap({ id: map.id, data: config, @@ -115,10 +111,6 @@ const FileMenu = ({ onFileMenuAction }) => { delete data.id - if (data.mapViews) { - data.mapViews.forEach((view) => delete view.id) - } - const res = await postMap({ data }) if (res.status === 'OK') { diff --git a/src/components/app/__tests__/Detailspanel.spec.js b/src/components/app/__tests__/Detailspanel.spec.js index 6d6110eefe..2ad70e5cbc 100644 --- a/src/components/app/__tests__/Detailspanel.spec.js +++ b/src/components/app/__tests__/Detailspanel.spec.js @@ -22,121 +22,113 @@ jest.mock( } ) -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 }, +jest.mock( + '../../feature/FeatureProfile.js', + () => + function MockFeatureProfile() { + return
Feature Profile
} +) - const { container } = render( - - - - ) - expect(container).toMatchSnapshot() - }) - test('renders InterpretationsPanel when has interpretationId, no orgUnitProfile and panel is open', () => { - const store = { - interpretation: { id: 'abc123' }, +const interpretation = { id: 'myinterpretation' } +const orgUnitProfile = 'myorgunit' +const featureProfile = 'myfeature' +const uiWithPanelOpen = { rightPanelOpen: true } +const uiWithPanelClosed = { rightPanelOpen: false } + +const testCases = [ + { + store: { + interpretation, + orgUnitProfile, + featureProfile, + ui: uiWithPanelOpen, + }, + expected: 'Interpretations', + }, + { + store: { + interpretation, + orgUnitProfile, + featureProfile, + ui: uiWithPanelClosed, + }, + expected: 'Interpretations', + }, + { + store: { + interpretation, 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' }, + featureProfile: null, + ui: uiWithPanelOpen, + }, + expected: 'Interpretations', + }, + { + store: { + interpretation, 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 = { + featureProfile: null, + ui: uiWithPanelClosed, + }, + expected: 'Interpretations', + }, + { + 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 = { + orgUnitProfile: null, + featureProfile: null, + ui: uiWithPanelOpen, + }, + expected: 'Interpretations', + }, + { + 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 = { + orgUnitProfile, + featureProfile, + ui: uiWithPanelOpen, + }, + expected: 'OrgUnitProfile', + }, + { + 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 = { + featureProfile, + ui: uiWithPanelOpen, + }, + expected: 'FeatureProfile', + }, + { + store: { + interpretation: {}, + orgUnitProfile, + featureProfile: null, + ui: uiWithPanelClosed, + }, + expected: 'null', + }, + { + store: { interpretation: {}, orgUnitProfile: null, - ui: { rightPanelOpen: false }, - } + featureProfile: null, + ui: uiWithPanelClosed, + }, + expected: 'null', + }, +] - const { container } = render( - - - - ) - expect(container).toMatchSnapshot() +describe('DetailsPanel', () => { + testCases.forEach(({ store, expected }) => { + test(`renders ${expected} when interpretation=${store.interpretation.id}, orgUnitProfile=${store.orgUnitProfile}, featureProfile=${store.featureProfile} and panelOpen=${store.ui.rightPanelOpen}`, () => { + 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 index b45d254cec..20f399498f 100644 --- a/src/components/app/__tests__/__snapshots__/Detailspanel.spec.js.snap +++ b/src/components/app/__tests__/__snapshots__/Detailspanel.spec.js.snap @@ -1,9 +1,23 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`DetailsPanel renders InterpretationsPanel when has interpretationId, has orgUnitProfile and panel is closed 1`] = ` +exports[`DetailsPanel renders FeatureProfile when interpretation=undefined, orgUnitProfile=null, featureProfile=myfeature and panelOpen=true 1`] = ` +
+
+
+ Feature Profile +
+
+
+`; + +exports[`DetailsPanel renders Interpretations when interpretation=myinterpretation, orgUnitProfile=myorgunit, featureProfile=myfeature and panelOpen=false 1`] = `