Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
778689a
feat: add heatmap layer
BRaimbault Feb 17, 2025
8b1020d
chore: fix lint error
BRaimbault Feb 17, 2025
1c4bfe6
Merge branch 'master' into feat/DHIS2-240
BRaimbault Feb 21, 2025
41859c3
Merge branch 'master' into feat/DHIS2-240
BRaimbault Mar 3, 2025
bacf25c
Merge branch 'master' into feat/DHIS2-240
BRaimbault Apr 4, 2025
c907eb6
Merge branch 'master' into feat/DHIS2-240
BRaimbault Apr 24, 2025
9174bf7
Merge branch 'master' into feat/DHIS2-240
BRaimbault May 8, 2025
331ea3f
Merge branch 'master' into feat/DHIS2-240
BRaimbault May 9, 2025
28d47f1
fix: update date.js
BRaimbault Jun 11, 2025
8d0e9fe
fix: prevent text from being highlighted while dragging map layer car…
jenniferarnesen Jun 13, 2025
016d47d
Merge branch 'fix/june-2025-releases' into fix/DHIS2-19717
BRaimbault Jun 13, 2025
8532c3c
fix: bump @dhis2/ui dependency
BRaimbault Jun 16, 2025
0e6f1df
fix: adjust Calendar width
BRaimbault Jun 19, 2025
2c8024f
fix: give user with ALL authority access to layer sources management …
BRaimbault Jun 25, 2025
fc48a83
fix: update split map help text [DHIS2-19718] (#3546)
BRaimbault Jun 25, 2025
56414cf
Merge branch 'fix/june-2025-releases' into fix/DHIS2-19717
BRaimbault Jun 25, 2025
526d166
fix: custom CalendarInput
BRaimbault Jun 25, 2025
eb005b8
Merge branch 'master' into fix/june-2025-releases
BRaimbault Jun 26, 2025
b5bd189
Merge branch 'fix/june-2025-releases' into fix/DHIS2-19717
BRaimbault Jun 26, 2025
480fc99
Merge branch 'master' into fix/DHIS2-19717
BRaimbault Jul 21, 2025
a2d5e77
Merge remote-tracking branch 'origin/master' into fix/DHIS2-19717
BRaimbault Aug 19, 2025
652459c
fix: yarn.lock update
BRaimbault Aug 19, 2025
e00187d
fix: file format error
BRaimbault Aug 19, 2025
db5fdfe
chore: yarn lock rebase and ui bump
BRaimbault Sep 8, 2025
4eaeb08
chore: update cypress tests
BRaimbault Sep 8, 2025
4163b1c
chore: update cypress tests
BRaimbault Sep 9, 2025
72d22eb
chore: update cypress tests
BRaimbault Sep 9, 2025
0d5ed07
chore: update cypress tests
BRaimbault Sep 9, 2025
fa93495
chore: update cypress tests
BRaimbault Sep 9, 2025
805a52e
fix: first date edit ignored
BRaimbault Sep 9, 2025
969c819
chore: update cypress tests
BRaimbault Sep 9, 2025
de8a012
chore: update cypress tests
BRaimbault Sep 9, 2025
81b9dbc
chore: update cypress tests
BRaimbault Sep 9, 2025
679d388
chore: update cypress tests
BRaimbault Sep 9, 2025
ba3fedf
fix: dates clear error on event layer
BRaimbault Sep 11, 2025
5229658
Merge remote-tracking branch 'origin/fix/DHIS2-19717' into feat/DHIS2…
BRaimbault Sep 11, 2025
a17368c
fix: update maps-gl dependency
BRaimbault Sep 11, 2025
247c81c
feat: dynamic heatmap props
BRaimbault Sep 12, 2025
450b812
Merge branch 'master' into feat/DHIS2-240
BRaimbault Sep 15, 2025
6508feb
feat: heat layer config update
BRaimbault Sep 15, 2025
9e4c8b1
fix: lint error
BRaimbault Sep 15, 2025
821b0d5
chore: update requestsErrors
BRaimbault Sep 29, 2025
7888a67
chore: add option to filter cypress test files
BRaimbault Sep 29, 2025
7bdfd2e
chore: generate only one group
BRaimbault Sep 29, 2025
d85f0d4
chore: update requestsErrors.cy.js
BRaimbault Sep 29, 2025
6b19248
chore: fix lint error
BRaimbault Sep 29, 2025
df96f2a
chore: single test
BRaimbault Sep 29, 2025
5210475
chore: extend timeout
BRaimbault Sep 29, 2025
eb01afe
chore: skip getThematic_Analytics1 test
BRaimbault Sep 29, 2025
9fe1d8c
chore: clear cookies and local storage between steps
BRaimbault Sep 29, 2025
56b1bcf
chore: change order
BRaimbault Sep 29, 2025
e181486
chore: restore all requestErrors tests
BRaimbault Sep 30, 2025
6551d71
chore: fix requestsErrors tests
BRaimbault Sep 30, 2025
9d1c0b1
chore: set number of groups to 1
BRaimbault Sep 30, 2025
7d416a8
chore: update generateTestMatrix
BRaimbault Sep 30, 2025
bbc2676
chore: update generateTestMatrix
BRaimbault Sep 30, 2025
7552d17
chore: update generateTestMatrix
BRaimbault Sep 30, 2025
7f7c511
chore: update requestsErrors
BRaimbault Sep 30, 2025
81f747d
chore: update requestsErrors
BRaimbault Sep 30, 2025
c431512
chore: update requestsErrors
BRaimbault Sep 30, 2025
e14548a
chore: update requestsErrors
BRaimbault Sep 30, 2025
4970269
chore: update requestsErrors
BRaimbault Sep 30, 2025
0e7b124
chore: update requestsErrors
BRaimbault Sep 30, 2025
332bca0
chore: update requestsErrors
BRaimbault Sep 30, 2025
4bb18af
chore: update requestsErrors
BRaimbault Sep 30, 2025
63db80d
chore: update requestsErrors
BRaimbault Sep 30, 2025
213d84f
chore: update requestsErrors
BRaimbault Oct 3, 2025
c734507
chore: update generateTestMatrix.js
BRaimbault Oct 3, 2025
a21a77f
Merge branch 'chore/cypress_tests_update' into feat/DHIS2-240
BRaimbault Oct 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 31 additions & 41 deletions cypress/integration/requestsErrors.cy.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
import { getRequest } from '../support/requests.js'
import {
assertIntercepts,
EXTENDED_TIMEOUT,
assertIntercepts,
getDhis2Version,
} from '../support/util.js'

const clearAndLogin = () => {
cy.clearCookies()
cy.clearLocalStorage()

const username = Cypress.env('dhis2Username')
const password = Cypress.env('dhis2Password')
const baseUrl = Cypress.env('dhis2BaseUrl')

cy.loginByApi({ username, password, baseUrl })
.its('status')
.should('equal', 200)

cy.wait(100) // eslint-disable-line cypress/no-unnecessary-waiting
}

const commonTriggerFn = () => {
clearAndLogin()
cy.reload(true)
}

Expand All @@ -16,7 +32,7 @@ describe('Error handling check for all layer types', () => {
assertIntercepts({
intercepts: [getRequest('getMap', id)],
commonTriggerFn: () => {
cy.visit(`#/${id}`)
cy.visit(`/?id=${id}`)
},
})
})
Expand All @@ -43,41 +59,13 @@ describe('Error handling check for all layer types', () => {
.should('be.visible')
}

cy.visit(`#/${id}`)
cy.visit(`/?id=${id}`)

assertIntercepts({
intercepts: [
{
intercepts: [
{
...getRequest('getThematic_Analytics1'),
error: 'network',
},
{
...getRequest('getThematic_Analytics2'),
error: 'network',
},
],
alias: 'getThematic_AnalyticsGroup',
assertFn: () => {
commonAssertFn({ error: 'network' })
},
},
{
intercepts: [
{
...getRequest('getThematic_Analytics1'),
error: 409,
},
{
...getRequest('getThematic_Analytics2'),
error: 'network',
},
],
alias: 'getThematic_AnalyticsGroup',
assertFn: () => {
commonAssertFn({ error: 409 })
},
...getRequest('getThematic_Analytics1'),
errors: ['network', 409],
},
{
...getRequest('getThematic_Analytics2'),
Expand Down Expand Up @@ -120,7 +108,7 @@ describe('Error handling check for all layer types', () => {
.should('be.visible')
}

cy.visit(`#/${id}`)
cy.visit(`/?id=${id}`)

assertIntercepts({
intercepts: [
Expand Down Expand Up @@ -238,14 +226,15 @@ describe('Error handling check for all layer types', () => {
.should('be.visible')
}

cy.visit(`#/${id}`)
cy.visit(`/?id=${id}`)

assertIntercepts({
intercepts: [
{
...getRequest('getEventsStandard_Analytics1'),
triggerFn: () => {
cy.reload(true)
clearAndLogin()
cy.visit(`/?id=${id}`)
cy.wait(10000) // eslint-disable-line cypress/no-unnecessary-waiting
},
errors: ['network', 409], // !TODO: Improve messages
Expand All @@ -263,7 +252,8 @@ describe('Error handling check for all layer types', () => {
{
...getRequest('getEventsStandard_Analytics2'),
triggerFn: () => {
cy.reload(true)
clearAndLogin()
cy.visit(`/?id=${id}`)
cy.getByDataTest('layercard')
.find('[data-test="layerlegend"]', EXTENDED_TIMEOUT)
.should('exist')
Expand Down Expand Up @@ -311,7 +301,7 @@ describe('Error handling check for all layer types', () => {
.should('be.visible')
}

cy.visit(`#/${id}`)
cy.visit(`/?id=${id}`)

const serverVersion = getDhis2Version()
let layerSpecificRequests
Expand Down Expand Up @@ -376,7 +366,7 @@ describe('Error handling check for all layer types', () => {
// E2E - Facilities Layer [kIAUN3dInEz]
const id = 'kIAUN3dInEz'

cy.visit(`#/${id}`)
cy.visit(`/?id=${id}`)

const commonAssertFn = ({ error }) => {
const errorMessage = {
Expand Down Expand Up @@ -435,7 +425,7 @@ describe('Error handling check for all layer types', () => {
.should('be.visible')
}

cy.visit(`#/${id}`)
cy.visit(`/?id=${id}`)

assertIntercepts({
intercepts: [
Expand Down Expand Up @@ -483,7 +473,7 @@ describe('Error handling check for all layer types', () => {
.should('be.visible')
}

cy.visit(`#/${id}`)
cy.visit(`/?id=${id}`)

assertIntercepts({
intercepts: [
Expand Down
138 changes: 129 additions & 9 deletions cypress/support/generateTestMatrix.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,155 @@
const fs = require('fs')
const path = require('path')

const getAllFiles = (dirPath, arrayOfFiles = []) => {
const NUMBER_OF_GROUPS = 5
const CYPRESS_FILES = {
'cypress/integration/basemaps.cy.js': { include: true, duration: 60 },
'cypress/integration/dataDownload.cy.js': { include: true, duration: 60 },
'cypress/integration/dataTable.cy.js': { include: true, duration: 60 },
'cypress/integration/fetcherrors.cy.js': { include: true, duration: 30 },
'cypress/integration/filemenu.cy.js': { include: true, duration: 90 },
'cypress/integration/interpretations.cy.js': {
include: true,
duration: 45,
},
'cypress/integration/keyboard.cy.js': { include: true, duration: 30 },
'cypress/integration/manageLayerSources.cy.js': {
include: true,
duration: 45,
},
'cypress/integration/mapDownload.cy.js': { include: true, duration: 15 },
'cypress/integration/orgUnitInfo.cy.js': { include: true, duration: 15 },
'cypress/integration/plugin.cy.js': { include: true, duration: 15 },
'cypress/integration/pushAnalytics.cy.js': { include: true, duration: 15 },
'cypress/integration/requests.cy.js': { include: true, duration: 120 },
'cypress/integration/requestsErrors.cy.js': {
include: false,
duration: 480,
},
'cypress/integration/routes.cy.js': { include: true, duration: 120 },
'cypress/integration/systemsettings.cy.js': {
include: true,
duration: 45,
},
'cypress/integration/ui.cy.js': { include: true, duration: 30 },
'cypress/integration/usersettings.cy.js': { include: true, duration: 45 },
'cypress/integration/layers/eventlayer.cy.js': {
include: true,
duration: 210,
},
'cypress/integration/layers/multilayers.cy.js': {
include: true,
duration: 15,
},
'cypress/integration/layers/externallayer.cy.js': {
include: true,
duration: 15,
},
'cypress/integration/layers/orgunitlayer.cy.js': {
include: true,
duration: 30,
},
'cypress/integration/layers/facilitylayer.cy.js': {
include: true,
duration: 30,
},
'cypress/integration/layers/thematiclayer.cy.js': {
include: true,
duration: 240,
},
'cypress/integration/layers/geojsonlayer.cy.js': {
include: true,
duration: 30,
},
'cypress/integration/layers/trackedentitylayer.cy.js': {
include: true,
duration: 60,
},
}

const filterFn = (fullPath) =>
CYPRESS_FILES[fullPath] ? CYPRESS_FILES[fullPath].include : true // include files by default

const getAllFiles = (dirPath, arrayOfFiles = [], filterFn = () => true) => {
const files = fs.readdirSync(dirPath)

files.forEach((file) => {
if (fs.statSync(path.join(dirPath, file)).isDirectory()) {
arrayOfFiles = getAllFiles(path.join(dirPath, file), arrayOfFiles)
} else if (path.extname(file) === '.js') {
arrayOfFiles.push(path.join(dirPath, file))
const fullPath = path.join(dirPath, file)
const stats = fs.statSync(fullPath)

if (stats.isDirectory()) {
arrayOfFiles = getAllFiles(fullPath, arrayOfFiles, filterFn)
} else if (path.extname(file) === '.js' && filterFn(fullPath)) {
arrayOfFiles.push(fullPath)
}
})

return arrayOfFiles
}

const createGroups = (files, numberOfGroups = 5) => {
const groups = []
const createGroupsStandard = (files, numberOfGroups = NUMBER_OF_GROUPS) => {
let groups = []
for (let i = 0; i < numberOfGroups; i++) {
groups.push([])
}

files.forEach((file, index) => {
groups[index % numberOfGroups].push(file)
})
groups = groups.map((group, index) => ({ id: index + 1, tests: group }))
return groups
}

const createGroupsByDuration = (files, numberOfGroups = NUMBER_OF_GROUPS) => {
const durations = Object.values(CYPRESS_FILES)
.map((f) => f.duration)
.filter((d) => typeof d === 'number' && !isNaN(d))
const avgDuration =
durations.reduce((sum, d) => sum + d, 0) / durations.length

const enriched = files.map((f) => {
const meta = CYPRESS_FILES[f] ?? {}
return {
file: f,
duration:
typeof meta.duration === 'number' ? meta.duration : avgDuration,
}
})
// Sort longest duration first (greedy assignment works better this way)
enriched.sort((a, b) => b.duration - a.duration)

const groups = Array.from({ length: numberOfGroups }, (_, i) => ({
id: i + 1,
tests: [],
totalDuration: 0,
}))
for (const f of enriched) {
groups.sort((a, b) => a.totalDuration - b.totalDuration)
groups[0].tests.push(f.file)
groups[0].totalDuration += f.duration
}
return groups
}

return groups.map((group, index) => ({ id: index + 1, tests: group }))
const createGroups = (files, numberOfGroups = NUMBER_OF_GROUPS) => {
if (!files.length || numberOfGroups < 1) {
return []
}

const durations = Object.values(CYPRESS_FILES)
.map((f) => f.duration)
.filter((d) => typeof d === 'number' && !isNaN(d))
const adjustedNumberOfGroups = Math.min(files.length, numberOfGroups)

if (durations.length === 0) {
return createGroupsStandard(files, adjustedNumberOfGroups)
} else {
return createGroupsByDuration(files, adjustedNumberOfGroups)
}
}

const cypressSpecsPath = './cypress/integration'
const specs = getAllFiles(cypressSpecsPath)
const specs = getAllFiles(cypressSpecsPath, [], filterFn)
const groupedSpecs = createGroups(specs)

console.log(JSON.stringify(groupedSpecs))
3 changes: 3 additions & 0 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,9 @@ msgstr "Group events"
msgid "View all events"
msgstr "View all events"

msgid "View heat map"
msgstr "View heat map"

msgid "Radius"
msgstr "Radius"

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"@dhis2/analytics": "^28.0.4",
"@dhis2/app-runtime": "^3.14.4",
"@dhis2/app-service-datastore": "^1.0.0-beta.3",
"@dhis2/maps-gl": "^4.1.2",
"@dhis2/maps-gl": "git+https://github.com/d2-ci/maps-gl.git#5d8967b0b70c49968c390edd29ef04b9576ae3c0",
"@dhis2/ui": "^10.9.0",
"@turf/centroid": "^6.5.0",
"abortcontroller-polyfill": "^1.7.5",
Expand Down
Binary file added public/images/heatmap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/actions/layerEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ export const setFallbackCoordinateField = (fieldId) => ({
fieldId,
})

// Set if event heatmap should be used (event)
export const setEventHeatmap = (checked) => ({
type: types.LAYER_EDIT_EVENT_HEATMAP_SET,
checked,
})

// Set if event clustering should be used (event)
export const setEventClustering = (checked) => ({
type: types.LAYER_EDIT_EVENT_CLUSTERING_SET,
Expand Down
14 changes: 14 additions & 0 deletions src/actions/layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ export const changeLayerOpacity = (id, opacity) => ({
opacity,
})

// Set overlay heat intensity
export const changeLayerIntensity = (id, heatIntensity) => ({
type: types.LAYER_CHANGE_INTENSITY,
id,
heatIntensity,
})

// Set overlay heat radius
export const changeLayerRadius = (id, heatRadius) => ({
type: types.LAYER_CHANGE_RADIUS,
id,
heatRadius,
})

// Change ordering of overlays
export const sortLayers = ({ oldIndex, newIndex }) => ({
type: types.LAYER_SORT,
Expand Down
Loading
Loading