diff --git a/apps/climatemappedafrica/src/components/HURUmap/Chart/configureScope.js b/apps/climatemappedafrica/src/components/HURUmap/Chart/configureScope.js index 8dcb06bb5..961c7fc81 100644 --- a/apps/climatemappedafrica/src/components/HURUmap/Chart/configureScope.js +++ b/apps/climatemappedafrica/src/components/HURUmap/Chart/configureScope.js @@ -1,6 +1,5 @@ import { Scope } from "@hurumap/core"; -import LineChartScope from "./LineChartScope"; import MultiLineChartScope from "./MultiLineChartScope"; import StackedChartScope from "./StackedChartScope"; import TreemapChartScope from "./TreemapChartScope"; @@ -10,7 +9,7 @@ import VerticalStackedChartScope from "./VerticalStackedChartScope"; import { hurumapArgs } from "@/climatemappedafrica/config"; import theme from "@/climatemappedafrica/theme"; -const { BarChartScope, DonutChartScope } = Scope; +const { BarChartScope, LineChartScope, DonutChartScope } = Scope; export default function configureScope( indicator, @@ -30,6 +29,7 @@ export default function configureScope( let vegaSpec; const chartType = configuration?.chart_type?.toLowerCase(); + /** * @deprecated Use scopeOptions for implementing new charts * This will be completely removed once all charts scopes @@ -47,6 +47,7 @@ export default function configureScope( isCompare, isMobile, ]; + const scopeOptions = { primaryData: indicator?.data, metadata: indicator?.metadata, @@ -60,12 +61,13 @@ export default function configureScope( theme, args: hurumapArgs, }; + switch (chartType) { case "line": if (configuration?.stacked_field) { vegaSpec = MultiLineChartScope(..._scopeOptions); } else { - vegaSpec = LineChartScope(..._scopeOptions); + vegaSpec = LineChartScope(scopeOptions); } break; case "donut": diff --git a/apps/pesayetu/src/components/HURUmap/Chart/LineChartScope.js b/apps/pesayetu/src/components/HURUmap/Chart/LineChartScope.js deleted file mode 100644 index 5781d1e7b..000000000 --- a/apps/pesayetu/src/components/HURUmap/Chart/LineChartScope.js +++ /dev/null @@ -1,582 +0,0 @@ -import merge from "deepmerge"; - -import Scope from "./Scope"; - -import theme from "@/pesayetu/theme"; - -export default function LineChartScope( - primaryData, - metadata, - config, - secondaryData, - primaryParentData, - secondaryParentData, - profileNames, - isCompare, - isMobile, -) { - const { - parentLabel, - xScaleType, - xScaleFormat, - xScaleMobileFormat, - timeUnit, - timeFormat, - } = config; - - const { primary_group: primaryGroup } = metadata; - - const timeTransform = - xScaleType === "time" - ? [ - { - type: "formula", - as: primaryGroup, - expr: "timeParse(datum[mainGroup], timeFormat)", - }, - { - type: "timeunit", - units: timeUnit, - field: primaryGroup, - }, - ] - : undefined; - - return merge( - Scope( - primaryData, - metadata, - config, - secondaryData, - primaryParentData, - secondaryParentData, - "line", - timeTransform, - ), - { - height: isMobile && isCompare && secondaryData?.length > 1 ? 620 : 310, - signals: [ - { - name: "height", - value: isMobile && isCompare && secondaryData?.length > 1 ? 620 : 310, - }, - { - name: "isMobile", - value: isMobile, - }, - { - name: "isCompare", - value: isCompare, - }, - { - name: "timeFormat", - value: timeFormat || "%b", - }, - ], - scales: [ - { - name: "xscale", - type: xScaleType || "point", - domain: { - data: "primary_formatted", - field: primaryGroup, - }, - range: [ - 15, - { - signal: - "data('secondary').length > 1 && !isMobile ? width/2 - 30 : width", - }, - ], - }, - { - name: "s_xscale", - type: xScaleType || "point", - domain: { - data: "secondary_formatted", - field: primaryGroup, - }, - range: [ - 15, - { - signal: - "!isMobile && data('secondary').length > 1 ? width/2 - 30 : data('secondary').length > 1 ? width : 0", - }, - ], - }, - { - name: "yscale", - type: "linear", - domain: { - data: "primary_formatted", - field: { signal: "datatype[Units]" }, - }, - range: [{ signal: "isCompare && isMobile ? height/2: height" }, 0], - nice: { signal: "primaryYTickCount" }, - zero: false, - clamp: true, - }, - { - name: "s_yscale", - type: "linear", - domain: { - data: "secondary_formatted", - field: { signal: "datatype[Units]" }, - }, - range: [{ signal: "isCompare && isMobile ? height/2: height" }, 0], - nice: { signal: "secondaryYTickCount" }, - zero: false, - clamp: true, - }, - { - name: "color", - type: "ordinal", - range: "category", - domain: { - data: "primary_formatted", - field: primaryGroup, - }, - }, - { - name: "secondary_color", - type: "ordinal", - range: "secondary", - domain: { - data: "secondary_formatted", - field: primaryGroup, - }, - }, - { - name: "parent_color_scale", - type: "ordinal", - range: "category", - domain: [parentLabel], - }, - { - 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], - }, - ], - - marks: [ - { - type: "group", - name: "primary_lines", - encode: { - update: { - x: { value: 0 }, - y: { signal: "chartY" }, - height: { - signal: - "isMobile && isCompare && data('secondary').length > 1 ? height/2: height", - }, - width: { - signal: - "isMobile && data('secondary').length > 1 ? width : width/2", - }, - }, - }, - legends: isCompare - ? [ - { - orient: "top", - fill: "legend_primary_scale", - labelFontWeight: "bold", - labelColor: "#666", - labelFont: theme.typography.fontFamily, - }, - ] - : null, - axes: [ - { - orient: "left", - scale: "yscale", - domain: false, - domainOpacity: 0.5, - tickSize: 0, - tickCount: { signal: "primaryYTickCount" }, - labelPadding: 6, - zindex: 1, - format: { signal: "numberFormat[Units]" }, - }, - { - orient: "bottom", - scale: "xscale", - bandPosition: 0, - domainOpacity: 0.5, - tickSize: 0, - grid: true, - labelPadding: 6, - labelFlush: true, - labelOverlap: true, - formatType: xScaleType, - format: - (isMobile ? xScaleMobileFormat : xScaleFormat) || undefined, - }, - ], - marks: [ - { - name: "line", - from: { data: "primary_formatted" }, - type: "line", - encode: { - enter: { - x: { scale: "xscale", field: { signal: "mainGroup" } }, - stroke: { scale: "color", field: { signal: "mainGroup" } }, - y: { scale: "yscale", field: { signal: "datatype[Units]" } }, - strokeWidth: { value: 2 }, - }, - update: { - x: { scale: "xscale", field: { signal: "mainGroup" } }, - y: { scale: "yscale", field: { signal: "datatype[Units]" } }, - interpolate: { signal: "interpolate" }, - strokeOpacity: { value: 1 }, - }, - }, - }, - { - name: "line symbol", - from: { data: "primary_formatted" }, - type: "symbol", - encode: { - enter: { - x: { scale: "xscale", field: { signal: "mainGroup" } }, - y: { scale: "yscale", field: { signal: "datatype[Units]" } }, - fill: { value: theme.palette.primary.main }, - }, - update: { - x: { scale: "xscale", field: { signal: "mainGroup" } }, - y: { scale: "yscale", field: { signal: "datatype[Units]" } }, - size: { value: 5 }, - tooltip: { - signal: - "{'group': datum[mainGroup], 'count': format(datum.count, numberFormat.value)}", - }, - }, - hover: { - size: { value: 70 }, - }, - }, - }, - ], - }, - { - type: "group", - name: "primary_parent_line", - encode: { - update: { - x: { value: 0 }, - y: { signal: "chartY" }, - height: { - signal: - "isMobile && isCompare && data('secondary').length > 1 ? height/2: height", - }, - }, - }, - legends: - primaryParentData?.length > 1 - ? [ - { - fill: "parent_color_scale", - orient: "none", - legendX: { - signal: - "data('secondary').length > 1 && !isMobile? (width / 2 ) - 100 : width - 85", - }, - legendY: { value: -35 }, - 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: "line", - from: { data: "primary_parent_formatted" }, - type: "line", - encode: { - enter: { - x: { scale: "xscale", field: { signal: "mainGroup" } }, - stroke: { value: theme.palette.chart.text.primary }, - y: { scale: "yscale", field: { signal: "datatype[Units]" } }, - strokeWidth: { value: 2 }, - strokeDash: { value: [2, 2] }, - }, - update: { - x: { scale: "xscale", field: { signal: "mainGroup" } }, - y: { scale: "yscale", field: { signal: "datatype[Units]" } }, - interpolate: { signal: "interpolate" }, - strokeOpacity: { value: 1 }, - }, - }, - }, - { - name: "line symbol", - from: { data: "primary_parent_formatted" }, - type: "symbol", - encode: { - enter: { - x: { scale: "xscale", field: { signal: "mainGroup" } }, - y: { scale: "yscale", field: { signal: "datatype[Units]" } }, - fill: { value: theme.palette.chart.text.primary }, - }, - update: { - x: { scale: "xscale", field: { signal: "mainGroup" } }, - y: { scale: "yscale", field: { signal: "datatype[Units]" } }, - size: { value: 5 }, - tooltip: { - signal: - "{'group': datum[mainGroup], 'count': format(datum.count, numberFormat.value)}", - }, - }, - hover: { - size: { value: 70 }, - }, - }, - }, - ], - }, - { - type: "group", - name: "secondary_lines", - encode: { - update: { - x: { - signal: - "!isMobile && data('secondary').length > 1 ? width / 2 + 30 : 0", - }, - y: { - signal: - "isMobile && data('secondary').length > 1 ? height/2 + 60: data('secondary').length > 1 ? chartY: height + 60", - }, - height: { - signal: - "isMobile && data('secondary').length > 1 ? height/2: data('secondary').length > 1 ? height: 0", - }, - width: { - signal: - "!isMobile && data('secondary').length > 1 ? (width / 2 ) : data('secondary').length > 1 ? width : 0", - }, - }, - }, - legends: isCompare - ? [ - { - orient: "top", - fill: "legend_secondary_scale", - labelFontWeight: "bold", - labelLimit: 400, - labelColor: "#666", - labelFont: theme.typography.fontFamily, - }, - ] - : null, - axes: - secondaryData?.length > 1 - ? [ - { - orient: "left", - scale: "s_yscale", - domain: false, - domainOpacity: 0.5, - tickSize: 0, - tickCount: { signal: "secondaryYTickCount" }, - labelPadding: 6, - zindex: 1, - format: { signal: "numberFormat[Units]" }, - }, - { - orient: "bottom", - scale: "s_xscale", - bandPosition: 0, - domainOpacity: 0.5, - tickSize: 0, - grid: true, - labelPadding: 6, - labelFlush: true, - labelOverlap: true, - formatType: xScaleType, - format: - (isMobile ? xScaleMobileFormat : xScaleFormat) || - undefined, - }, - ] - : null, - marks: [ - { - name: "line", - from: { data: "secondary_formatted" }, - type: "line", - encode: { - enter: { - x: { scale: "s_xscale", field: { signal: "mainGroup" } }, - stroke: { - scale: "secondary_color", - field: { signal: "mainGroup" }, - }, - y: { - scale: "s_yscale", - field: { signal: "datatype[Units]" }, - }, - strokeWidth: { value: 2 }, - }, - update: { - interpolate: { signal: "interpolate" }, - strokeOpacity: { value: 1 }, - }, - }, - }, - { - name: "line symbol", - from: { data: "secondary_formatted" }, - type: "symbol", - encode: { - enter: { - x: { scale: "s_xscale", field: { signal: "mainGroup" } }, - y: { - scale: "s_yscale", - field: { signal: "datatype[Units]" }, - }, - fill: { value: theme.palette.secondary.main }, - }, - update: { - size: { value: 5 }, - tooltip: { - signal: - "{'group': datum[mainGroup], 'count': format(datum.count, numberFormat.value)}", - }, - }, - hover: { - size: { value: 70 }, - }, - }, - }, - ], - }, - { - type: "group", - name: "secondary_parent_line", - encode: { - update: { - x: { - signal: - "!isMobile && data('secondary').length > 1 ? width / 2 + 30 : 0", - }, - y: { - signal: - "isMobile && data('secondary').length > 1 ? height/2 + 30: data('secondary').length > 1 ? chartY: height + 40", - }, - height: { - signal: - "isMobile && data('secondary').length > 1 ? height/2: 0", - }, - }, - }, - legends: - secondaryParentData?.length > 1 - ? [ - { - fill: "parent_color_scale", - offset: 20, - orient: "none", - legendX: { - signal: "isMobile ? width -90: (width / 2 ) - 120", - }, - legendY: { value: isMobile ? -10 : -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: - secondaryParentData?.length > 1 - ? [ - { - name: "line", - from: { data: "secondary_parent_formatted" }, - type: "line", - encode: { - enter: { - x: { - scale: "s_xscale", - field: { signal: "mainGroup" }, - }, - stroke: { value: theme.palette.chart.text.primary }, - y: { - scale: "s_yscale", - field: { signal: "datatype[Units]" }, - }, - strokeWidth: { value: 2 }, - strokeDash: { value: [2, 2] }, - }, - update: { - interpolate: { signal: "interpolate" }, - strokeOpacity: { value: 1 }, - }, - }, - }, - { - name: "line symbol", - from: { data: "secondary_parent_formatted" }, - type: "symbol", - encode: { - enter: { - x: { - scale: "s_xscale", - field: { signal: "mainGroup" }, - }, - y: { - scale: "s_yscale", - field: { signal: "datatype[Units]" }, - }, - fill: { value: theme.palette.chart.text.primary }, - }, - update: { - size: { value: 5 }, - tooltip: { - signal: - "{'group': datum[mainGroup], 'count': format(datum.count, numberFormat.value)}", - }, - }, - hover: { - size: { value: 70 }, - }, - }, - }, - ] - : null, - }, - ], - }, - ); -} diff --git a/apps/pesayetu/src/components/HURUmap/Chart/configureScope.js b/apps/pesayetu/src/components/HURUmap/Chart/configureScope.js index 5b549099d..f32afbbf6 100644 --- a/apps/pesayetu/src/components/HURUmap/Chart/configureScope.js +++ b/apps/pesayetu/src/components/HURUmap/Chart/configureScope.js @@ -1,6 +1,5 @@ import { Scope } from "@hurumap/core"; -import LineChartScope from "./LineChartScope"; import MultiLineChartScope from "./MultiLineChartScope"; import StackedChartScope from "./StackedChartScope"; import TreemapChartScope from "./TreemapChartScope"; @@ -10,7 +9,7 @@ import VerticalStackedChartScope from "./VerticalStackedChartScope"; import { hurumapArgs } from "@/pesayetu/config"; import theme from "@/pesayetu/theme"; -const { BarChartScope, DonutChartScope } = Scope; +const { BarChartScope, LineChartScope, DonutChartScope } = Scope; export default function configureScope( indicator, @@ -30,6 +29,7 @@ export default function configureScope( let vegaSpec; const chartType = configuration?.chart_type?.toLowerCase(); + /** * @deprecated Use scopeOptions for implementing new charts * This will be completely removed once all charts scopes @@ -47,6 +47,7 @@ export default function configureScope( isCompare, isMobile, ]; + const scopeOptions = { primaryData: indicator?.data, metadata: indicator?.metadata, @@ -60,12 +61,13 @@ export default function configureScope( theme, args: hurumapArgs, }; + switch (chartType) { case "line": if (configuration?.stacked_field) { vegaSpec = MultiLineChartScope(..._scopeOptions); } else { - vegaSpec = LineChartScope(..._scopeOptions); + vegaSpec = LineChartScope(scopeOptions); } break; case "donut": diff --git a/apps/climatemappedafrica/src/components/HURUmap/Chart/LineChartScope.js b/packages/hurumap-core/src/Scope/LineChartScope.js similarity index 98% rename from apps/climatemappedafrica/src/components/HURUmap/Chart/LineChartScope.js rename to packages/hurumap-core/src/Scope/LineChartScope.js index a40eb11dc..7311718a7 100644 --- a/apps/climatemappedafrica/src/components/HURUmap/Chart/LineChartScope.js +++ b/packages/hurumap-core/src/Scope/LineChartScope.js @@ -2,9 +2,7 @@ import merge from "deepmerge"; import Scope from "./Scope"; -import theme from "@/climatemappedafrica/theme"; - -export default function LineChartScope( +export default function LineChartScope({ primaryData, metadata, config, @@ -14,7 +12,9 @@ export default function LineChartScope( profileNames, isCompare, isMobile, -) { + theme, + args, +}) { const { parentLabel, xScaleType, @@ -23,7 +23,6 @@ export default function LineChartScope( timeUnit, timeFormat, } = config; - const { primary_group: primaryGroup } = metadata; const timeTransform = @@ -43,16 +42,18 @@ export default function LineChartScope( : undefined; return merge( - Scope( + Scope({ primaryData, metadata, config, secondaryData, primaryParentData, secondaryParentData, - "line", + chartType: "line", timeTransform, - ), + theme, + args, + }), { height: isMobile && isCompare && secondaryData?.length > 1 ? 620 : 310, signals: [ @@ -112,6 +113,7 @@ export default function LineChartScope( field: { signal: "datatype[Units]" }, }, range: [{ signal: "isCompare && isMobile ? height/2: height" }, 0], + // TODO: explore why adding nice breaks the chart with error: Error: Cycle detected in dataflow graph. // nice: { signal: "primaryYTickCount" }, zero: false, clamp: true, @@ -124,6 +126,7 @@ export default function LineChartScope( field: { signal: "datatype[Units]" }, }, range: [{ signal: "isCompare && isMobile ? height/2: height" }, 0], + // TODO: explore why adding nice breaks the chart with error: Error: Cycle detected in dataflow graph. // nice: { signal: "secondaryYTickCount" }, zero: false, clamp: true, diff --git a/packages/hurumap-core/src/Scope/index.js b/packages/hurumap-core/src/Scope/index.js index 596db59b1..453c25931 100644 --- a/packages/hurumap-core/src/Scope/index.js +++ b/packages/hurumap-core/src/Scope/index.js @@ -1,9 +1,11 @@ import BarChartScope from "./BarChartScope"; import DonutChartScope from "./DonutChartScope"; +import LineChartScope from "./LineChartScope"; import Scope from "./Scope"; export default { Scope, BarChartScope, + LineChartScope, DonutChartScope, };