From 5e8f345d78605a0d6ab8b2ac965a691c9a26faf7 Mon Sep 17 00:00:00 2001 From: Timothy Mugo Date: Tue, 4 Aug 2026 20:36:42 +0300 Subject: [PATCH 1/7] feat: implement dynamic variables in the map tooltip configuration --- .../blocks/map/BlockEdit.js | 3 +- .../blocks/map/Tooltips.js | 436 +++++++++++++----- 2 files changed, 321 insertions(+), 118 deletions(-) diff --git a/plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js b/plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js index 4486424ea..61c52dea5 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js +++ b/plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js @@ -36,6 +36,7 @@ class BlockEdit extends BlockEditWithAPIMetadata { componentDidMount() { super.componentDidMount(); this.getTaxonomies() + console.log("BlockEdit componentDidMount called", this.props); const { attributes: { mainLayerId @@ -694,7 +695,7 @@ class BlockEdit extends BlockEditWithAPIMetadata { - + {mapType == "POINTS_MAP" && diff --git a/plugins/wp-react-blocks-plugin/blocks/map/Tooltips.js b/plugins/wp-react-blocks-plugin/blocks/map/Tooltips.js index c627b3bb3..c1fcc0404 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/Tooltips.js +++ b/plugins/wp-react-blocks-plugin/blocks/map/Tooltips.js @@ -1,132 +1,334 @@ import { Component } from "@wordpress/element"; -import { __ } from '@wordpress/i18n'; +import { __ } from "@wordpress/i18n"; import { - Button, - PanelBody, - PanelRow, - SelectControl, - TextareaControl, - RangeControl, - ToggleControl -} from '@wordpress/components'; + Button, + PanelBody, + PanelRow, + SelectControl, + TextareaControl, + RangeControl, + ToggleControl, +} from "@wordpress/components"; -export default class Tooltips extends Component { - constructor(props) { - super(props); - this.addCustomTooltip = this.addCustomTooltip.bind(this) - this.removeCustomTooltip = this.removeCustomTooltip.bind(this) - } +function APITooltipGuide({ + allMeasures = [], + allDimensions = [], + dimension1 = "none", + dimension2 = "none", + filters = [], +}) { + return ( + <> +

DEFAULT VARIABLES

+ + + Location -> {"{locationName}"} + + + + + Field Label -> {"{label}"} + + + + + Measure Label -> {"{measureLabel}"} + + + + + Field Value -> {"{value}"} + + +
+

DYNAMIC VARIABLES

+ {dimension1 && dimension1 !== "none" && ( + +

+ Dimension 1: {"{" + dimension1 + "}"} +

+
+ )} - setFieldData(field, value, idx) { - const { attributes: { customTooltips }, setAttributes } = this.props - const newCustomTooltip = customTooltips.slice() - newCustomTooltip[idx][field] = value; - setAttributes({ customTooltips: newCustomTooltip }); - } + {dimension2 && dimension2 !== "none" && ( + +

+ Dimension 2: {"{" + dimension2 + "}"} +

+
+ )} - addCustomTooltip() { - const { attributes: { customTooltips }, setAttributes } = this.props - let index = customTooltips.length; - const newCustomTooltip = {} - let newCustomTooltips = customTooltips.slice() - newCustomTooltips.push(newCustomTooltip) - setAttributes({ customTooltips: newCustomTooltips }) - } +

+ Measures +

+ {allMeasures && + allMeasures.map((m) => ( + +

+ {"{" + m + "}"} +

+
+ ))} - removeCustomTooltip(f) { - const { attributes: { customTooltips }, setAttributes } = this.props - let newCustomTooltips = customTooltips.slice(0, -1) - setAttributes({ customTooltips: newCustomTooltips }) - } +

+ Filters +

+ {filters && filters.map((f) => ( + +

+ {"{" + f.param + "}"} +

+
+ ))} + + ); +} - render() { - const { setAttributes, attributes: { customTooltips, app, tooltipFormat, showTooltip, showNoDataTooltip, tooltipTheme, tooltipFontSize}, locations } = this.props; - return [ - - - setAttributes({ showTooltip: !showTooltip })} /> +function CSVTooltipGuide() { + return ( + <> + + + Location -> {"{locationName}"} + + + + + Field Label -> {"{label}"} + + + + + Measure Label -> {"{measureLabel}"} + + + + + Field Value -> {"{value}"} + + + + + All variables/columns that start with an _ in csv + + + + ); +} + +export default class Tooltips extends Component { + constructor(props) { + super(props); + this.addCustomTooltip = this.addCustomTooltip.bind(this); + this.removeCustomTooltip = this.removeCustomTooltip.bind(this); + } - - {showTooltip && - <> - - setAttributes({ showNoDataTooltip: !showNoDataTooltip })} /> + setFieldData(field, value, idx) { + const { + attributes: { customTooltips }, + setAttributes, + } = this.props; + const newCustomTooltip = customTooltips.slice(); + newCustomTooltip[idx][field] = value; + setAttributes({ customTooltips: newCustomTooltip }); + } - - - { - setAttributes({ tooltipTheme }) - }} - options={[{ label: "Dark", value: "map-tooltip-dark" }, { label: "Light", value: "map-tooltip-light" }]}> + addCustomTooltip() { + const { + attributes: { customTooltips }, + setAttributes, + } = this.props; + let index = customTooltips.length; + const newCustomTooltip = {}; + let newCustomTooltips = customTooltips.slice(); + newCustomTooltips.push(newCustomTooltip); + setAttributes({ customTooltips: newCustomTooltips }); + } - - - - setAttributes({ tooltipFontSize })} - min={0} - max={20} /> - - - Location -> { '{locationName}'} - - - Field Label -> { '{label}'} - - - Measure Label -> { '{measureLabel}'} - - - Field Value -> { '{value}'} - - - All variables/columns that start with an _ in csv - - - setAttributes({tooltipFormat})} - /> + removeCustomTooltip(f) { + const { + attributes: { customTooltips }, + setAttributes, + } = this.props; + let newCustomTooltips = customTooltips.slice(0, -1); + setAttributes({ customTooltips: newCustomTooltips }); + } + + render() { + const { + setAttributes, + attributes: { + customTooltips, + app, + tooltipFormat, + showTooltip, + showNoDataTooltip, + tooltipTheme, + tooltipFontSize, + dimension1, + dimension2, + measures, + filters, + }, + locations, + } = this.props; + + return [ + + + setAttributes({ showTooltip: !showTooltip })} + /> + + {showTooltip && ( + <> + + + setAttributes({ showNoDataTooltip: !showNoDataTooltip }) + } + /> - - {customTooltips.map((f, index) => { - return ( - - { { - this.setFieldData('location', value, index) - }} - options={locations ? [{value:'', label: 'Select Location'}, ...locations] : []}> - } + + { + setAttributes({ tooltipTheme }); + }} + options={[ + { label: "Dark", value: "map-tooltip-dark" }, + { label: "Light", value: "map-tooltip-light" }, + ]} + > + + + + setAttributes({ tooltipFontSize }) + } + min={0} + max={20} + /> + + {app === "csv" && } - { this.setFieldData('tooltip', value, index)} />} - ) - })} + {app !== "csv" && ( + + )} - - + setAttributes({ tooltipFormat })} + /> - - - } - - ] - } + + {customTooltips.map((f, index) => { + return ( + + { + { + this.setFieldData("location", value, index); + }} + options={ + locations + ? [ + { value: "", label: "Select Location" }, + ...locations, + ] + : [] + } + > + } + + { + + this.setFieldData("tooltip", value, index) + } + /> + } + + ); + })} + + + + + + + )} + , + ]; + } } From 1676ea401d24f5d6a9961dd49723581ad361d16e Mon Sep 17 00:00:00 2001 From: Timothy Mugo Date: Thu, 6 Aug 2026 11:44:59 +0300 Subject: [PATCH 2/7] feat: add support for additional dimension in map configuration and tooltips --- .../blocks/map/APIConfig.js | 11 +++++ .../blocks/map/BlockEdit.js | 10 +++-- .../blocks/map/BlockSave.js | 5 ++- .../blocks/map/README.md | 1 + .../blocks/map/Tooltips.js | 43 ++++++++----------- .../blocks/map/index.js | 6 ++- 6 files changed, 45 insertions(+), 31 deletions(-) diff --git a/plugins/wp-react-blocks-plugin/blocks/map/APIConfig.js b/plugins/wp-react-blocks-plugin/blocks/map/APIConfig.js index 3c94c7714..e57f99e88 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/APIConfig.js +++ b/plugins/wp-react-blocks-plugin/blocks/map/APIConfig.js @@ -194,6 +194,7 @@ export class APIConfig extends Component { filters, dimension1, dimension2, + dimension3, type, types } @@ -223,6 +224,16 @@ export class APIConfig extends Component { options={allDimensions} />
+ + { + setAttributes({dimension3: value}) + }} + options={allDimensions} + /> +
, , <> diff --git a/plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js b/plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js index 61c52dea5..334af7d56 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js +++ b/plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js @@ -428,6 +428,7 @@ class BlockEdit extends BlockEditWithAPIMetadata { app, dimension1, dimension2, + dimension3, legendBreaks, mainLayerId, mappingField, @@ -447,7 +448,7 @@ class BlockEdit extends BlockEditWithAPIMetadata { } = this.props; const {mapFiles} = this.state; - const levels = [dimension1, dimension2] + const levels = [dimension1, dimension2, dimension3] const source = levels.filter(l => l != 'none' && l != null).join('/') const validLegendBreaks = legendBreaks.filter(b => b.min || b.max); let params = {} @@ -490,6 +491,7 @@ class BlockEdit extends BlockEditWithAPIMetadata { app: app, dimension1: 'none', dimension2: 'none', + dimension3: 'none', filters: [] }) }} @@ -559,7 +561,7 @@ class BlockEdit extends BlockEditWithAPIMetadata { onChange={(value) => {this.setValue(file.value, 'index', value)}} max={10} type="number" />} - {app != 'csv' && + {app !== 'csv' &&

@@ -677,7 +679,7 @@ class BlockEdit extends BlockEditWithAPIMetadata { - {app != 'csv' && + {app !== 'csv' && { } -export default Edit; +export default Edit; \ No newline at end of file diff --git a/plugins/wp-react-blocks-plugin/blocks/map/BlockSave.js b/plugins/wp-react-blocks-plugin/blocks/map/BlockSave.js index 0249f2a4d..d2d8a98d0 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/BlockSave.js +++ b/plugins/wp-react-blocks-plugin/blocks/map/BlockSave.js @@ -10,6 +10,7 @@ const SaveComponent = (props) => { dataSourceLabel, dimension1, dimension2, + dimension3, measures, filters, app, @@ -74,6 +75,7 @@ const SaveComponent = (props) => { labelsExclusionList, noDataText, customMeasureLabels, + tooltipValueVariables, showShadingLayerLabels, dvzProxyDatasetId } @@ -83,7 +85,7 @@ const SaveComponent = (props) => { className: 'viz component map' }); - const levels = [dimension1, dimension2] + const levels = [dimension1, dimension2, dimension3] const source = levels.filter(l => l != 'none' && l != null).join('/') const validLegendBreaks = legendBreaks.filter(b => b.min || b.max); @@ -152,6 +154,7 @@ const SaveComponent = (props) => { data-file-type={fileType} data-dimension1={dimension1} data-dimension2={dimension2} + data-dimension3={dimension3} data-enabled-layers={encodeURIComponent(JSON.stringify(enabledLayers))} data-point-label-color={pointLabelColor} data-point-label-format={pointLabelFormat} diff --git a/plugins/wp-react-blocks-plugin/blocks/map/README.md b/plugins/wp-react-blocks-plugin/blocks/map/README.md index 5cd62bafe..aa34e742c 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/README.md +++ b/plugins/wp-react-blocks-plugin/blocks/map/README.md @@ -34,6 +34,7 @@ A Gutenberg block that renders an interactive choropleth/symbol map by embedding | `tooltipTheme` | `String` | `"map-tooltip-dark"` | CSS class applied to the tooltip container. | | `tooltipFontSize` | `Numeric` | `14` | Font size (px) for tooltip text. | | `customTooltips` | `Array` | `[]` | Per-measure custom tooltip templates. | +| `tooltipValueVariables` | `Object` | `{}` | Per-measure (API apps only) override: `{ [measure]: { useVariable, variable } }`. When enabled, the named column's value replaces `{value}` in the tooltip (e.g. show a text legend instead of a numeric/categorical code). | | `showOverallValue` | `Boolean` | `false` | Display an overall/national average value on the map. | | `nationalAverageLabel` | `String` | `"National Prevalence Avg"` | Label for the national average annotation. | | `dataSourceLabel` | `String` | `"Source"` | Label prefix for the data source credit. | diff --git a/plugins/wp-react-blocks-plugin/blocks/map/Tooltips.js b/plugins/wp-react-blocks-plugin/blocks/map/Tooltips.js index c1fcc0404..05109d25d 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/Tooltips.js +++ b/plugins/wp-react-blocks-plugin/blocks/map/Tooltips.js @@ -15,6 +15,7 @@ function APITooltipGuide({ allDimensions = [], dimension1 = "none", dimension2 = "none", + dimension3 = "none", filters = [], }) { return ( @@ -72,6 +73,21 @@ function APITooltipGuide({ )} + {dimension3 && dimension3 !== "none" && ( + +

+ Dimension 3: {"{" + dimension3 + "}"} +

+
+ )} +

))} - -

- Filters -

- {filters && filters.map((f) => ( - -

- {"{" + f.param + "}"} -

-
- ))} ); } @@ -209,6 +200,7 @@ export default class Tooltips extends Component { tooltipFontSize, dimension1, dimension2, + dimension3, measures, filters, }, @@ -267,6 +259,7 @@ export default class Tooltips extends Component { allMeasures={measures} dimension1={dimension1} dimension2={dimension2} + dimension3={dimension3} filters={filters} /> )} diff --git a/plugins/wp-react-blocks-plugin/blocks/map/index.js b/plugins/wp-react-blocks-plugin/blocks/map/index.js index ca1c04dd8..7de804703 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/index.js +++ b/plugins/wp-react-blocks-plugin/blocks/map/index.js @@ -33,7 +33,11 @@ registerBlockType(BLOCKS_NS + '/map', }, dimension2: { type: 'String', - default: 'gender' + default: 'none' + }, + dimension3: { + type: 'String', + default: 'none' }, app: { type: 'String', From 24c315f7da5ee32a2ace43a98442d9ba053eacf4 Mon Sep 17 00:00:00 2001 From: Timothy Mugo Date: Thu, 6 Aug 2026 12:03:41 +0300 Subject: [PATCH 3/7] feat: update release and pre-release branch configurations in workflow --- .github/workflows/build-and-pre-release-wordpress.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-pre-release-wordpress.yml b/.github/workflows/build-and-pre-release-wordpress.yml index 138f2460a..43cbaa526 100644 --- a/.github/workflows/build-and-pre-release-wordpress.yml +++ b/.github/workflows/build-and-pre-release-wordpress.yml @@ -43,8 +43,9 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} tag_prefix: v - release_branches: production,release/* - pre_release_branches: main,task/*,feature/*,bugfix/*,feat/*,fix/* + default_prerelease_bump: prerelease + release_branches: __no-stable-release-branch__ + pre_release_branches: .* append_to_pre_release_tag: rc dry_run: "true" From 036c8feef059ec851d9303636f1778d4f629e508 Mon Sep 17 00:00:00 2001 From: Timothy Mugo Gachengo <55899373+timothygachengo@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:09:01 +0300 Subject: [PATCH 4/7] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- plugins/wp-react-blocks-plugin/blocks/map/APIConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/wp-react-blocks-plugin/blocks/map/APIConfig.js b/plugins/wp-react-blocks-plugin/blocks/map/APIConfig.js index e57f99e88..34126642f 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/APIConfig.js +++ b/plugins/wp-react-blocks-plugin/blocks/map/APIConfig.js @@ -227,7 +227,7 @@ export class APIConfig extends Component { { setAttributes({dimension3: value}) }} From b3aa6ba19af473ace401bac78bd17231260af2db Mon Sep 17 00:00:00 2001 From: Timothy Mugo Gachengo <55899373+timothygachengo@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:09:18 +0300 Subject: [PATCH 5/7] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js b/plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js index 334af7d56..ea0ccadf6 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js +++ b/plugins/wp-react-blocks-plugin/blocks/map/BlockEdit.js @@ -36,7 +36,6 @@ class BlockEdit extends BlockEditWithAPIMetadata { componentDidMount() { super.componentDidMount(); this.getTaxonomies() - console.log("BlockEdit componentDidMount called", this.props); const { attributes: { mainLayerId From 21ebd7f4c9623aff518ea55142df558f2c149b92 Mon Sep 17 00:00:00 2001 From: Timothy Mugo Date: Thu, 6 Aug 2026 15:11:52 +0300 Subject: [PATCH 6/7] chore: update documentation --- plugins/wp-react-blocks-plugin/blocks/map/BlockSave.js | 3 +-- plugins/wp-react-blocks-plugin/blocks/map/README.md | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/wp-react-blocks-plugin/blocks/map/BlockSave.js b/plugins/wp-react-blocks-plugin/blocks/map/BlockSave.js index d2d8a98d0..d85842c70 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/BlockSave.js +++ b/plugins/wp-react-blocks-plugin/blocks/map/BlockSave.js @@ -74,8 +74,7 @@ const SaveComponent = (props) => { zoomOnFilterField, labelsExclusionList, noDataText, - customMeasureLabels, - tooltipValueVariables, + customMeasureLabels, showShadingLayerLabels, dvzProxyDatasetId } diff --git a/plugins/wp-react-blocks-plugin/blocks/map/README.md b/plugins/wp-react-blocks-plugin/blocks/map/README.md index aa34e742c..dc2e6b817 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/README.md +++ b/plugins/wp-react-blocks-plugin/blocks/map/README.md @@ -12,7 +12,8 @@ A Gutenberg block that renders an interactive choropleth/symbol map by embedding | `app` | `String` | `"csv"` | Data source identifier (`"csv"` or an API app slug). | | `csv` | `String` | `""` | Inline CSV data used when `app` is `"csv"`. | | `dimension1` | `String` | `"zone"` | Primary geographic dimension field. | -| `dimension2` | `String` | `"gender"` | Secondary dimension field (e.g. for disaggregation). | +| `dimension2` | `String` | `"none"` | Secondary dimension field (e.g. for disaggregation). | +| `dimension3` | `String` | `"none"` | Secondary dimension field (e.g. for disaggregation). | | `measures` | `Array` | `[]` | Measure fields to visualise on the map. Must be configured; no measure is pre-selected by default. | | `filters` | `Array` | `[]` | Active filter values passed as query params to the data API. | | `mapFile` | `String` | `""` | Path (relative to the UI app) to the TopoJSON map file. | @@ -34,7 +35,6 @@ A Gutenberg block that renders an interactive choropleth/symbol map by embedding | `tooltipTheme` | `String` | `"map-tooltip-dark"` | CSS class applied to the tooltip container. | | `tooltipFontSize` | `Numeric` | `14` | Font size (px) for tooltip text. | | `customTooltips` | `Array` | `[]` | Per-measure custom tooltip templates. | -| `tooltipValueVariables` | `Object` | `{}` | Per-measure (API apps only) override: `{ [measure]: { useVariable, variable } }`. When enabled, the named column's value replaces `{value}` in the tooltip (e.g. show a text legend instead of a numeric/categorical code). | | `showOverallValue` | `Boolean` | `false` | Display an overall/national average value on the map. | | `nationalAverageLabel` | `String` | `"National Prevalence Avg"` | Label for the national average annotation. | | `dataSourceLabel` | `String` | `"Source"` | Label prefix for the data source credit. | From 53c305c46f2b12bd1f16100a06a7557e93912381 Mon Sep 17 00:00:00 2001 From: Timothy Mugo Date: Thu, 6 Aug 2026 15:16:04 +0300 Subject: [PATCH 7/7] chore: update documentation --- plugins/wp-react-blocks-plugin/blocks/map/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/wp-react-blocks-plugin/blocks/map/README.md b/plugins/wp-react-blocks-plugin/blocks/map/README.md index dc2e6b817..aac0f46e8 100644 --- a/plugins/wp-react-blocks-plugin/blocks/map/README.md +++ b/plugins/wp-react-blocks-plugin/blocks/map/README.md @@ -13,7 +13,7 @@ A Gutenberg block that renders an interactive choropleth/symbol map by embedding | `csv` | `String` | `""` | Inline CSV data used when `app` is `"csv"`. | | `dimension1` | `String` | `"zone"` | Primary geographic dimension field. | | `dimension2` | `String` | `"none"` | Secondary dimension field (e.g. for disaggregation). | -| `dimension3` | `String` | `"none"` | Secondary dimension field (e.g. for disaggregation). | +| `dimension3` | `String` | `"none"` | Additional dimension (can be for tooltips) | | `measures` | `Array` | `[]` | Measure fields to visualise on the map. Must be configured; no measure is pre-selected by default. | | `filters` | `Array` | `[]` | Active filter values passed as query params to the data API. | | `mapFile` | `String` | `""` | Path (relative to the UI app) to the TopoJSON map file. |