Skip to content

feat: redesign data table toolbar and add row selection context menu [PR1] - #3714

Open
BRaimbault wants to merge 11 commits into
chore/setup-claudefrom
feat/datatable-pr1-toolbar
Open

feat: redesign data table toolbar and add row selection context menu [PR1]#3714
BRaimbault wants to merge 11 commits into
chore/setup-claudefrom
feat/datatable-pr1-toolbar

Conversation

@BRaimbault

@BRaimbault BRaimbault commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Part of DHIS2-21456

Description

Replaces the 20px data table control strip with a full 36px toolbar and adds a right-click context menu to table rows. First of a series of PRs improving the data table experience.

Toolbar (BottomPanel)

  • Shows the active layer name and live row count (X of Y rows when filters are active, X rows otherwise)
  • "Clear filters" button appears when any column filter is active
  • Sort arrows in column headers now correctly reflect ascending/descending direction

Row context menu (TableContextMenu)

  • Right-click a row: drill up/down, view profile, zoom to feature
  • Drill items are hidden for boundary, facility, event, and GeoJSON URL layers
  • "View profile" opens the feature profile for GeoJSON URL layers and the org unit profile for all others

GeoJSON URL layer

  • Right-click on map features now opens the context menu (was wired only for click)
  • Map context menu: "View profile" opens the feature profile; drill up/down items are hidden
  • Shared getGeojsonFeatureProfile utility keeps the profile format consistent across map click, map context menu, and table context menu

Bug fixes

  • Feature highlight no longer lost after async layer rebuilds (thematic timeline / split-by-period, EarthEngine full rebuild path)
  • Fixed null crash in highlightFeature when the map layer has no highlight method
  • O(1) feature lookup by id in the data table (was an O(n) scan per interaction)

Quality checklist

Add N/A to items that are not applicable.

  • Dashboard tested N/A
  • Cypress and/or Jest tests added/updated
  • Docs added N/A
  • d2-ci dependencies replaced N/A
  • Tester approved (name)

Screenshots

supporting images

@dhis2-bot

Copy link
Copy Markdown
Contributor

🚀 Deployed on https://pr-3714.maps.netlify.dhis2.org

@dhis2-bot
dhis2-bot temporarily deployed to netlify July 6, 2026 17:25 Inactive
@BRaimbault
BRaimbault force-pushed the feat/datatable-pr1-toolbar branch from fdf69f4 to 3de8004 Compare July 6, 2026 17:31
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 6, 2026 17:39 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 9, 2026 17:46 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 10, 2026 08:07 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 13, 2026 15:57 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 14, 2026 17:20 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 16, 2026 22:14 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 17, 2026 10:02 Inactive
@BRaimbault
BRaimbault changed the base branch from master to chore/setup-claude July 17, 2026 18:18
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 17, 2026 18:20 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 19, 2026 10:02 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 19, 2026 13:41 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 19, 2026 15:48 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 19, 2026 20:52 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 19, 2026 21:23 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 19, 2026 23:26 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 20, 2026 07:18 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 20, 2026 12:05 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 20, 2026 14:12 Inactive
@dhis2-bot
dhis2-bot temporarily deployed to netlify July 20, 2026 15:00 Inactive
BRaimbault and others added 6 commits July 27, 2026 11:36
Adds clearDataFilters(layerId) action creator and the corresponding
DATA_FILTERS_CLEAR_ALL reducer case that resets dataFilters to {} for
the target layer. Used by the new "Clear filters" button in the toolbar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Returns the total number of rows before filtering (totalCount) and
after filtering (filteredCount) so the toolbar can show "X of Y rows".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the 20px grey strip with a 36px toolbar that shows:
- Active layer name (truncated with ellipsis)
- Row count ("X of Y rows" when filtered, "Y rows" otherwise)
- "Clear filters" button (only visible when filters are active)
- Close button

Adds onCountChange callback prop to DataTable so BottomPanel can
display the post-filter row count without reading useTableData directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends highlightFeature with an optional zoom:true flag. When a table
row is clicked (plain click, not Ctrl+click), dispatches highlightFeature
with zoom:true. Layer.js watches for this in componentDidUpdate and calls
panToFeature(), which uses getFeaturesById() to compute the feature's
bounding box and calls map.fitBounds() to pan and zoom to the feature.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@BRaimbault
BRaimbault force-pushed the feat/datatable-pr1-toolbar branch from 34435f4 to 6541b3a Compare July 27, 2026 09:45
@sonarqubecloud

Copy link
Copy Markdown

@dhis2-bot
dhis2-bot temporarily deployed to netlify July 27, 2026 09:47 Inactive
@BRaimbault BRaimbault changed the title feat: redesign toolbar and add row context menu [PR1] feat: redesign data table toolbar and add row context menu [PR1] Jul 28, 2026
@BRaimbault BRaimbault changed the title feat: redesign data table toolbar and add row context menu [PR1] feat: redesign data table toolbar and add row selection context menu [PR1] Jul 28, 2026
@BRaimbault
BRaimbault marked this pull request as ready for review August 4, 2026 15:21

@HendrikThePendric HendrikThePendric left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some minor suggestions, but this is looking very tidy 🚀

Comment on lines +106 to +115
let rowCountLabel = null
if (totalCount !== null && filteredCount !== null) {
rowCountLabel =
filteredCount < totalCount
? i18n.t('{{filtered}} of {{total}} rows', {
filtered: filteredCount,
total: totalCount,
})
: i18n.t('{{total}} rows', { total: totalCount })
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MINOR: Could be nice to wrap in useMemo, not because this is an expensive calculation, but simply because you can then use a const instead of let and return null early.

Comment on lines +141 to +148
style={{
top: nameTooltipPos.top,
left: nameTooltipPos.left,
color: nameTooltipPos.color,
fontSize: nameTooltipPos.fontSize,
lineHeight: nameTooltipPos.lineHeight,
paddingLeft: nameTooltipPos.paddingLeft,
}}

@HendrikThePendric HendrikThePendric Aug 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MINOR: You can simplify this, provided there will never be any non-style fields.

Suggested change
style={{
top: nameTooltipPos.top,
left: nameTooltipPos.left,
color: nameTooltipPos.color,
fontSize: nameTooltipPos.fontSize,
lineHeight: nameTooltipPos.lineHeight,
paddingLeft: nameTooltipPos.paddingLeft,
}}
style={nameTooltipPos}

Works for null too

Comment on lines +52 to +55
layerType !== BOUNDARY_LAYER &&
layerType !== FACILITY_LAYER &&
layerType !== EVENT_LAYER &&
layerType !== GEOJSON_URL_LAYER

@HendrikThePendric HendrikThePendric Aug 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MINOR: Maybe store these layers in a Set in the module scope and do sth like !UNDRILLABLE_LAYERS.has(layer)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants