Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
13653c6
feat: preserve optionSet on event layer table headers
BRaimbault Jul 13, 2026
2c9a62b
feat: compute columnOptions for categorical data table columns
BRaimbault Jul 13, 2026
cb5da12
feat: support array-valued (multi-select) filters in filterData
BRaimbault Jul 13, 2026
960bfa6
feat: add filterByGlobalSearch utility and thread globalSearch throug…
BRaimbault Jul 13, 2026
8e4e380
feat: add multi-select dropdown filter to FilterInput
BRaimbault Jul 13, 2026
66c18c2
feat: add numeric filter syntax help tooltip
BRaimbault Jul 13, 2026
c3f809c
feat: pass columnOptions and optionSet through DataTable to FilterInput
BRaimbault Jul 13, 2026
828c3ad
feat: add global search box to data table toolbar
BRaimbault Jul 13, 2026
ab4d3fe
feat: wire legend item clicks to pre-populate data table filter
BRaimbault Jul 13, 2026
44ae9ee
chore: fix lint/prettier issues in PR3 files
BRaimbault Jul 13, 2026
75c583a
fix: toolbar polish - clear filters button, search sizing, collapse i…
BRaimbault Jul 14, 2026
17879b1
feat: round out data table filtering with reverse-selection, zoom-to-…
BRaimbault Jul 14, 2026
33c6968
revert: remove legend-click-to-filter, deferred to a future PR
BRaimbault Jul 14, 2026
1a0f16a
chore: sonarqube issues
BRaimbault Jul 14, 2026
d6f5ef9
chore: sonarqube issues
BRaimbault Jul 14, 2026
0897362
chore: sonarqube issue
BRaimbault Jul 14, 2026
7c2c9ca
fix: handle narrow columns and hide index column
BRaimbault Jul 14, 2026
21deee3
fix: rollback merging range and color columns
BRaimbault Jul 16, 2026
3c427a0
chore: PR clean-up
BRaimbault Jul 16, 2026
6125853
chore: sonarqube issues
BRaimbault Jul 16, 2026
adc0916
fix: properly format dropdown numeric values
BRaimbault Jul 16, 2026
11594e8
chore: fix cypress tests
BRaimbault Jul 16, 2026
b49a917
chore: cypress test fix
BRaimbault Jul 16, 2026
1a3abbb
chore: cypress test fix
BRaimbault Jul 16, 2026
fc1a2a1
chore: cypress test fix
BRaimbault Jul 17, 2026
0d1ee17
chore: create and use replica accounts in CI
BRaimbault Jul 19, 2026
6811010
chore: update e2eReplicaAccount.js
BRaimbault Jul 20, 2026
fc975bd
chore: apply PR3 code review suggestions
BRaimbault Sep 4, 2026
02500a5
chore: regenerate i18n/en.pot after PR3 rebase
BRaimbault Sep 4, 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
136 changes: 69 additions & 67 deletions cypress/integration/dataTable.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const checkTableCell = ({ row = 0, column = 0, expectedContent }) => {
}

describe('data table', () => {
it('opens data table and filters and sorts', () => {
it('opens data table for a Thematic layer and filters and sorts', () => {
const viewportHeight = Cypress.config('viewportHeight')
const expectedBottoms1 = [viewportHeight]
const expectedHeights1 = [
Expand All @@ -47,7 +47,7 @@ describe('data table', () => {
cy.visit(`/#/${map.id}`)
cy.get('canvas', EXTENDED_TIMEOUT).should('be.visible')

//check that the map resizes properly
// Check that the map resizes properly
assertMapPosition(expectedBottoms1, expectedHeights1)

cy.getByDataTest('moremenubutton').first().click()
Expand All @@ -62,10 +62,10 @@ describe('data table', () => {
.contains('Show data table')
.click()

//check that the bottom panel is present
// Check that the bottom panel is present
cy.getByDataTest('bottom-panel').should('be.visible')

//check that the map resizes properly
// Check that the map resizes properly
cy.getByDataTest('bottom-panel')
.invoke('height')
.then((height) => {
Expand All @@ -80,25 +80,25 @@ describe('data table', () => {
// Collapse the Layers Panel to give the table more width
cy.getByDataTest('layers-toggle-button').click()

// check number of columns
// Check number of columns
cy.getByDataTest('bottom-panel')
.findByDataTest('dhis2-uicore-datatablecellhead')
.should('have.length', 11)
.should('have.length', 10)

// Filter by name
cy.getByDataTest('data-table-column-filter-input-Name')
cy.getByDataTest('data-table-column-filter-search-Name')
.find('input')
.type('bar')
.type('bar{enter}')

// check that the filter returned the correct number of rows
// Check that the filter returned the correct number of rows
cy.getByDataTest('bottom-panel')
.findByDataTest('dhis2-uicore-tablebody')
.findByDataTest('dhis2-uicore-datatablerow')
.should('have.length', 7)

// confirm that the sort order is initially ascending by Name
checkTableCell({ row: 0, column: 2, expectedContent: 'Bargbe' })
checkTableCell({ row: 6, column: 2, expectedContent: 'Upper Bambara' })
// Confirm that the sort order is initially ascending by Name
checkTableCell({ row: 0, column: 1, expectedContent: 'Bargbe' })
checkTableCell({ row: 6, column: 1, expectedContent: 'Upper Bambara' })

// Sort by name
cy.getByDataTest('data-table-column-sort-button-Name').click()
Expand All @@ -108,16 +108,16 @@ describe('data table', () => {
// so we reset to top before asserting on row indices below
cy.get('[data-testid="virtuoso-scroller"]').scrollTo('top')

// confirm that the rows are sorted by Name descending
checkTableCell({ row: 0, column: 2, expectedContent: 'Upper Bambara' })
checkTableCell({ row: 6, column: 2, expectedContent: 'Bargbe' })
// Confirm that the rows are sorted by Name descending
checkTableCell({ row: 0, column: 1, expectedContent: 'Upper Bambara' })
checkTableCell({ row: 6, column: 1, expectedContent: 'Bargbe' })

// filter by Value (numeric)
cy.getByDataTest('data-table-column-filter-input-Value')
// Filter by Value (numeric)
cy.getByDataTest('data-table-column-filter-search-Value')
.find('input')
.type('>26')
.type('>26{enter}')

// check that the (combined) filter returned the correct number of rows
// Check that the (combined) filter returned the correct number of rows
cy.getByDataTest('bottom-panel')
.findByDataTest('dhis2-uicore-tablebody')
.findByDataTest('dhis2-uicore-datatablerow')
Expand All @@ -129,11 +129,11 @@ describe('data table', () => {
// Reset scroll position after sorting - see comment above
cy.get('[data-testid="virtuoso-scroller"]').scrollTo('top')

// check that the rows are sorted by Value ascending
checkTableCell({ row: 0, column: 4, expectedContent: '35' })
checkTableCell({ row: 4, column: 4, expectedContent: '76' })
// Check that the rows are sorted by Value ascending
checkTableCell({ row: 0, column: 3, expectedContent: '35' })
checkTableCell({ row: 4, column: 3, expectedContent: '76' })

// right-click a row and select "View profile"
// Right-click a row and select "View profile"
cy.getByDataTest('bottom-panel')
.findByDataTest('dhis2-uicore-tablebody')
.findByDataTest('dhis2-uicore-datatablerow')
Expand All @@ -142,22 +142,22 @@ describe('data table', () => {

cy.getByDataTest('data-table-context-menu-view-profile').click()

// check that the org unit profile drawer is opened
// Check that the org unit profile drawer is opened
cy.getByDataTest('org-unit-profile').should('be.visible')

cy.getByDataTest('layers-toggle-button').click()

// close the datatable
// Close the datatable
cy.getByDataTest('moremenubutton').first().click()
cy.getByDataTest('more-menu')
.find('li')
.contains('Hide data table')
.click()

//check that the bottom panel is closed
// Check that the bottom panel is closed
cy.getByDataTest('bottom-panel').should('not.exist')

//check that the map resizes properly
// Check that the map resizes properly
assertMapPosition(expectedBottoms1, expectedHeights1)
})

Expand Down Expand Up @@ -192,56 +192,59 @@ describe('data table', () => {
// Collapse the Layers Panel to give the table more width
cy.getByDataTest('layers-toggle-button').click()

// check number of columns
// Check number of columns
cy.getByDataTest('bottom-panel')
.findByDataTest('dhis2-uicore-datatablecellhead')
.should('have.length', 11)
.should('have.length', 10)

cy.getByDataTest('bottom-panel')
.findByDataTest('dhis2-uicore-datatablecellhead')
.contains('Age in years', { matchCase: false })
.should('be.visible')

// filter by Org unit
// Filter by Org unit
const ouName = 'Moyowa'
cy.getByDataTest('data-table-column-filter-input-Org unit')
cy.getByDataTest('data-table-column-filter-search-Org unit')
.find('input')
.type(ouName)
.type(`${ouName}{enter}`)

// check that all the rows have Org unit Moyowa
checkTableCell({ row: 0, column: 2, expectedContent: ouName })
checkTableCell({ row: 2, column: 2, expectedContent: ouName })
// Check that all the rows have Org unit Moyowa
checkTableCell({ row: 0, column: 1, expectedContent: ouName })
checkTableCell({ row: 2, column: 1, expectedContent: ouName })

cy.getByDataTest('bottom-panel')
.findByDataTest('dhis2-uicore-tablebody')
.findByDataTest('dhis2-uicore-datatablerow')
.should('have.length', 3)

// filter by Mode of Discharge
cy.getByDataTest('data-table-column-filter-input-Mode of Discharge')
// Filter by Mode of Discharge
cy.getByDataTest('data-table-column-filter-search-Mode of Discharge')
.find('input')
.type('Absconded')
cy.contains('label', 'Absconded').click()

cy.getByDataTest('bottom-panel')
.findByDataTest('dhis2-uicore-tablebody')
.findByDataTest('dhis2-uicore-datatablerow')
.should('have.length', 1)

cy.getByDataTest('data-table-column-filter-input-Mode of Discharge')
.find('input')
.clear()
cy.get('.backdrop').last().click()

cy.getByDataTest('data-table-column-filter-search-Mode of Discharge')
.find('.clear-button')
.click()

cy.getByDataTest('bottom-panel')
.findByDataTest('dhis2-uicore-tablebody')
.findByDataTest('dhis2-uicore-datatablerow')
.should('have.length', 3)

// filter by Age in years (numeric)
cy.getByDataTest('data-table-column-filter-input-Age in years')
// Filter by Age in years (numeric)
cy.getByDataTest('data-table-column-filter-search-Age in years')
.find('input')
.type('<51')
.type('<51{enter}')

// check that the filter returned the correct number of rows
// Check that the filter returned the correct number of rows
cy.getByDataTest('bottom-panel')
.findByDataTest('dhis2-uicore-tablebody')
.findByDataTest('dhis2-uicore-datatablerow')
Expand All @@ -252,10 +255,10 @@ describe('data table', () => {

// Confirm that the rows are sorted by Age in years ascending
// (the first click on a new column always sorts ascending)
checkTableCell({ row: 0, column: 8, expectedContent: '6' })
checkTableCell({ row: 1, column: 8, expectedContent: '32' })
checkTableCell({ row: 0, column: 7, expectedContent: '6' })
checkTableCell({ row: 1, column: 7, expectedContent: '32' })

// right-click a row: Event layers have no profile to view
// Right-click a row: Event layers have no profile to view
cy.getByDataTest('bottom-panel')
.findByDataTest('dhis2-uicore-tablebody')
.findByDataTest('dhis2-uicore-datatablerow')
Expand All @@ -266,7 +269,7 @@ describe('data table', () => {
'not.exist'
)

// check that the org unit profile drawer is NOT opened
// Check that the org unit profile drawer is NOT opened
cy.getByDataTest('org-unit-profile').should('not.exist')
})

Expand Down Expand Up @@ -314,7 +317,7 @@ describe('data table', () => {
cy.getByDataTest('layers-toggle-button').click()

// Confirm that the sort order is initially ascending by Name
checkTableCell({ row: 0, column: 2, expectedContent: 'Bendu CHC' })
checkTableCell({ row: 0, column: 1, expectedContent: 'Bendu CHC' })

// First click on a new column always sorts ascending
cy.getByDataTest('data-table-column-sort-button-Value').click()
Expand All @@ -323,40 +326,39 @@ describe('data table', () => {
cy.get('[data-testid="virtuoso-scroller"]').scrollTo('top')

// Check that first row has Tihun CHC with value 28.63
checkTableCell({ row: 0, column: 2, expectedContent: 'Tihun CHC' })
checkTableCell({ row: 0, column: 4, expectedContent: '28.63' })
checkTableCell({ row: 0, column: 1, expectedContent: 'Tihun CHC' })
checkTableCell({ row: 0, column: 3, expectedContent: '28.63' })

// Check that row 5 has Gbamgbama CHC with value 117.98
checkTableCell({ row: 5, column: 2, expectedContent: 'Gbamgbama CHC' })
checkTableCell({ row: 5, column: 4, expectedContent: '117.98' })
checkTableCell({ row: 5, column: 1, expectedContent: 'Gbamgbama CHC' })
checkTableCell({ row: 5, column: 3, expectedContent: '117.98' })

// Check that row 6 has no value (undefined)
checkTableCell({ row: 6, column: 4, expectedContent: '' })
checkTableCell({ row: 6, column: 3, expectedContent: '' })

// Sort descending by Value
cy.getByDataTest('data-table-column-sort-button-Value').click()

// Reset scroll position after sorting - see comment above
cy.get('[data-testid="virtuoso-scroller"]').scrollTo('top')

checkTableCell({ row: 0, column: 2, expectedContent: 'Gbamgbama CHC' })
checkTableCell({ row: 0, column: 4, expectedContent: '117.98' })
checkTableCell({ row: 0, column: 1, expectedContent: 'Gbamgbama CHC' })
checkTableCell({ row: 0, column: 3, expectedContent: '117.98' })

checkTableCell({ row: 5, column: 2, expectedContent: 'Tihun CHC' })
checkTableCell({ row: 5, column: 4, expectedContent: '28.63' })
checkTableCell({ row: 5, column: 1, expectedContent: 'Tihun CHC' })
checkTableCell({ row: 5, column: 3, expectedContent: '28.63' })

checkTableCell({ row: 6, column: 4, expectedContent: '' })
checkTableCell({ row: 6, column: 3, expectedContent: '' })

// Sort by index (a new column, so ascending)
cy.getByDataTest('data-table-column-sort-button-Index').click()
// Third click on the same column cycles back to natural (unsorted)
// order - there's no dedicated Index column/button any more
cy.getByDataTest('data-table-column-sort-button-Value').click()

// Reset scroll position after sorting - see comment above
cy.get('[data-testid="virtuoso-scroller"]').scrollTo('top')

checkTableCell({ row: 0, column: 1, expectedContent: '0' })

// Check that row 0 range value is empty
checkTableCell({ row: 0, column: 6, expectedContent: '' })
checkTableCell({ row: 0, column: 5, expectedContent: '' })

// Sort by range, which is a string
cy.getByDataTest('data-table-column-sort-button-Range').click()
Expand All @@ -365,12 +367,12 @@ describe('data table', () => {
cy.get('[data-testid="virtuoso-scroller"]').scrollTo('top')

// Check that row 0 range value has value '0-40'
checkTableCell({ row: 0, column: 6, expectedContent: '0 – 40' })
checkTableCell({ row: 0, column: 5, expectedContent: '0 – 40' })

// Check that row 5 range value has value '90 - 120'
checkTableCell({ row: 5, column: 6, expectedContent: '90 – 120' })
checkTableCell({ row: 5, column: 5, expectedContent: '90 – 120' })

// Check that row 6 range value is empty
checkTableCell({ row: 6, column: 6, expectedContent: '' })
checkTableCell({ row: 6, column: 5, expectedContent: '' })
})
})
Loading
Loading