From f15f83a21db0bb4b65e9eb1f567927a542d6f215 Mon Sep 17 00:00:00 2001
From: Kevin Koech <koechkevin92@gmail.com>
Date: Mon, 29 Jul 2024 16:02:26 +0300
Subject: [PATCH 1/4] Barchart to hurumap core

---
 .../HURUmap/Chart/configureScope.js           |  28 +-
 .../components/HURUmap/Chart/BarChartScope.js | 349 ------------------
 .../HURUmap/Chart/configureScope.js           |  28 +-
 packages/hurumap-core/package.json            |   1 +
 .../hurumap-core/src/Scope}/BarChartScope.js  |  16 +-
 packages/hurumap-core/src/Scope/Scope.js      | 224 +++++++++++
 packages/hurumap-core/src/Scope/data.js       |  44 +++
 packages/hurumap-core/src/Scope/index.js      |   7 +
 packages/hurumap-core/src/Scope/signals.js    | 248 +++++++++++++
 packages/hurumap-core/src/Scope/utils.js      |  28 ++
 packages/hurumap-core/src/index.js            |   1 +
 pnpm-lock.yaml                                | 172 ++-------
 12 files changed, 622 insertions(+), 524 deletions(-)
 delete mode 100644 apps/pesayetu/src/components/HURUmap/Chart/BarChartScope.js
 rename {apps/climatemappedafrica/src/components/HURUmap/Chart => packages/hurumap-core/src/Scope}/BarChartScope.js (98%)
 create mode 100644 packages/hurumap-core/src/Scope/Scope.js
 create mode 100644 packages/hurumap-core/src/Scope/data.js
 create mode 100644 packages/hurumap-core/src/Scope/index.js
 create mode 100644 packages/hurumap-core/src/Scope/signals.js
 create mode 100644 packages/hurumap-core/src/Scope/utils.js

diff --git a/apps/climatemappedafrica/src/components/HURUmap/Chart/configureScope.js b/apps/climatemappedafrica/src/components/HURUmap/Chart/configureScope.js
index 92983666e..486598bec 100644
--- a/apps/climatemappedafrica/src/components/HURUmap/Chart/configureScope.js
+++ b/apps/climatemappedafrica/src/components/HURUmap/Chart/configureScope.js
@@ -1,4 +1,5 @@
-import BarChartScope from "./BarChartScope";
+import Scope from "@hurumap/core";
+
 import DonutChartScope from "./DonutChartScope";
 import LineChartScope from "./LineChartScope";
 import MultiLineChartScope from "./MultiLineChartScope";
@@ -7,6 +8,10 @@ import TreemapChartScope from "./TreemapChartScope";
 import VerticalBarChartScope from "./VerticalBarChartScope";
 import VerticalStackedChartScope from "./VerticalStackedChartScope";
 
+import { hurumapArgs } from "@/climatemappedafrica/config";
+import theme from "@/climatemappedafrica/theme";
+
+const { BarChartScope } = Scope;
 export default function configureScope(
   indicator,
   secondaryIndicator = null,
@@ -87,16 +92,21 @@ export default function configureScope(
           isCompare,
         );
       } else {
-        vegaSpec = BarChartScope(
-          indicator?.data,
-          indicator?.metadata,
-          configuration,
-          secondaryIndicator?.data ?? null,
-          showParent ? indicator?.parentData : [{}],
-          showParent ? secondaryIndicator?.parentData : [{}],
+        const barChartArgs = {
+          primaryData: indicator?.data,
+          metadata: indicator?.metadata,
+          config: configuration,
+          secondaryData: secondaryIndicator?.data ?? null,
+          primaryParentData: showParent ? indicator?.parentData : [{}],
+          secondaryParentData: showParent
+            ? secondaryIndicator?.parentData
+            : [{}],
           profileNames,
           isCompare,
-        );
+          theme,
+          args: hurumapArgs,
+        };
+        vegaSpec = BarChartScope(barChartArgs);
       }
       break;
   }
diff --git a/apps/pesayetu/src/components/HURUmap/Chart/BarChartScope.js b/apps/pesayetu/src/components/HURUmap/Chart/BarChartScope.js
deleted file mode 100644
index 29410005c..000000000
--- a/apps/pesayetu/src/components/HURUmap/Chart/BarChartScope.js
+++ /dev/null
@@ -1,349 +0,0 @@
-import merge from "deepmerge";
-
-import Scope from "./Scope";
-
-import theme from "@/pesayetu/theme";
-
-export default function BarChartScope(
-  primaryData,
-  metadata,
-  config,
-  secondaryData,
-  primaryParentData,
-  secondaryParentData,
-  profileNames,
-  isCompare,
-) {
-  const { parentLabel } = config;
-
-  const { primary_group: primaryGroup } = metadata;
-
-  return merge(
-    Scope(
-      primaryData,
-      metadata,
-      config,
-      secondaryData,
-      primaryParentData,
-      secondaryParentData,
-      "bar",
-    ),
-    {
-      signals: [
-        {
-          name: "height",
-          update: "bandspace(domain('yscale').length, 0.1, 0.05) * y_step",
-        },
-      ],
-      scales: [
-        {
-          name: "yscale",
-          type: "band",
-          domain: {
-            data: "primary_formatted",
-            field: primaryGroup,
-          },
-          range: { step: { signal: "y_step" } },
-          padding: 0.15,
-        },
-        {
-          name: "xscale",
-          type: "linear",
-          range: [
-            0,
-            {
-              signal: "data('secondary').length > 1 ? width/2 - 30 : width",
-            },
-          ],
-          nice: { signal: "primaryXTickCount" },
-          zero: true,
-          domain: {
-            data: "primary_formatted",
-            field: { signal: "datatype[Units]" },
-          },
-        },
-        {
-          name: "s_xscale",
-          type: "linear",
-          range: [
-            0,
-            {
-              signal: "data('secondary').length > 1 ? width/2 - 30 : 0",
-            },
-          ],
-          nice: { signal: "secondaryXTickCount" },
-          zero: true,
-          domain: {
-            data: "secondary_formatted",
-            field: { signal: "datatype[Units]" },
-          },
-        },
-        {
-          name: "legend_primary_scale",
-          type: "ordinal",
-          domain: [profileNames.primary.toUpperCase()],
-          range: [theme.palette.primary.main],
-        },
-        {
-          name: "legend_secondary_scale",
-          type: "ordinal",
-          domain: [profileNames.secondary.toUpperCase()],
-          range: [theme.palette.secondary.main],
-        },
-        {
-          name: "parent_color_scale",
-          type: "ordinal",
-          range: "category",
-          domain: [parentLabel],
-        },
-      ],
-
-      marks: [
-        {
-          type: "group",
-          name: "primary_bars",
-          encode: {
-            update: {
-              x: { value: 0 },
-              y: { signal: "chartY" },
-              height: { signal: "height" },
-              width: {
-                signal: "data('secondary').length > 1 ? (width / 2 ) : width",
-              },
-            },
-          },
-          legends: isCompare
-            ? [
-                {
-                  orient: "top",
-                  fill: "legend_primary_scale",
-                  labelFontWeight: "bold",
-                  labelColor: "#666",
-                  labelFont: theme.typography.fontFamily,
-                },
-              ]
-            : null,
-          axes: [
-            {
-              orient: "left",
-              scale: "yscale",
-              domainOpacity: 0.5,
-              tickSize: 0,
-              labelPadding: 6,
-              zindex: 1,
-            },
-            {
-              orient: "bottom",
-              scale: "xscale",
-              bandPosition: 0,
-              domainOpacity: 0.5,
-              tickSize: 0,
-              format: { signal: "numberFormat[Units]" },
-              grid: true,
-              labelPadding: 6,
-              tickCount: { signal: "primaryXTickCount" },
-            },
-          ],
-          marks: [
-            {
-              from: { data: "primary_formatted" },
-              type: "rect",
-              encode: {
-                enter: {
-                  y: { scale: "yscale", field: { signal: "mainGroup" } },
-                  height: { scale: "yscale", band: 1 },
-                  x: { scale: "xscale", field: { signal: "datatype[Units]" } },
-                },
-                update: {
-                  fill: { value: theme.palette.primary.main },
-                  x: { scale: "xscale", field: { signal: "datatype[Units]" } },
-                  x2: { scale: "xscale", value: 0 },
-                  y: { scale: "yscale", field: { signal: "mainGroup" } },
-                  tooltip: {
-                    signal:
-                      "{'group': datum[mainGroup], 'count': format(datum.count, numberFormat.value)}",
-                  },
-                },
-              },
-            },
-          ],
-        },
-        {
-          type: "group",
-          name: "primary_parent_rule",
-          encode: {
-            update: {
-              x: { value: 0 },
-              y: { signal: "chartY" },
-              height: { signal: "height" },
-            },
-          },
-          legends:
-            primaryParentData?.length > 1
-              ? [
-                  {
-                    fill: "parent_color_scale",
-                    orient: "none",
-                    legendX: { signal: "width - 90" },
-                    legendY: { value: -40 },
-                    labelFont: theme.typography.fontFamily,
-                    labelColor: theme.palette.chart.text.primary,
-                    encode: {
-                      symbols: {
-                        shape: { value: "stroke" },
-                        update: {
-                          shape: { value: "stroke" },
-                          size: { value: 500 },
-                          stroke: { value: theme.palette.chart.text.primary },
-                          strokeDash: { value: [2, 2] },
-                        },
-                      },
-                    },
-                  },
-                ]
-              : null,
-          marks: [
-            {
-              name: "parent",
-              from: { data: "primary_parent_formatted" },
-              type: "rule",
-              encode: {
-                update: {
-                  y: { scale: "yscale", field: { signal: "mainGroup" } },
-                  y2: {
-                    scale: "yscale",
-                    field: { signal: "mainGroup" },
-                    offset: { signal: "y_step - 5" },
-                  },
-                  x: { scale: "xscale", field: { signal: "datatype[Units]" } },
-                  x2: { scale: "xscale", field: { signal: "datatype[Units]" } },
-                  stroke: {
-                    signal:
-                      " datum.primary && (datum[datatype[Units]] > datum.primary[datatype[Units]]) ? grey_mark: white_mark",
-                  },
-                  strokeWidth: { value: 1 },
-                  strokeDash: { value: [2, 2] },
-                },
-              },
-            },
-          ],
-        },
-        {
-          type: "group",
-          name: "secondary_bars",
-          encode: {
-            update: {
-              x: { signal: "(width / 2 ) + 30" },
-              y: { signal: "chartY" },
-              height: {
-                signal: "data('secondary').length > 1 ? height : 0",
-              },
-            },
-          },
-          legends: isCompare
-            ? [
-                {
-                  orient: {
-                    signal: "data('secondary').length > 1 ? 'top' : 'none'",
-                  },
-                  legendY: {
-                    signal: "data('secondary').length > 1 ? 0 : height + 28 ",
-                  },
-                  legendX: { signal: "-width/2 - 30" },
-                  labelLimit: 400,
-                  fill: "legend_secondary_scale",
-                  labelFontWeight: "bold",
-                  labelColor: isCompare ? "#666" : "transparent",
-                  labelFont: theme.typography.fontFamily,
-                },
-              ]
-            : null,
-          axes: [
-            {
-              orient: "bottom",
-              scale: "s_xscale",
-              bandPosition: 0,
-              domainOpacity: 0.5,
-              tickSize: 0,
-              format: { signal: "numberFormat[Units]" },
-              grid: true,
-              labelPadding: 6,
-              tickCount: { signal: "secondaryXTickCount" },
-            },
-          ],
-
-          marks: [
-            {
-              type: "rect",
-              from: { data: "secondary_formatted" },
-              encode: {
-                enter: {
-                  y: { scale: "yscale", field: { signal: "mainGroup" } },
-                  height: { scale: "yscale", band: 1 },
-                  x: {
-                    scale: "s_xscale",
-                    field: { signal: "datatype[Units]" },
-                  },
-                },
-                update: {
-                  fill: { value: theme.palette.secondary.main },
-                  y: { scale: "yscale", field: { signal: "mainGroup" } },
-                  x: {
-                    scale: "s_xscale",
-                    field: { signal: "datatype[Units]" },
-                  },
-                  x2: { scale: "s_xscale", value: 0 },
-                  tooltip: {
-                    signal:
-                      "{'group': datum[mainGroup], 'count': format(datum.count, numberFormat.value)}",
-                  },
-                },
-              },
-            },
-          ],
-        },
-        {
-          type: "group",
-          name: "secondary_parent_rule",
-          encode: {
-            update: {
-              x: { signal: "(width / 2 ) + 30" },
-              height: { signal: "height" },
-            },
-          },
-          marks: [
-            {
-              name: "secondary_parent",
-              from: { data: "secondary_parent_formatted" },
-              type: "rule",
-              encode: {
-                update: {
-                  y: { scale: "yscale", field: { signal: "mainGroup" } },
-                  y2: {
-                    scale: "yscale",
-                    field: { signal: "mainGroup" },
-                    offset: { signal: "y_step - 5" },
-                  },
-                  x: {
-                    scale: "s_xscale",
-                    field: { signal: "datatype[Units]" },
-                  },
-                  x2: {
-                    scale: "s_xscale",
-                    field: { signal: "datatype[Units]" },
-                  },
-                  stroke: {
-                    signal:
-                      " datum.secondary && (datum[datatype[Units]] > datum.secondary[datatype[Units]]) ? grey_mark: white_mark",
-                  },
-                  strokeWidth: { value: 1 },
-                  strokeDash: { value: [2, 2] },
-                },
-              },
-            },
-          ],
-        },
-      ],
-    },
-  );
-}
diff --git a/apps/pesayetu/src/components/HURUmap/Chart/configureScope.js b/apps/pesayetu/src/components/HURUmap/Chart/configureScope.js
index 92983666e..1cb9a6173 100644
--- a/apps/pesayetu/src/components/HURUmap/Chart/configureScope.js
+++ b/apps/pesayetu/src/components/HURUmap/Chart/configureScope.js
@@ -1,4 +1,5 @@
-import BarChartScope from "./BarChartScope";
+import Scope from "@hurumap/core";
+
 import DonutChartScope from "./DonutChartScope";
 import LineChartScope from "./LineChartScope";
 import MultiLineChartScope from "./MultiLineChartScope";
@@ -7,6 +8,10 @@ import TreemapChartScope from "./TreemapChartScope";
 import VerticalBarChartScope from "./VerticalBarChartScope";
 import VerticalStackedChartScope from "./VerticalStackedChartScope";
 
+import { hurumapArgs } from "@/pesayetu/config";
+import theme from "@/pesayetu/theme";
+
+const { BarChartScope } = Scope;
 export default function configureScope(
   indicator,
   secondaryIndicator = null,
@@ -87,16 +92,21 @@ export default function configureScope(
           isCompare,
         );
       } else {
-        vegaSpec = BarChartScope(
-          indicator?.data,
-          indicator?.metadata,
-          configuration,
-          secondaryIndicator?.data ?? null,
-          showParent ? indicator?.parentData : [{}],
-          showParent ? secondaryIndicator?.parentData : [{}],
+        const barChartArgs = {
+          primaryData: indicator?.data,
+          metadata: indicator?.metadata,
+          config: configuration,
+          secondaryData: secondaryIndicator?.data ?? null,
+          primaryParentData: showParent ? indicator?.parentData : [{}],
+          secondaryParentData: showParent
+            ? secondaryIndicator?.parentData
+            : [{}],
           profileNames,
           isCompare,
-        );
+          theme,
+          args: hurumapArgs,
+        };
+        vegaSpec = BarChartScope(barChartArgs);
       }
       break;
   }
diff --git a/packages/hurumap-core/package.json b/packages/hurumap-core/package.json
index 2ff1cc6db..2f72ab77e 100644
--- a/packages/hurumap-core/package.json
+++ b/packages/hurumap-core/package.json
@@ -67,6 +67,7 @@
     "react-leaflet": "^4.2.1"
   },
   "dependencies": {
+    "deepmerge": "^4.3.1",
     "prop-types": "^15.8.1"
   }
 }
diff --git a/apps/climatemappedafrica/src/components/HURUmap/Chart/BarChartScope.js b/packages/hurumap-core/src/Scope/BarChartScope.js
similarity index 98%
rename from apps/climatemappedafrica/src/components/HURUmap/Chart/BarChartScope.js
rename to packages/hurumap-core/src/Scope/BarChartScope.js
index 93c658a0c..23722cc5a 100644
--- a/apps/climatemappedafrica/src/components/HURUmap/Chart/BarChartScope.js
+++ b/packages/hurumap-core/src/Scope/BarChartScope.js
@@ -2,9 +2,7 @@ import merge from "deepmerge";
 
 import Scope from "./Scope";
 
-import theme from "@/climatemappedafrica/theme";
-
-export default function BarChartScope(
+export default function BarChartScope({
   primaryData,
   metadata,
   config,
@@ -13,21 +11,25 @@ export default function BarChartScope(
   secondaryParentData,
   profileNames,
   isCompare,
-) {
+  theme,
+  args,
+}) {
   const { parentLabel } = config;
 
   const { primary_group: primaryGroup } = metadata;
 
   return merge(
-    Scope(
+    Scope({
       primaryData,
       metadata,
       config,
       secondaryData,
       primaryParentData,
       secondaryParentData,
-      "bar",
-    ),
+      chartType: "bar",
+      theme,
+      args,
+    }),
     {
       signals: [
         {
diff --git a/packages/hurumap-core/src/Scope/Scope.js b/packages/hurumap-core/src/Scope/Scope.js
new file mode 100644
index 000000000..43ed932d6
--- /dev/null
+++ b/packages/hurumap-core/src/Scope/Scope.js
@@ -0,0 +1,224 @@
+import data from "./data";
+import signals from "./signals";
+import { createFiltersForGroups } from "./utils";
+
+export default function Scope({
+  primaryData,
+  metadata,
+  config,
+  secondaryData,
+  primaryParentData,
+  secondaryParentData,
+  chartType,
+  transform = [],
+  theme,
+  args,
+}) {
+  const { primary_group: primaryGroup, groups } = metadata;
+  const stackedField = config?.stacked_field;
+  const nestedFields = config?.nest_fields;
+
+  let groupBy = [primaryGroup];
+  if ((chartType === "stacked" || chartType === "line") && stackedField) {
+    groupBy = [primaryGroup, stackedField];
+  } else if (chartType === "treemap" && nestedFields) {
+    groupBy = nestedFields;
+  }
+
+  const { signals: filterSignals, filters } = createFiltersForGroups(groups);
+
+  return {
+    $schema: "https://vega.github.io/schema/vega/v5.json",
+    description: "A",
+    config: {
+      axis: {
+        labelColor: theme.palette.chart.text.primary,
+        labelFont: theme.typography.fontFamily,
+        labelFontSize: 11,
+        gridColor: theme.palette.chart.text.primary,
+        gridOpacity: 0.2,
+      },
+      axisBottom: {
+        domainColor: "transparent",
+      },
+      legends: {
+        layout: {
+          direction: "horizontal",
+        },
+      },
+      range: {
+        category: theme.palette.chart.primary,
+        secondary: theme.palette.chart.secondary,
+      },
+    },
+    autosize: { type: "fit-x", contains: "padding" },
+    padding: 5,
+    width: { signal: "width" },
+    height: { signal: "totalHeight" },
+    data: [
+      ...data("primary", primaryData, filters, transform),
+      ...data("primary_parent", primaryParentData, filters, [
+        ...transform,
+        {
+          type: "lookup",
+          from: "primary_formatted",
+          key: { signal: "mainGroup" },
+          fields: [{ signal: "mainGroup" }],
+          as: ["primary"],
+        },
+      ]),
+      ...data("secondary", secondaryData, filters, transform),
+      ...data("secondary_parent", secondaryParentData, filters, [
+        ...transform,
+        {
+          type: "lookup",
+          from: "secondary_formatted",
+          key: { signal: "mainGroup" },
+          fields: [{ signal: "mainGroup" }],
+          as: ["secondary"],
+        },
+      ]),
+    ],
+    signals: signals(
+      chartType,
+      filterSignals,
+      primaryGroup,
+      groupBy,
+      config,
+      theme,
+      args,
+    ),
+    marks: [
+      {
+        type: "group",
+        name: "titleGroup",
+        encode: {
+          enter: {
+            y2: { signal: "titleGroupY" },
+            height: { signal: "titleH" },
+          },
+        },
+        marks: [
+          {
+            type: "text",
+            encode: {
+              enter: {
+                fill: { value: "#000" },
+                text: { signal: "chartTitle" },
+              },
+              update: {
+                opacity: { value: 1 },
+                x: { signal: "titleX" },
+                y: { signal: "titleY" },
+                font: { signal: "titlefont" },
+                fontSize: { signal: "titlefontSize" },
+                fontWeight: { signal: "titlefontWeight" },
+              },
+            },
+          },
+          {
+            type: "text",
+            encode: {
+              enter: {
+                fill: { value: theme.palette.chart.text.primary },
+                text: { signal: "chartSubtitle" },
+              },
+              update: {
+                opacity: { value: 1 },
+                x: { signal: "subtitleX" },
+                y: { signal: "subtitleY" },
+                font: { signal: "subtitlefont" },
+                fontSize: { signal: "subtitlefontSize" },
+                fontWeight: { signal: "subtitlefontWeight" },
+              },
+            },
+          },
+          {
+            type: "image",
+            encode: {
+              enter: {
+                url: { signal: "projectLogoUrl" },
+              },
+              update: {
+                align: { signal: "projectAlign" },
+                x: { signal: "projectX" },
+                y: { signal: "projectY" },
+              },
+            },
+          },
+          {
+            type: "rule",
+            encode: {
+              enter: {
+                x: { value: 0 },
+                x2: { signal: "titleH > 0 ? width: 0" },
+                y: { signal: "titleGroupY > 0 ? 0 : titleH" },
+                y2: { signal: "titleGroupY > 0 ? 0 : titleH" },
+                stroke: { value: theme.palette.chart.text.primary },
+                strokeWidth: { value: 2 },
+                strokeOpacity: { value: 0.1 },
+              },
+            },
+          },
+        ],
+      },
+      {
+        type: "group",
+        name: "sourceGroup",
+        encode: {
+          enter: {
+            y2: { signal: "sourceGroupY" },
+            height: { signal: "sourceGroupH" },
+          },
+        },
+        marks: [
+          {
+            type: "rule",
+            encode: {
+              enter: {
+                x: { value: 0 },
+                x2: { signal: "sourceGroupY > 0 ? width: 0" },
+                y: { signal: "sourceGroupY > 50 ? 0 : sourceGroupH" },
+                y2: { signal: "sourceGroupY > 50 ? 0 : sourceGroupH" },
+                stroke: { value: theme.palette.chart.text.primary },
+                strokeWidth: { value: 2 },
+                strokeOpacity: { value: 0.1 },
+              },
+            },
+          },
+          {
+            type: "text",
+            encode: {
+              enter: {
+                fill: { value: "#000" },
+                text: { signal: "chartSource" },
+              },
+              update: {
+                opacity: { value: 1 },
+                x: { signal: "sourceX" },
+                y: { signal: "sourceY" },
+                font: { signal: "sourceFont" },
+                fontSize: { signal: "sourceFontSize" },
+                fontWeight: { signal: "sourceFontWeight" },
+              },
+            },
+          },
+          {
+            type: "image",
+            encode: {
+              enter: {
+                url: { signal: "logoUrl" },
+              },
+              update: {
+                x: { signal: "logoX" },
+                y: { signal: "sourceY - 15" },
+                width: { signal: "logoWidth" },
+                aspect: { signal: "logoAspect" },
+              },
+            },
+          },
+        ],
+      },
+    ],
+  };
+}
diff --git a/packages/hurumap-core/src/Scope/data.js b/packages/hurumap-core/src/Scope/data.js
new file mode 100644
index 000000000..7d5fafefc
--- /dev/null
+++ b/packages/hurumap-core/src/Scope/data.js
@@ -0,0 +1,44 @@
+export default function Data(name, data, filters, transformArr = []) {
+  return [
+    {
+      name,
+      values: data,
+      transform: [...filters],
+    },
+    {
+      name: `${name}_formatted`,
+      source: name,
+      transform: [
+        {
+          type: "aggregate",
+          ops: ["sum"],
+          as: ["count"],
+          fields: ["count"],
+          groupby: { signal: "groups" },
+        },
+        {
+          type: "joinaggregate",
+          as: ["TotalCount"],
+          ops: ["sum"],
+          fields: ["count"],
+        },
+        {
+          type: "formula",
+          expr: "datum.count/datum.TotalCount",
+          as: "percentage",
+        },
+        {
+          type: "extent",
+          field: "percentage",
+          signal: `${name}_percentage_extent`,
+        },
+        {
+          type: "extent",
+          field: "count",
+          signal: `${name}_value_extent`,
+        },
+        ...transformArr,
+      ],
+    },
+  ];
+}
diff --git a/packages/hurumap-core/src/Scope/index.js b/packages/hurumap-core/src/Scope/index.js
new file mode 100644
index 000000000..9cbb70f75
--- /dev/null
+++ b/packages/hurumap-core/src/Scope/index.js
@@ -0,0 +1,7 @@
+import BarChartScope from "./BarChartScope";
+import Scope from "./Scope";
+
+export default {
+  Scope,
+  BarChartScope,
+};
diff --git a/packages/hurumap-core/src/Scope/signals.js b/packages/hurumap-core/src/Scope/signals.js
new file mode 100644
index 000000000..8ac90fb85
--- /dev/null
+++ b/packages/hurumap-core/src/Scope/signals.js
@@ -0,0 +1,248 @@
+const graphValueTypes = {
+  Percentage: "percentage",
+  Value: "value",
+};
+
+export default function Signals(
+  chartType,
+  filterSignals,
+  primaryGroup,
+  groups,
+  config,
+  theme,
+  args,
+) {
+  const formatting = args.chartFormatting;
+
+  const valueFormatting = config?.types?.Value?.formatting ?? formatting.value;
+  const percentageFormatting =
+    config?.types?.Percentage?.formatting ?? formatting.percentage;
+
+  const valueMaxX = config?.types?.Value?.maxX ?? undefined;
+  const valueMinX = config?.types?.Value?.minX ?? undefined;
+  const percentageMinX = config?.types?.Percentage?.maxX ?? undefined;
+  const percentageMaxX = config?.types?.Percentage?.minX ?? undefined;
+
+  const defaultType = config?.defaultType ?? "Value";
+  const xTicks = config?.xTicks ?? 6;
+  const yTicks = config?.yTicks ?? 6;
+
+  return [
+    {
+      name: "width",
+      update: "containerSize()[0] ? containerSize()[0] : 800",
+      on: [
+        {
+          events: "window:resize",
+          update: "containerSize()[0] ? containerSize()[0] : 800",
+        },
+      ],
+    },
+    {
+      name: "x_step",
+      value: 40,
+    },
+    {
+      name: "y_step",
+      value: 35,
+    },
+    {
+      name: "groups",
+      value: groups,
+    },
+    {
+      name: "Units",
+      value: graphValueTypes[defaultType],
+    },
+    {
+      name: "applyFilter",
+      value: false,
+    },
+    {
+      name: "filterIndicator",
+    },
+    {
+      name: "filterValue",
+    },
+    {
+      name: "mainGroup",
+      value: primaryGroup,
+    },
+    {
+      name: "percentageFormatting",
+      value: percentageFormatting,
+    },
+    {
+      name: "valueFormatting",
+      value: valueFormatting,
+    },
+    {
+      name: "trimZeroFormat",
+      value: "~s",
+    },
+    {
+      name: "numberFormat",
+      update:
+        "{ 'percentage': percentageFormatting, 'value':  data('secondary').length > 1 ? trimZeroFormat : valueFormatting }",
+    },
+    {
+      name: "datatype",
+      value: { percentage: "percentage", value: "count" },
+    },
+    {
+      name: "percentageMaxX",
+      value: percentageMaxX !== "default" ? percentageMaxX : undefined,
+    },
+    {
+      name: "percentageMinX",
+      value: percentageMinX !== "default" ? percentageMinX : undefined,
+    },
+    {
+      name: "valueMaxX",
+      value: valueMaxX !== "default" ? valueMaxX : undefined,
+    },
+    {
+      name: "valueMinX",
+      value: valueMinX !== "default" ? valueMinX : undefined,
+    },
+    {
+      name: "domainMin",
+      update: "Units === 'percentage' ? percentageMinX : valueMinX",
+    },
+    {
+      name: "domainMax",
+      update: "Units === 'percentage' ? percentageMaxX : valueMaxX",
+    },
+    {
+      name: "chartType",
+      value: chartType,
+    },
+    {
+      name: "totalHeight",
+      update: "height",
+    },
+    {
+      name: "interpolate",
+      value: "linear",
+    },
+    {
+      name: "white_mark",
+      value: theme.palette.text.secondary,
+    },
+    {
+      name: "grey_mark",
+      value: theme.palette.chart.text.primary,
+    },
+    // arc signals (pie)
+    {
+      name: "startAngle",
+      value: 0,
+    },
+    {
+      name: "endAngle",
+      value: 6.29,
+    },
+    {
+      name: "padAngle",
+      value: 0,
+    },
+    {
+      name: "innerRadius",
+      value: 55,
+    },
+    {
+      name: "cornerRadius",
+      value: 0,
+    },
+    {
+      name: "sort",
+      value: false,
+    },
+    // treemap signals
+    {
+      name: "layout",
+      value: "squarify",
+    },
+    {
+      name: "aspectRatio",
+      value: 1.6,
+    },
+    // tickCount signals
+    {
+      name: "xTicks",
+      value: xTicks,
+    },
+    {
+      name: "yTicks",
+      value: yTicks,
+    },
+    {
+      name: "maxXScaleValue",
+      update: "extent(domain('xscale'))[1]",
+    },
+    {
+      name: "maxYScaleValue",
+      update: "extent(domain('yscale'))[1]",
+    },
+    {
+      name: "maxSecondaryXScaleValue",
+      update: "extent(domain('s_xscale'))[1]",
+    },
+    {
+      name: "maxSecondaryYScaleValue",
+      update: "extent(domain('s_yscale'))[1]",
+    },
+    // if the maximum value of xaxis is less than xTicks return maximum value as tick Count
+    {
+      name: "primaryXTickCount",
+      update: "maxXScaleValue < xTicks ? maxXScaleValue : xTicks",
+    },
+    {
+      name: "primaryYTickCount",
+      update: "maxYScaleValue < yTicks ? maxYScaleValue : yTicks",
+    },
+    {
+      name: "secondaryXTickCount",
+      update:
+        "maxSecondaryXScaleValue < xTicks ? maxSecondaryXScaleValue : xTicks",
+    },
+    {
+      name: "secondaryYTickCount",
+      update:
+        "maxSecondaryYScaleValue < yTicks ? maxSecondaryYScaleValue : yTicks",
+    },
+    // signals for logo and title => image layout downloads
+    { name: "chartY", value: 0 },
+    { name: "titleX", value: 0 },
+    { name: "titleY", value: 0 },
+    { name: "titleH", value: 0 },
+    { name: "titleGroupY" },
+    { name: "titlefontSize", value: 18 },
+    { name: "titlefontWeight", value: 600 },
+    { name: "titlefont", value: theme.typography.fontFamily },
+    { name: "subtitleX", value: 0 },
+    { name: "subtitleY", update: "titleH - 10" },
+    { name: "subtitlefontSize", value: 10 },
+    { name: "subtitlefont", value: theme.typography.fontFamily },
+    { name: "subtitlefontWeight", value: 400 },
+    { name: "projectX", update: "width" },
+    { name: "projectY", update: "titleY - 10" },
+    { name: "projectAlign", value: "right" },
+    { name: "sourceGroupH", value: 0 },
+    { name: "sourceGroupY" },
+    { name: "sourceX", value: 0 },
+    { name: "sourceY", value: 0 },
+    { name: "sourceFontSize", value: 11 },
+    { name: "sourceFont", value: theme.typography.fontFamily },
+    { name: "sourceFontWeight", value: 400 },
+    { name: "logoX", update: "width - 40" },
+    { name: "logoWidth", value: 0 },
+    { name: "logoUrl" },
+    { name: "logoAspect", value: true },
+    { name: "chartSource" },
+    { name: "chartTitle" },
+    { name: "chartSubtitle" },
+    { name: "projectLogoUrl" },
+    ...filterSignals,
+  ];
+}
diff --git a/packages/hurumap-core/src/Scope/utils.js b/packages/hurumap-core/src/Scope/utils.js
new file mode 100644
index 000000000..58526881b
--- /dev/null
+++ b/packages/hurumap-core/src/Scope/utils.js
@@ -0,0 +1,28 @@
+function idify(string) {
+  return string
+    .replace(/^\s+|\s+$/g, "")
+    .replace(/[^a-z0-9]/g, "")
+    .replace(/\s+/g, "_")
+    .replace(/_+/g, "_");
+}
+
+export function createFiltersForGroups(groups) {
+  // we use a Map here to make it faster to create unique filters
+  const filters = new Map();
+  const signals = new Map();
+  groups.forEach((group) => {
+    const { name } = group;
+    const keyName = idify(name);
+    filters.set(keyName, {
+      type: "filter",
+      expr: `!${keyName}Filter || (${keyName}Filter && datum["${name}"] === ${keyName}FilterValue)`,
+    });
+    signals.set(keyName, { name: `${keyName}Filter`, value: false });
+    signals.set(`${keyName}Value`, { name: `${keyName}FilterValue` });
+  });
+  return {
+    signals: Array.from(signals.values()),
+    filters: Array.from(filters.values()),
+  };
+}
+export default createFiltersForGroups;
diff --git a/packages/hurumap-core/src/index.js b/packages/hurumap-core/src/index.js
index fa932f531..5354c5901 100644
--- a/packages/hurumap-core/src/index.js
+++ b/packages/hurumap-core/src/index.js
@@ -3,3 +3,4 @@ export { default as ChartTooltip } from "./ChartTooltip";
 export { default as LocationTag } from "./LocationTag";
 export { default as LocationHighlight } from "./LocationHighlight";
 export { default as Location } from "./Location";
+export { default as Scope } from "./Scope";
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 44abde036..ee7f55760 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -854,7 +854,7 @@ importers:
         version: 8.2.2(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))
       '@storybook/addon-interactions':
         specifier: ^8.2.2
-        version: 8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))
+        version: 8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))
       '@storybook/addon-links':
         specifier: ^8.2.2
         version: 8.2.2(react@18.3.1)(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))
@@ -866,13 +866,13 @@ importers:
         version: 8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8))
       '@storybook/nextjs':
         specifier: ^8.2.2
-        version: 8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(babel-plugin-macros@3.1.0)(esbuild@0.21.5)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))(next@14.2.5(@babel/core@7.24.8)(@opentelemetry/api@1.9.0)(@playwright/test@1.45.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.69.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.69.4)(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))(type-fest@4.21.0)(typescript@5.5.3)(webpack-hot-middleware@2.26.1)(webpack@5.93.0(esbuild@0.21.5))
+        version: 8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(babel-plugin-macros@3.1.0)(esbuild@0.21.5)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))(next@14.2.5(@babel/core@7.24.8)(@opentelemetry/api@1.9.0)(@playwright/test@1.45.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.69.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.69.4)(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))(type-fest@4.21.0)(typescript@5.5.3)(webpack-hot-middleware@2.26.1)(webpack@5.93.0(esbuild@0.21.5))
       '@storybook/react':
         specifier: ^8.2.2
         version: 8.2.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))(typescript@5.5.3)
       '@storybook/test':
         specifier: ^8.2.2
-        version: 8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))
+        version: 8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))
       '@svgr/webpack':
         specifier: ^8.1.0
         version: 8.1.0(typescript@5.5.3)
@@ -1457,7 +1457,7 @@ importers:
         version: 133.0.0(encoding@0.1.13)
       jest:
         specifier: ^29.7.0
-        version: 29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
+        version: 29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.6.13(@swc/helpers@0.5.5))(@types/node@20.14.10)(typescript@5.5.3))
       next:
         specifier: ^14.2.5
         version: 14.2.5(@babel/core@7.24.8)(@opentelemetry/api@1.9.0)(@playwright/test@1.45.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.69.4)
@@ -1631,7 +1631,7 @@ importers:
     dependencies:
       '@testing-library/jest-dom':
         specifier: ^6.4.6
-        version: 6.4.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(babel-plugin-macros@3.1.0))
+        version: 6.4.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))
       '@testing-library/react':
         specifier: ^14.3.1
         version: 14.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -1769,6 +1769,9 @@ importers:
 
   packages/hurumap-core:
     dependencies:
+      deepmerge:
+        specifier: ^4.3.1
+        version: 4.3.1
       prop-types:
         specifier: ^15.8.1
         version: 15.8.1
@@ -16194,41 +16197,6 @@ snapshots:
       - supports-color
       - ts-node
 
-  '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))':
-    dependencies:
-      '@jest/console': 29.7.0
-      '@jest/reporters': 29.7.0
-      '@jest/test-result': 29.7.0
-      '@jest/transform': 29.7.0
-      '@jest/types': 29.6.3
-      '@types/node': 20.14.10
-      ansi-escapes: 4.3.2
-      chalk: 4.1.2
-      ci-info: 3.9.0
-      exit: 0.1.2
-      graceful-fs: 4.2.11
-      jest-changed-files: 29.7.0
-      jest-config: 29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
-      jest-haste-map: 29.7.0
-      jest-message-util: 29.7.0
-      jest-regex-util: 29.6.3
-      jest-resolve: 29.7.0
-      jest-resolve-dependencies: 29.7.0
-      jest-runner: 29.7.0
-      jest-runtime: 29.7.0
-      jest-snapshot: 29.7.0
-      jest-util: 29.7.0
-      jest-validate: 29.7.0
-      jest-watcher: 29.7.0
-      micromatch: 4.0.7
-      pretty-format: 29.7.0
-      slash: 3.0.0
-      strip-ansi: 6.0.1
-    transitivePeerDependencies:
-      - babel-plugin-macros
-      - supports-color
-      - ts-node
-
   '@jest/environment@29.7.0':
     dependencies:
       '@jest/fake-timers': 29.7.0
@@ -18560,11 +18528,11 @@ snapshots:
       '@storybook/global': 5.0.0
       storybook: 8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8))
 
-  '@storybook/addon-interactions@8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))':
+  '@storybook/addon-interactions@8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))':
     dependencies:
       '@storybook/global': 5.0.0
       '@storybook/instrumenter': 8.2.2(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))
-      '@storybook/test': 8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))
+      '@storybook/test': 8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))
       polished: 4.3.1
       storybook: 8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8))
       ts-dedent: 2.2.0
@@ -19030,7 +18998,7 @@ snapshots:
 
   '@storybook/mdx2-csf@1.1.0': {}
 
-  '@storybook/nextjs@8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(babel-plugin-macros@3.1.0)(esbuild@0.21.5)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))(next@14.2.5(@babel/core@7.24.8)(@opentelemetry/api@1.9.0)(@playwright/test@1.45.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.69.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.69.4)(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))(type-fest@4.21.0)(typescript@5.5.3)(webpack-hot-middleware@2.26.1)(webpack@5.93.0(esbuild@0.21.5))':
+  '@storybook/nextjs@8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(babel-plugin-macros@3.1.0)(esbuild@0.21.5)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))(next@14.2.5(@babel/core@7.24.8)(@opentelemetry/api@1.9.0)(@playwright/test@1.45.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.69.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.69.4)(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))(type-fest@4.21.0)(typescript@5.5.3)(webpack-hot-middleware@2.26.1)(webpack@5.93.0(esbuild@0.21.5))':
     dependencies:
       '@babel/core': 7.24.8
       '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.8)
@@ -19049,7 +19017,7 @@ snapshots:
       '@storybook/builder-webpack5': 8.2.2(esbuild@0.21.5)(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))(typescript@5.5.3)
       '@storybook/preset-react-webpack': 8.2.2(esbuild@0.21.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))(typescript@5.5.3)
       '@storybook/react': 8.2.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))(typescript@5.5.3)
-      '@storybook/test': 8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))
+      '@storybook/test': 8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))
       '@types/node': 18.19.39
       '@types/semver': 7.5.8
       babel-loader: 9.1.3(@babel/core@7.24.8)(webpack@5.93.0(esbuild@0.21.5))
@@ -19375,12 +19343,12 @@ snapshots:
       memoizerific: 1.11.3
       qs: 6.12.3
 
-  '@storybook/test@8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))':
+  '@storybook/test@8.2.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))':
     dependencies:
       '@storybook/csf': 0.1.11
       '@storybook/instrumenter': 8.2.2(storybook@8.2.2(@babel/preset-env@7.24.8(@babel/core@7.24.8)))
       '@testing-library/dom': 10.1.0
-      '@testing-library/jest-dom': 6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))
+      '@testing-library/jest-dom': 6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))
       '@testing-library/user-event': 14.5.2(@testing-library/dom@10.1.0)
       '@vitest/expect': 1.6.0
       '@vitest/spy': 1.6.0
@@ -19670,7 +19638,7 @@ snapshots:
       lz-string: 1.5.0
       pretty-format: 27.5.1
 
-  '@testing-library/jest-dom@6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0))':
+  '@testing-library/jest-dom@6.4.5(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))':
     dependencies:
       '@adobe/css-tools': 4.4.0
       '@babel/runtime': 7.24.8
@@ -19700,7 +19668,7 @@ snapshots:
       '@types/jest': 29.5.12
       jest: 29.7.0(@types/node@20.14.10)
 
-  '@testing-library/jest-dom@6.4.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(babel-plugin-macros@3.1.0))':
+  '@testing-library/jest-dom@6.4.6(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))':
     dependencies:
       '@adobe/css-tools': 4.4.0
       '@babel/runtime': 7.24.8
@@ -21407,21 +21375,6 @@ snapshots:
       - supports-color
       - ts-node
 
-  create-jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)):
-    dependencies:
-      '@jest/types': 29.6.3
-      chalk: 4.1.2
-      exit: 0.1.2
-      graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
-      jest-util: 29.7.0
-      prompts: 2.4.2
-    transitivePeerDependencies:
-      - '@types/node'
-      - babel-plugin-macros
-      - supports-color
-      - ts-node
-
   create-require@1.1.1: {}
 
   cross-spawn@5.1.0:
@@ -22345,7 +22298,7 @@ snapshots:
       '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.3)
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0)
+      eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
       eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
       eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0)
       eslint-plugin-react: 7.34.3(eslint@8.57.0)
@@ -22403,12 +22356,12 @@ snapshots:
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0):
+  eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0):
     dependencies:
       debug: 4.3.5
       enhanced-resolve: 5.17.0
       eslint: 8.57.0
-      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
+      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
       eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
       fast-glob: 3.3.2
       get-tsconfig: 4.7.5
@@ -22504,14 +22457,14 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0):
+  eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
     dependencies:
       debug: 3.2.7
     optionalDependencies:
       '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.3)
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0)
+      eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -22562,7 +22515,7 @@ snapshots:
       doctrine: 2.1.0
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0)
+      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
       hasown: 2.0.2
       is-core-module: 2.14.0
       is-glob: 4.0.3
@@ -24335,25 +24288,6 @@ snapshots:
       - supports-color
       - ts-node
 
-  jest-cli@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)):
-    dependencies:
-      '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
-      '@jest/test-result': 29.7.0
-      '@jest/types': 29.6.3
-      chalk: 4.1.2
-      create-jest: 29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
-      exit: 0.1.2
-      import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
-      jest-util: 29.7.0
-      jest-validate: 29.7.0
-      yargs: 17.7.2
-    transitivePeerDependencies:
-      - '@types/node'
-      - babel-plugin-macros
-      - supports-color
-      - ts-node
-
   jest-config@29.7.0(@types/node@20.14.10):
     dependencies:
       '@babel/core': 7.24.8
@@ -24416,37 +24350,6 @@ snapshots:
       - babel-plugin-macros
       - supports-color
 
-  jest-config@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)):
-    dependencies:
-      '@babel/core': 7.24.8
-      '@jest/test-sequencer': 29.7.0
-      '@jest/types': 29.6.3
-      babel-jest: 29.7.0(@babel/core@7.24.8)
-      chalk: 4.1.2
-      ci-info: 3.9.0
-      deepmerge: 4.3.1
-      glob: 7.2.3
-      graceful-fs: 4.2.11
-      jest-circus: 29.7.0(babel-plugin-macros@3.1.0)
-      jest-environment-node: 29.7.0
-      jest-get-type: 29.6.3
-      jest-regex-util: 29.6.3
-      jest-resolve: 29.7.0
-      jest-runner: 29.7.0
-      jest-util: 29.7.0
-      jest-validate: 29.7.0
-      micromatch: 4.0.7
-      parse-json: 5.2.0
-      pretty-format: 29.7.0
-      slash: 3.0.0
-      strip-json-comments: 3.1.1
-    optionalDependencies:
-      '@types/node': 20.14.10
-      ts-node: 10.9.2(@types/node@20.14.10)(typescript@5.5.3)
-    transitivePeerDependencies:
-      - babel-plugin-macros
-      - supports-color
-
   jest-diff@29.7.0:
     dependencies:
       chalk: 4.1.2
@@ -24708,18 +24611,6 @@ snapshots:
       - supports-color
       - ts-node
 
-  jest@29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)):
-    dependencies:
-      '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
-      '@jest/types': 29.6.3
-      import-local: 3.1.0
-      jest-cli: 29.7.0(@types/node@20.14.10)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
-    transitivePeerDependencies:
-      - '@types/node'
-      - babel-plugin-macros
-      - supports-color
-      - ts-node
-
   jiti@1.21.6: {}
 
   jmespath@0.16.0: {}
@@ -28523,25 +28414,6 @@ snapshots:
     optionalDependencies:
       '@swc/core': 1.6.13(@swc/helpers@0.5.5)
 
-  ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3):
-    dependencies:
-      '@cspotcode/source-map-support': 0.8.1
-      '@tsconfig/node10': 1.0.11
-      '@tsconfig/node12': 1.0.11
-      '@tsconfig/node14': 1.0.3
-      '@tsconfig/node16': 1.0.4
-      '@types/node': 20.14.10
-      acorn: 8.12.1
-      acorn-walk: 8.3.3
-      arg: 4.1.3
-      create-require: 1.1.1
-      diff: 4.0.2
-      make-error: 1.3.6
-      typescript: 5.5.3
-      v8-compile-cache-lib: 3.0.1
-      yn: 3.1.1
-    optional: true
-
   ts-pnp@1.2.0(typescript@5.5.3):
     optionalDependencies:
       typescript: 5.5.3

From baed227fa6a53f4c34a8958a33003047cf08d499 Mon Sep 17 00:00:00 2001
From: Kevin Koech <koechkevin92@gmail.com>
Date: Tue, 30 Jul 2024 09:29:18 +0300
Subject: [PATCH 2/4] Barchart import fix

---
 .../src/components/HURUmap/Chart/configureScope.js             | 3 ++-
 apps/pesayetu/src/components/HURUmap/Chart/configureScope.js   | 3 ++-
 packages/hurumap-core/src/Scope/BarChartScope.js               | 1 -
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/apps/climatemappedafrica/src/components/HURUmap/Chart/configureScope.js b/apps/climatemappedafrica/src/components/HURUmap/Chart/configureScope.js
index 486598bec..1c8376c72 100644
--- a/apps/climatemappedafrica/src/components/HURUmap/Chart/configureScope.js
+++ b/apps/climatemappedafrica/src/components/HURUmap/Chart/configureScope.js
@@ -1,4 +1,4 @@
-import Scope from "@hurumap/core";
+import { Scope } from "@hurumap/core";
 
 import DonutChartScope from "./DonutChartScope";
 import LineChartScope from "./LineChartScope";
@@ -12,6 +12,7 @@ import { hurumapArgs } from "@/climatemappedafrica/config";
 import theme from "@/climatemappedafrica/theme";
 
 const { BarChartScope } = Scope;
+
 export default function configureScope(
   indicator,
   secondaryIndicator = null,
diff --git a/apps/pesayetu/src/components/HURUmap/Chart/configureScope.js b/apps/pesayetu/src/components/HURUmap/Chart/configureScope.js
index 1cb9a6173..cc0be0067 100644
--- a/apps/pesayetu/src/components/HURUmap/Chart/configureScope.js
+++ b/apps/pesayetu/src/components/HURUmap/Chart/configureScope.js
@@ -1,4 +1,4 @@
-import Scope from "@hurumap/core";
+import { Scope } from "@hurumap/core";
 
 import DonutChartScope from "./DonutChartScope";
 import LineChartScope from "./LineChartScope";
@@ -12,6 +12,7 @@ import { hurumapArgs } from "@/pesayetu/config";
 import theme from "@/pesayetu/theme";
 
 const { BarChartScope } = Scope;
+
 export default function configureScope(
   indicator,
   secondaryIndicator = null,
diff --git a/packages/hurumap-core/src/Scope/BarChartScope.js b/packages/hurumap-core/src/Scope/BarChartScope.js
index 23722cc5a..7a2fbcbe3 100644
--- a/packages/hurumap-core/src/Scope/BarChartScope.js
+++ b/packages/hurumap-core/src/Scope/BarChartScope.js
@@ -17,7 +17,6 @@ export default function BarChartScope({
   const { parentLabel } = config;
 
   const { primary_group: primaryGroup } = metadata;
-
   return merge(
     Scope({
       primaryData,

From c5f730e3627ade607d8206df3752a4cbd57e0d5a Mon Sep 17 00:00:00 2001
From: Kevin Koech <koechkevin92@gmail.com>
Date: Tue, 30 Jul 2024 14:07:10 +0300
Subject: [PATCH 3/4] Move deep merge to peer dependencies

---
 packages/hurumap-core/package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/hurumap-core/package.json b/packages/hurumap-core/package.json
index 2f72ab77e..ee0a94bff 100644
--- a/packages/hurumap-core/package.json
+++ b/packages/hurumap-core/package.json
@@ -61,13 +61,13 @@
     "@emotion/styled": "^11.11.0",
     "@mui/material": "^5.14.0",
     "@mui/utils": "^5.12.3",
+    "deepmerge": "^4.3.1",
     "leaflet": "^1.9.4",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
     "react-leaflet": "^4.2.1"
   },
   "dependencies": {
-    "deepmerge": "^4.3.1",
     "prop-types": "^15.8.1"
   }
 }

From fd319b82ef9705e2e61ebe9f07c7d41accfcc2c0 Mon Sep 17 00:00:00 2001
From: Kevin Koech <koechkevin92@gmail.com>
Date: Tue, 30 Jul 2024 16:00:20 +0300
Subject: [PATCH 4/4] Fix regex

---
 packages/hurumap-core/src/Scope/utils.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/hurumap-core/src/Scope/utils.js b/packages/hurumap-core/src/Scope/utils.js
index 58526881b..8abb9837f 100644
--- a/packages/hurumap-core/src/Scope/utils.js
+++ b/packages/hurumap-core/src/Scope/utils.js
@@ -1,5 +1,6 @@
 function idify(string) {
   return string
+    .toLowerCase()
     .replace(/^\s+|\s+$/g, "")
     .replace(/[^a-z0-9]/g, "")
     .replace(/\s+/g, "_")