Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7d6c5a1
chore: refactor EventDialog to functional component [DHIS2-18209]
BRaimbault Jun 18, 2026
ccd1c78
fix: prevent default relative period from re-applying after switching…
BRaimbault Jun 19, 2026
12a365d
Merge branch 'master' into chore/DHIS2-18209-EventDialog-v2
BRaimbault Jun 19, 2026
d3a0485
fix: forward both arguments to setEventCoordinateField to prevent inf…
BRaimbault Jun 19, 2026
34ed240
Merge branch 'master' into chore/DHIS2-18209-EventDialog-v2
BRaimbault Jun 22, 2026
f7ed9f7
Merge origin/master into feat/DHIS2-8165-v2
BRaimbault Jun 26, 2026
befc3af
fix: prevent duplicate datastore requests on load (#3696)
karolinelien Jun 26, 2026
357cc53
fix: remove north arrow rotate listener on cleanup (#3698)
karolinelien Jun 26, 2026
36f8f31
fix: prevent data table from crashing when cursor leaves browser wind…
karolinelien Jun 26, 2026
f57e515
fix: correct time period filtering in Earth Engine layers (#3697)
karolinelien Jun 26, 2026
1a3f267
fix: prevent crash when loading map with external layer (#3700)
karolinelien Jun 26, 2026
7ec0beb
chore(release): cut 101.13.4 [skip release]
dhis2-bot Jun 29, 2026
a65ae36
chore(deps): bump the dependencies group across 1 directory with 2 up…
dependabot[bot] Jun 29, 2026
4d52924
chore(deps): bump react-sortable-hoc from 1.11.0 to 2.0.0 (#3704)
dependabot[bot] Jun 29, 2026
eaa71c2
chore(deps): bump @dhis2/analytics from 29.4.1 to 29.5.4 (#3706)
dependabot[bot] Jun 29, 2026
dc0e02a
feat: add style by geometry source for event layers [DHIS2-8165]
BRaimbault Jun 29, 2026
70f2525
Merge branch 'master' into feat/DHIS2-8165-v2
BRaimbault Jun 29, 2026
5770420
Merge remote-tracking branch 'origin/master' into feat/DHIS2-8165-v2
BRaimbault Jul 10, 2026
3ad451f
chore: sonarqube issue
BRaimbault Jul 10, 2026
18416cd
Merge branch 'master' into feat/DHIS2-8165-v2
BRaimbault Jul 14, 2026
79599fe
Merge branch 'master' into feat/DHIS2-8165-v2
BRaimbault Sep 14, 2026
dc42644
feat: add fallback coordinate field for event layers [DHIS2-8165]
BRaimbault Sep 15, 2026
a7a1f44
fix: show display names instead of raw ids for custom geometry sources
BRaimbault Sep 17, 2026
c955fbb
fix: reset geometry-source style/label selection when fallback field …
BRaimbault Sep 17, 2026
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
99 changes: 87 additions & 12 deletions cypress/elements/event_layer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,63 @@
import { EXTENDED_TIMEOUT } from '../support/util.js'
import { Layer } from './layer.js'

// Waits for the popper's text to stabilize before clicking.
const clickStablePopperItem = (text) => {
let lastSignature = null
let stableCount = 0

cy.get(
'[data-test="dhis2-uicore-popper"]:visible',
EXTENDED_TIMEOUT
).should(($popper) => {
const signature = $popper.text()
if (signature === lastSignature) {
stableCount++
} else {
stableCount = 0
lastSignature = signature
}
expect(stableCount, 'popper content settled').to.be.at.least(1)
})

cy.get('[data-test="dhis2-uicore-popper"]:visible')
.containsExact(text)
.click()
}

const MAX_SELECT_ATTEMPTS = 3

// Selects targetText, then retries the whole open+click if it didn't take -
// clickStablePopperItem() narrows the detached-click race but doesn't close it.
const selectPopperOption = (
contentDataTest,
targetText,
attemptsLeft = MAX_SELECT_ATTEMPTS
) => {
cy.getByDataTest(contentDataTest, EXTENDED_TIMEOUT).then(($element) => {
if ($element.text().trim() === targetText) {
return
}

cy.getByDataTest(contentDataTest).click()
clickStablePopperItem(targetText)

cy.getByDataTest(contentDataTest).then(($after) => {
if ($after.text().trim() !== targetText) {
expect(
attemptsLeft,
`select "${targetText}" eventually took effect`
).to.be.greaterThan(1)
selectPopperOption(
contentDataTest,
targetText,
attemptsLeft - 1
)
}
})
})
}

export class EventLayer extends Layer {
selectProgram(program) {
cy.get('[data-test="programselect"]', EXTENDED_TIMEOUT).click()
Expand All @@ -21,19 +78,17 @@ export class EventLayer extends Layer {
cy.getByDataTest('coordinatefield-content', EXTENDED_TIMEOUT).should(
($el) => expect($el.text().trim().length).to.be.greaterThan(0)
)
selectPopperOption('coordinatefield-content', coordinate)

cy.getByDataTest('coordinatefield-content').then(($element) => {
// Check if the coordinate is already selected by looking at the text content
if ($element.text().trim() !== coordinate) {
cy.log('Select the coordinate')
cy.getByDataTest('coordinatefield-content').click()
cy.getByDataTest('dhis2-uicore-popper')
.containsExact(coordinate)
.click()
} else {
cy.log('Coordinate already selected, no action needed')
}
})
return this
}

selectFallbackCoordinate(coordinate) {
cy.getByDataTest(
'fallbackcoordinatefield-content',
EXTENDED_TIMEOUT
).should(($el) => expect($el.text().trim().length).to.be.greaterThan(0))
selectPopperOption('fallbackcoordinatefield-content', coordinate)

return this
}
Expand Down Expand Up @@ -76,4 +131,24 @@ export class EventLayer extends Layer {

return this
}

selectCountEventsWithoutCoordinates() {
cy.getByDataTest('eventdialog-styletab')
.contains('Count events without coordinates')
.click()

return this
}

selectLabelField(name) {
cy.getByDataTest('eventdialog-styletab').then(($tab) => {
if (!$tab.find('[data-test="label-field-select-content"]').length) {
cy.wrap($tab).contains('Labels').click()
}
})

selectPopperOption('label-field-select-content', name)

return this
}
}
4 changes: 4 additions & 0 deletions cypress/support/cypressFiles.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,9 @@
"cypress/integration/layers/trackedentitylayer.cy.js": {
"include": true,
"duration": 38
},
"cypress/integration/layers/eventCoordinateFallbackScenarios.cy.js": {
"include": false,
"note": "yarn cypress run --spec cypress/integration/layers/eventCoordinateFallbackScenarios.cy.js"
}
}
2 changes: 1 addition & 1 deletion cypress/support/requests.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const requests = {
},
getCachedDataProvider_SystemSettings: {
method: 'GET',
url: '**/systemSettings?key=keyAnalysisRelativePeriod,keyHideDailyPeriods,keyHideWeeklyPeriods,keyHideBiWeeklyPeriods,keyHideMonthlyPeriods,keyHideBiMonthlyPeriods,keyDefaultBaseMap,keyBingMapsApiKey,keyAzureMapsApiKey',
url: '**/systemSettings?key=keyAnalysisRelativePeriod,keyHideDailyPeriods,keyHideWeeklyPeriods,keyHideBiWeeklyPeriods,keyHideMonthlyPeriods,keyHideBiMonthlyPeriods,keyDefaultBaseMap,orgUnitCentroidsInEventsAnalytics,keyBingMapsApiKey,keyAzureMapsApiKey',
},
getCachedDataProvider_ExternalMapLayers: {
method: 'GET',
Expand Down
27 changes: 18 additions & 9 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -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: 2026-09-01T12:42:29.454Z\n"
"PO-Revision-Date: 2026-09-01T12:42:29.454Z\n"
"POT-Creation-Date: 2026-09-15T16:43:35.708Z\n"
"PO-Revision-Date: 2026-09-15T16:43:35.708Z\n"

msgid "2020"
msgstr "2020"
Expand Down Expand Up @@ -101,15 +101,15 @@ msgstr "Enrollment location"
msgid "Tracked entity location"
msgstr "Tracked entity location"

msgid "Select a program stage to see additional coordinate options"
msgstr "Select a program stage to see additional coordinate options"

msgid "Enrollment > event > tracked entity > org unit coordinate"
msgstr "Enrollment > event > tracked entity > org unit coordinate"
msgid "Event > enrollment > tracked entity > org unit coordinate"
msgstr "Event > enrollment > tracked entity > org unit coordinate"

msgid "Event > org unit coordinate"
msgstr "Event > org unit coordinate"

msgid "Select a program stage to see additional coordinate options"
msgstr "Select a program stage to see additional coordinate options"

msgid "Select a program to see additional coordinate options"
msgstr "Select a program to see additional coordinate options"

Expand All @@ -119,11 +119,14 @@ msgstr "Fallback coordinate field"
msgid "Coordinate field"
msgstr "Coordinate field"

msgid "Include events with no data"
msgstr "Include events with no data"

msgid "Include unclassified events"
msgstr "Include unclassified events"

msgid "Include events with no data"
msgstr "Include events with no data"
msgid "Geometry source"
msgstr "Geometry source"

msgid "Previously selected value not available in list: {{id}}"
msgstr "Previously selected value not available in list: {{id}}"
Expand Down Expand Up @@ -710,6 +713,9 @@ msgstr "Duplicate layer"
msgid "Remove layer"
msgstr "Remove layer"

msgid "fallback"
msgstr "fallback"

msgid "Filters"
msgstr "Filters"

Expand Down Expand Up @@ -1834,6 +1840,9 @@ msgstr "The event filter is not supported"
msgid "An unknown error occurred while reading layer data"
msgstr "An unknown error occurred while reading layer data"

msgid "The fallback coordinate field is not supported by this server version"
msgstr "The fallback coordinate field is not supported by this server version"

msgid "Displaying first {{pageSize}} events out of {{total}}"
msgstr "Displaying first {{pageSize}} events out of {{total}}"

Expand Down
9 changes: 8 additions & 1 deletion src/actions/layerEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ export const setBooleanStyle = (value, color) => ({
color,
})

export const setGeometrySourceStyle = (value, color) => ({
type: types.LAYER_EDIT_STYLE_DATA_ITEM_GEOMETRY_SOURCE_SET,
value,
color,
})

// Set thematic map type (choropleth, bubble map)
export const setThematicMapType = (type) => ({
type: types.LAYER_EDIT_THEMATIC_MAP_TYPE_SET,
Expand Down Expand Up @@ -116,9 +122,10 @@ export const setEventCoordinateField = (fieldId, fieldType) => ({
})

// Set fallback coordinate field
export const setFallbackCoordinateField = (fieldId) => ({
export const setFallbackCoordinateField = (fieldId, fieldType) => ({
type: types.LAYER_EDIT_FALLBACK_COORDINATE_FIELD_SET,
fieldId,
fieldType,
})

// Set if event clustering should be used (event)
Expand Down
76 changes: 45 additions & 31 deletions src/components/dataItem/CoordinateField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
EVENT_COORDINATE_CASCADING,
NONE,
} from '../../constants/layers.js'
import {
coordinateValueTypes,
ouValueTypes,
} from '../../constants/valueTypes.js'
import { serverSupportsGeometrySource } from '../../util/versionToggle.js'
import { SelectField } from '../core/index.js'
import { useEventDataItems } from './EventDataItemsProvider.jsx'

Expand All @@ -21,21 +26,29 @@
eventCoordinateField,
onChange,
className,
dataTest = 'coordinatefield',
}) => {

Check failure on line 30 in src/components/dataItem/CoordinateField.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 17 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=dhis2_maps-app&issues=AaCmBxnnCs0sTIMXErjd&open=AaCmBxnnCs0sTIMXErjd&pullRequest=3707
const { serverVersion } = useConfig()

// VERSION-TOGGLE
// https://dhis2.atlassian.net/browse/DHIS2-19010 and:
// - [2.40.8] https://github.com/dhis2/dhis2-core/commit/f2286a5aa70b2957bd24925776e9394cd67d44c1
// - [2.41.4] https://github.com/dhis2/dhis2-core/commit/19f29f27385cfae1c7fac234439f49987ec2abe4
// - [2.42.0] https://github.com/dhis2/dhis2-core/commit/e5b29f4f1dbee791be9e6befb8a304151a1661c9
const includeTypes = ['COORDINATE']
if (
const isFallback = !!eventCoordinateField

const includeTypes = [...coordinateValueTypes]
if (isFallback) {
// VERSION-TOGGLE: fallbackCoordinateField pointed at a custom
// ORGANISATION_UNIT field crashes pre-2.44 - see util/versionToggle.js
if (serverSupportsGeometrySource(serverVersion)) {
includeTypes.push(...ouValueTypes)
}
} else if (
// VERSION-TOGGLE
// https://dhis2.atlassian.net/browse/DHIS2-19010 and:
// - [2.40.8] https://github.com/dhis2/dhis2-core/commit/f2286a5aa70b2957bd24925776e9394cd67d44c1
// - [2.41.4] https://github.com/dhis2/dhis2-core/commit/19f29f27385cfae1c7fac234439f49987ec2abe4
// - [2.42.0] https://github.com/dhis2/dhis2-core/commit/e5b29f4f1dbee791be9e6befb8a304151a1661c9
(serverVersion.minor === 40 && serverVersion.patch >= 8) ||
(serverVersion.minor === 41 && serverVersion.patch >= 4) ||
serverVersion.minor >= 42
) {
includeTypes.push('ORGANISATION_UNIT')
includeTypes.push(...ouValueTypes)
}

const {
Expand All @@ -44,10 +57,12 @@
loading: itemsLoading,
} = useEventDataItems({ includeTypes })

const defaultValue = eventCoordinateField ? NONE : EVENT_COORDINATE_DEFAULT
const defaultValue = useMemo(
() => (eventCoordinateField ? NONE : EVENT_COORDINATE_DEFAULT),
[eventCoordinateField]
)

const fields = useMemo(() => {
const isFallback = !!eventCoordinateField
const fields = []

if (isFallback) {
Expand Down Expand Up @@ -89,30 +104,23 @@
fields.push(...eventDataItems)
}

if (isFallback) {
fields.push({
id: EVENT_COORDINATE_ORG_UNIT,
name: i18n.t('Organisation unit location'),
})
}

return eventCoordinateField
return isFallback
? fields.filter((f) => f.id !== eventCoordinateField)
: fields
}, [trackedEntityType, eventDataItems, eventCoordinateField])
}, [trackedEntityType, eventDataItems, eventCoordinateField, isFallback])

let helpText = null
if (program) {
if (!programStage && trackedEntityType) {
helpText = i18n.t(
'Select a program stage to see additional coordinate options'
)
} else if (value === EVENT_COORDINATE_CASCADING) {
helpText = trackedEntityType
if (value === EVENT_COORDINATE_CASCADING) {
helpText = trackedEntityType?.id
? i18n.t(
'Enrollment > event > tracked entity > org unit coordinate'
'Event > enrollment > tracked entity > org unit coordinate'
)
: i18n.t('Event > org unit coordinate')
} else if (!programStage && trackedEntityType?.id) {
helpText = i18n.t(
'Select a program stage to see additional coordinate options'
)
}
} else {
helpText = i18n.t(
Expand All @@ -135,7 +143,9 @@
if (
trackedEntityType &&
eventDataItems &&
!fields.find((f) => f.id === value)
!fields.some((f) => f.id === value) &&
value !== defaultValue &&
fields.length > 0
) {
onChange(defaultValue, defaultValue)
}
Expand All @@ -156,7 +166,7 @@
: i18n.t('Coordinate field')
}
items={fields}
value={fields.find((f) => f.id === value) ? value : null}
value={fields.some((f) => f.id === value) ? value : null}
loading={
!!program && value !== EVENT_COORDINATE_DEFAULT && itemsLoading
}
Expand All @@ -165,15 +175,19 @@
onChange(field.id, field.valueType || field.id)
}
className={className}
dataTest="coordinatefield"
dataTest={dataTest}
/>
)
}

CoordinateField.propTypes = {
onChange: PropTypes.func.isRequired,
className: PropTypes.string,
eventCoordinateField: PropTypes.string,
dataTest: PropTypes.string,
eventCoordinateField: PropTypes.oneOfType([
PropTypes.string,
PropTypes.bool,
]),
program: PropTypes.object,
programStage: PropTypes.object,
type: PropTypes.string,
Expand Down
Loading
Loading