diff --git a/src/app/elements/page.tsx b/src/app/elements/page.tsx
index db7181ae..dc9a26e5 100644
--- a/src/app/elements/page.tsx
+++ b/src/app/elements/page.tsx
@@ -5,12 +5,12 @@ import { useState } from 'react';
import AccordionContainer from '@/components/Accordions/AccordionContainer';
import { CustomButton } from '@/components/Buttons/CustomButton';
import { CategoricalChart } from '@/components/Charts/CategoricalChart';
-import { LineChart } from '@/components/Charts/LineChart';
+import { ContinuousChart } from '@/components/Charts/ContinuousChart';
import MapSkeleton from '@/components/Map/MapSkeleton';
import SearchBar from '@/components/Search/SearchBar';
import { CategoricalChartData } from '@/domain/entities/charts/CategoricalChartData.ts';
-import { LineChartData } from '@/domain/entities/charts/LineChartData.ts';
-import { LineChartDataType } from '@/domain/enums/LineChartDataType.ts';
+import { ContinuousChartData } from '@/domain/entities/charts/ContinuousChartData.ts';
+import { ContinuousChartDataType } from '@/domain/enums/ContinuousChartDataType.ts';
import AccordionsOperations from '@/operations/accordions/AccordionOperations';
import { ReactComponent as FoodSvg } from '../../../public/Images/FoodConsumption.svg';
@@ -21,8 +21,8 @@ import { ReactComponent as FoodSvg } from '../../../public/Images/FoodConsumptio
*/
export default async function Elements() {
const [searchTerm, setSearchTerm] = useState('');
- const simpleAndSmallLineChartData: LineChartData = {
- type: LineChartDataType.LINE_CHART_DATA,
+ const simpleAndSmallContinuousChartData: ContinuousChartData = {
+ type: ContinuousChartDataType.LINE_CHART_DATA,
xAxisType: 'linear',
lines: [
{
@@ -37,8 +37,8 @@ export default async function Elements() {
],
};
- const maxedOutLineChartData: LineChartData = {
- type: LineChartDataType.LINE_CHART_DATA,
+ const maxedOutContinuousChartData: ContinuousChartData = {
+ type: ContinuousChartDataType.LINE_CHART_DATA,
xAxisType: 'linear',
yAxisLabel: 'yield',
lines: [
@@ -97,8 +97,8 @@ export default async function Elements() {
],
};
- const predictionDummyChartData: LineChartData = {
- type: LineChartDataType.LINE_CHART_DATA,
+ const predictionDummyChartData: ContinuousChartData = {
+ type: ContinuousChartDataType.LINE_CHART_DATA,
xAxisType: 'linear',
yAxisLabel: 'Mill',
predictionVerticalLineX: 3,
@@ -129,7 +129,7 @@ export default async function Elements() {
],
};
- const categoricalDummyChartData1: CategoricalChartData = {
+ const categoricalDummyChartData: CategoricalChartData = {
yAxisLabel: 'Mill',
categories: [
{
@@ -143,8 +143,8 @@ export default async function Elements() {
],
};
- const emptyDummyChartData: LineChartData = {
- type: LineChartDataType.LINE_CHART_DATA,
+ const emptyDummyChartData: ContinuousChartData = {
+ type: ContinuousChartDataType.LINE_CHART_DATA,
xAxisType: 'linear',
yAxisLabel: 'Mill',
predictionVerticalLineX: 3,
@@ -174,8 +174,8 @@ export default async function Elements() {
diff --git a/src/components/Charts/CategoricalChart.tsx b/src/components/Charts/CategoricalChart.tsx
index 8f599c77..05d08646 100644
--- a/src/components/Charts/CategoricalChart.tsx
+++ b/src/components/Charts/CategoricalChart.tsx
@@ -1,8 +1,7 @@
'use client';
import Highcharts from 'highcharts';
-import { useTheme } from 'next-themes';
-import { useEffect, useState } from 'react';
+import { useState } from 'react';
import { ChartContainer } from '@/components/Charts/helpers/ChartContainer';
import { ChartType } from '@/domain/enums/ChartType.ts';
@@ -11,7 +10,7 @@ import CategoricalChartOperations from '@/operations/charts/CategoricalChartOper
/**
* The `CategoricalChart` component is a box that primarily renders a title, description text, and a bar chart.
- * It should be used to plot categorical data. For continues data please use the `LineChart` component.
+ * It should be used to plot categorical data. For continues data please use the `ContinuousChart` component.
* This component has a width of 100%, so it adjusts to the width of its parent element in which it is used.
* The height of the entire box depends on the provided text, while the chart itself has a fixed height.
* It also provides the option to open the chart in a full-screen modal, where one can download the data as well.
@@ -22,6 +21,8 @@ import CategoricalChartOperations from '@/operations/charts/CategoricalChartOper
* @param small when selected, all components in the line chart box become slightly smaller (optional)
* @param noPadding when selected, the main box has no padding on all sides (optional)
* @param transparentBackground when selected, the background of the entire component is transparent (optional)
+ * @param chartHeight with this parameter, the height of the actual chart can be set to a fixed value in pixels. If
+ * chartHeight is not specified, the chart is given a fixed default height depending on `small`.
* @param disableExpandable when selected, the functionality to open the chart in a larger modal is disabled (optional)
* @param disablePieChartSwitch when selected, the functionality to switch to a pie chart is disabled (optional)
* @param disableDownload when selected, the functionality to download the chart is disabled (optional)
@@ -33,23 +34,21 @@ export function CategoricalChart({
small,
noPadding,
transparentBackground,
+ chartHeight,
disableExpandable,
disablePieChartSwitch,
disableDownload,
}: CategoricalChartProps) {
- const { theme } = useTheme();
-
- // build chart options for 'Highcharts'
- const defaultChartOptions: Highcharts.Options | undefined = CategoricalChartOperations.getHighChartOptions(data);
-
// controlling if a bar or pie chart is rendered; bar chart is the default
- const [showPieChart, setShowPieChart] = useState(false);
- const [chartOptions, setChartOptions] = useState(defaultChartOptions);
+ const [showPieChart, setShowPieChart] = useState(false);
+ const [chartOptions, setChartOptions] = useState(
+ CategoricalChartOperations.getHighChartOptions(data, showPieChart)
+ );
- // handling the bar and pie chart switch and the theme switch;
- useEffect(() => {
+ // function to update/recalculate the chart options
+ const recalculateChartOptions = () => {
setChartOptions(CategoricalChartOperations.getHighChartOptions(data, showPieChart));
- }, [showPieChart, theme, data]);
+ };
const alternativeSwitchButtonProps = disablePieChartSwitch
? undefined
@@ -62,13 +61,15 @@ export function CategoricalChart({
return (
{
- return LineChartOperations.getDistinctXAxisValues(lineChartData).length;
- }, [lineChartData]);
+ return ContinuousChartOperations.getDistinctXAxisValues(continuousChartData).length;
+ }, [continuousChartData]);
+ const [selectedXAxisRange, setSelectedXAxisRange] = useState([0, xAxisLength - 1]);
+ // we have to make sure that if the data changes we update the XAxis slider configuration as well
useEffect(() => {
setSelectedXAxisRange([0, xAxisLength - 1]);
}, [xAxisLength]);
// controlling if a line or bar chart is rendered; line chart is the default
- const [showBarChart, setShowBarChart] = useState(false);
- const [chartOptions, setChartOptions] = useState(lineChartOptions);
+ const [showBarChart, setShowBarChart] = useState(false);
+ const [chartOptions, setChartOptions] = useState(
+ ContinuousChartOperations.getHighChartOptions(continuousChartData)
+ );
- // handling the line and bar chart switch and the theme switch;
- // also handling changing the x-axis range using the `LineChartXAxisSlider`;
- // special: if the selected x-axis range has length 1 -> bar chart is displayed
- useEffect(() => {
+ // function to update/recalculate the chart options
+ const recalculateChartOptions = () => {
+ // also handling changing the x-axis range using the `ChartXAxisSlider`;
+ // special: if the selected x-axis range has length 1 -> bar chart is displayed
if (showBarChart || selectedXAxisRange[1] - selectedXAxisRange[0] === 0) {
setChartOptions(
- LineChartOperations.getHighChartOptions(lineChartData, selectedXAxisRange[0], selectedXAxisRange[1], true)
+ ContinuousChartOperations.getHighChartOptions(
+ continuousChartData,
+ selectedXAxisRange[0],
+ selectedXAxisRange[1],
+ true
+ )
);
} else {
setChartOptions(
- LineChartOperations.getHighChartOptions(lineChartData, selectedXAxisRange[0], selectedXAxisRange[1])
+ ContinuousChartOperations.getHighChartOptions(continuousChartData, selectedXAxisRange[0], selectedXAxisRange[1])
);
}
- }, [showBarChart, theme, selectedXAxisRange, lineChartData]);
+ };
// chart slider props - to manipulate the shown x-axis range
const sliderProps = disableXAxisSlider
@@ -105,13 +112,15 @@ export function LineChart({
return (
(null);
+ const [chartKey, setChartKey] = useState(0);
// full screen modal state handling
const { isOpen, onOpen, onClose, onOpenChange } = useDisclosure();
+ // handling the theme switch
+ useEffect(() => {
+ // `theme` change does not guarantee that the NextUI CSS colors have already been changed;
+ // therefore we synchronize the update with the next repaint cycle, ensuring the CSS variables are updated
+ const rafId = requestAnimationFrame(() => {
+ recalculateChartOptions();
+ setChartKey((prev) => prev + 1); // forces chart to remount -> chart animation will be re-triggered
+ });
+ return () => cancelAnimationFrame(rafId);
+ }, [theme]);
+
+ // handling chart type switch, data changes and slider changes
+ useEffect(() => {
+ recalculateChartOptions();
+ setChartKey((prev) => prev + 1); // forces chart to remount -> chart animation will be re-triggered
+ }, [alternativeSwitchButtonProps?.showAlternativeChart, sliderProps?.selectedSliderRange, chartData]);
+
// handling the x-axis range slider visibility
const [showSlider, setShowSlider] = useState(false);
@@ -122,10 +145,11 @@ export function ChartContainer({
highcharts={Highcharts}
options={chartOptions}
ref={chartRef}
+ key={chartKey}
containerProps={{
style: {
width: '100%',
- height: `${CHART_HEIGHT}rem`,
+ height: CHART_HEIGHT,
borderRadius: '0 0 0.375rem 0.375rem',
},
}}
diff --git a/src/components/Charts/helpers/ChartModal.tsx b/src/components/Charts/helpers/ChartModal.tsx
index 9e598cda..e59fad15 100644
--- a/src/components/Charts/helpers/ChartModal.tsx
+++ b/src/components/Charts/helpers/ChartModal.tsx
@@ -3,7 +3,8 @@ import { Modal, ModalBody, ModalContent, ModalFooter, ModalHeader } from '@nextu
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import { Minus } from 'iconsax-react';
-import { useRef } from 'react';
+import { useTheme } from 'next-themes';
+import { useEffect, useRef, useState } from 'react';
import ChartAlternativeSwitchButton from '@/components/Charts/helpers/buttons/ChartAlternativeSwitchButton';
import ChartDownloadButton from '@/components/Charts/helpers/buttons/ChartDownloadButton';
@@ -30,7 +31,15 @@ export function ChartModal({
showSlider,
setShowSlider,
}: ChartModalProps) {
+ const { theme } = useTheme();
+
const chartRef = useRef(null);
+ const [chartKey, setChartKey] = useState(0);
+
+ // if chart changes -> force chart to remount -> chart animation will be re-triggered
+ useEffect(() => {
+ setChartKey((prev) => prev + 1);
+ }, [theme, alternativeSwitchButtonProps?.showAlternativeChart, sliderProps?.selectedSliderRange, chartData]);
return (
diff --git a/src/components/Charts/helpers/buttons/ChartSliderButton.tsx b/src/components/Charts/helpers/buttons/ChartSliderButton.tsx
index 5b108ac0..48d1dc3d 100644
--- a/src/components/Charts/helpers/buttons/ChartSliderButton.tsx
+++ b/src/components/Charts/helpers/buttons/ChartSliderButton.tsx
@@ -2,12 +2,12 @@ import { Button } from '@nextui-org/button';
import { Settings } from 'iconsax-react';
import { Tooltip } from '@/components/Tooltip/Tooltip';
-import { LineChartSliderButtonProps } from '@/domain/props/ChartContainerProps';
+import { ChartSliderButtonProps } from '@/domain/props/ChartContainerProps';
/**
* This component is tied to the `ChartContainer` and `ChartModal` component and should not be used independently.
*/
-export default function ChartSliderButton({ showSlider, setShowSlider, size = 4 }: LineChartSliderButtonProps) {
+export default function ChartSliderButton({ showSlider, setShowSlider, size = 4 }: ChartSliderButtonProps) {
return (
{
if (isLoading) return;
- const countryNamesInChart = isLineChartData(chartData)
+ const countryNamesInChart = isContinuousChartData(chartData)
? chartData.lines.map((line) => line.name)
: chartData.categories.map((category) => category.name);
const missingCountryNames = selectedCountryNames.filter(
diff --git a/src/components/Map/FcsAccordion.tsx b/src/components/Map/FcsAccordion.tsx
index b7a32e8a..f842ad7d 100644
--- a/src/components/Map/FcsAccordion.tsx
+++ b/src/components/Map/FcsAccordion.tsx
@@ -9,7 +9,7 @@ import { ReactComponent as Import } from '../../../public/Images/Import.svg';
import { ReactComponent as Population } from '../../../public/Images/Population.svg';
import AccordionContainer from '../Accordions/AccordionContainer';
import CustomCard from '../Cards/Card';
-import { LineChart } from '../Charts/LineChart';
+import { ContinuousChart } from '../Charts/ContinuousChart';
import CustomInfoCircle from '../CustomInfoCircle/CustomInfoCircle';
export default function FcsAccordion({ countryData, loading, countryIso3Data, countryName }: FcsAccordionProps) {
@@ -78,7 +78,7 @@ export default function FcsAccordion({ countryData, loading, countryIso3Data, co
content: (
{fcsChartData ? (
-
{rcsiChartData ? (
-
{currencyExchangeChartData ? (
-
+
) : (
No data about currency exchange
)}
@@ -145,7 +145,7 @@ export default function FcsAccordion({ countryData, loading, countryIso3Data, co
content: (
{balanceOfTradeChartData ? (
-
+
) : (
No data about balance of trade
)}
@@ -159,7 +159,7 @@ export default function FcsAccordion({ countryData, loading, countryIso3Data, co
content: (
{headlineAndFoodInflationChartData ? (
-
+
) : (
No data about headline and food inflation
)}
diff --git a/src/components/Map/FcsRegionTooltip.tsx b/src/components/Map/FcsRegionTooltip.tsx
index 3ad1ad99..d1702089 100644
--- a/src/components/Map/FcsRegionTooltip.tsx
+++ b/src/components/Map/FcsRegionTooltip.tsx
@@ -3,7 +3,7 @@ import { Feature, GeoJsonProperties, Geometry } from 'geojson';
import { FcsRegionTooltipOperations } from '@/operations/map/FcsRegionTooltipOperations';
import { formatToMillion } from '@/utils/formatting';
-import { LineChart } from '../Charts/LineChart';
+import { ContinuousChart } from '../Charts/ContinuousChart';
interface FcsRegionTooltipProps {
feature: Feature
;
@@ -49,7 +49,7 @@ export default function FcsRegionTooltip({ feature }: FcsRegionTooltipProps) {
{feature.properties?.fcsGraph && (
-
>;
size?: number;
@@ -34,7 +34,7 @@ export interface ChartSliderProps {
export interface ChartDownloadButtonProps {
chartRef: MutableRefObject;
- chartData: LineChartData | CategoricalChartData;
+ chartData: ContinuousChartData | CategoricalChartData;
size?: number;
}
@@ -43,14 +43,16 @@ export interface ChartDownloadButtonProps {
*/
export default interface ChartContainerProps {
+ chartData: ContinuousChartData | CategoricalChartData;
chartOptions?: Highcharts.Options;
- chartData: LineChartData | CategoricalChartData;
+ recalculateChartOptions: () => void;
title?: string;
description?: string;
small?: boolean;
noPadding?: boolean;
transparentBackground?: boolean;
+ chartHeight?: number;
disableExpandable?: boolean;
disableDownload?: boolean;
diff --git a/src/domain/props/ChartModalProps.tsx b/src/domain/props/ChartModalProps.tsx
index b3f6f338..aa3285c0 100644
--- a/src/domain/props/ChartModalProps.tsx
+++ b/src/domain/props/ChartModalProps.tsx
@@ -2,12 +2,12 @@ import Highcharts from 'highcharts';
import { Dispatch, SetStateAction } from 'react';
import { CategoricalChartData } from '@/domain/entities/charts/CategoricalChartData.ts';
-import { LineChartData } from '@/domain/entities/charts/LineChartData.ts';
+import { ContinuousChartData } from '@/domain/entities/charts/ContinuousChartData.ts';
import { ChartAlternativeSwitchButtonProps, ChartSliderProps } from '@/domain/props/ChartContainerProps';
export default interface ChartModalProps {
- chartOptions: Highcharts.Options;
- chartData: LineChartData | CategoricalChartData;
+ chartOptions?: Highcharts.Options;
+ chartData: ContinuousChartData | CategoricalChartData;
title?: string;
description?: string;
diff --git a/src/domain/props/LineChartProps.tsx b/src/domain/props/ContinuousChartProps.tsx
similarity index 66%
rename from src/domain/props/LineChartProps.tsx
rename to src/domain/props/ContinuousChartProps.tsx
index d9a50954..a554b98c 100644
--- a/src/domain/props/LineChartProps.tsx
+++ b/src/domain/props/ContinuousChartProps.tsx
@@ -1,10 +1,10 @@
import { BalanceOfTradeGraph } from '@/domain/entities/charts/BalanceOfTradeGraph.ts';
+import { ContinuousChartData } from '@/domain/entities/charts/ContinuousChartData.ts';
import { CurrencyExchangeGraph } from '@/domain/entities/charts/CurrencyExchangeGraph.ts';
import { InflationGraphs } from '@/domain/entities/charts/InflationGraphs.ts';
-import { LineChartData } from '@/domain/entities/charts/LineChartData.ts';
-export default interface LineChartProps {
- data: LineChartData | BalanceOfTradeGraph | CurrencyExchangeGraph | InflationGraphs;
+export default interface ContinuousChartProps {
+ data: ContinuousChartData | BalanceOfTradeGraph | CurrencyExchangeGraph | InflationGraphs;
title?: string;
description?: string;
@@ -12,6 +12,7 @@ export default interface LineChartProps {
small?: boolean;
noPadding?: boolean;
transparentBackground?: boolean;
+ chartHeight?: number;
disableExpandable?: boolean;
disableBarChartSwitch?: boolean;
diff --git a/src/domain/props/NoDataHintProps.ts b/src/domain/props/NoDataHintProps.ts
index 30b2965f..2cb139df 100644
--- a/src/domain/props/NoDataHintProps.ts
+++ b/src/domain/props/NoDataHintProps.ts
@@ -1,9 +1,9 @@
-import { LineChartData } from '@/domain/entities/charts/LineChartData.ts';
+import { ContinuousChartData } from '@/domain/entities/charts/ContinuousChartData.ts';
import { CategoricalChartData } from '../entities/charts/CategoricalChartData';
export interface NoDataHintProps {
- chartData: LineChartData | CategoricalChartData;
+ chartData: ContinuousChartData | CategoricalChartData;
selectedCountryNames: string[];
isLoading: boolean;
}
diff --git a/src/operations/charts/CategoricalChartOperations.ts b/src/operations/charts/CategoricalChartOperations.ts
index 07f8c6e3..e4836d4a 100644
--- a/src/operations/charts/CategoricalChartOperations.ts
+++ b/src/operations/charts/CategoricalChartOperations.ts
@@ -165,8 +165,8 @@ export default class CategoricalChartOperations {
},
pie: {
animation: true,
- allowPointSelect: true,
- cursor: 'pointer',
+ allowPointSelect: false,
+ cursor: 'default',
innerSize: '60%',
borderWidth: 0,
dataLabels: {
@@ -181,6 +181,13 @@ export default class CategoricalChartOperations {
fontWeight: 'light',
},
},
+ states: {
+ hover: {
+ halo: {
+ size: 4, // shrinking the halo/glow size on hover
+ },
+ },
+ },
},
},
};
diff --git a/src/operations/charts/ChartDownloadButtonOperations.ts b/src/operations/charts/ChartDownloadButtonOperations.ts
index 93eed440..44aec9c4 100644
--- a/src/operations/charts/ChartDownloadButtonOperations.ts
+++ b/src/operations/charts/ChartDownloadButtonOperations.ts
@@ -9,7 +9,7 @@ import patternFill from 'highcharts/modules/pattern-fill';
import HighchartsReact from 'highcharts-react-official';
import { CategoricalChartData } from '@/domain/entities/charts/CategoricalChartData.ts';
-import { LineChartData } from '@/domain/entities/charts/LineChartData.ts';
+import { ContinuousChartData } from '@/domain/entities/charts/ContinuousChartData.ts';
// initialize the exporting module
if (typeof Highcharts === 'object') {
@@ -59,7 +59,7 @@ export default class ChartDownloadButtonOperations {
/**
* Trigger download of the given line chart `data` as a json file.
*/
- public static downloadDataJSON(data: LineChartData | CategoricalChartData): void {
+ public static downloadDataJSON(data: ContinuousChartData | CategoricalChartData): void {
// convert data json object to string and encode as URI
const jsonString = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(data, null, 2))}`;
// create a temporary link element and trigger the download
diff --git a/src/operations/charts/LineChartOperations.ts b/src/operations/charts/ContinuousChartOperations.ts
similarity index 69%
rename from src/operations/charts/LineChartOperations.ts
rename to src/operations/charts/ContinuousChartOperations.ts
index 9bf2422e..35ddbaa8 100644
--- a/src/operations/charts/LineChartOperations.ts
+++ b/src/operations/charts/ContinuousChartOperations.ts
@@ -10,10 +10,10 @@ import highchartsMore from 'highcharts/highcharts-more';
import patternFill from 'highcharts/modules/pattern-fill';
import { BalanceOfTradeGraph } from '@/domain/entities/charts/BalanceOfTradeGraph.ts';
+import { ContinuousChartData } from '@/domain/entities/charts/ContinuousChartData.ts';
import { CurrencyExchangeGraph } from '@/domain/entities/charts/CurrencyExchangeGraph.ts';
import { InflationGraphs } from '@/domain/entities/charts/InflationGraphs.ts';
-import { LineChartData } from '@/domain/entities/charts/LineChartData.ts';
-import { LineChartDataType } from '@/domain/enums/LineChartDataType.ts';
+import { ContinuousChartDataType } from '@/domain/enums/ContinuousChartDataType.ts';
import { formatToMillion } from '@/utils/formatting.ts';
import { getTailwindColor } from '@/utils/tailwind-util.ts';
@@ -23,11 +23,11 @@ if (typeof Highcharts === 'object') {
patternFill(Highcharts);
}
/**
- * Using LineChartOperations, the LineChart component can convert its received data into LineChartData
+ * Using ContinuousChartOperations, the ContinuousChart component can convert its received data into ContinuousChartData
* and then generate the chart `Highcharts.Options` object required by the Highcharts component.
- * Two types of options can be created: rendering the LineChart data as a line chart or as a bar chart.
+ * Two types of options can be created: rendering the ContinuousChart data as a line chart or as a bar chart.
*/
-export default class LineChartOperations {
+export default class ContinuousChartOperations {
/**
* List of different dash styles for visualizing prediction data.
* All lines that are marked as `prediction` are colored with the same predictions color,
@@ -75,7 +75,7 @@ export default class LineChartOperations {
}
/**
- * Tooltip formatter function for `LineChart.Options.xAxis.labels.formatter` usage only.
+ * Tooltip formatter function for `ContinuousChart.Options.xAxis.labels.formatter` usage only.
*/
private static chartXAxisFormatter(xAxisType: AxisTypeValue, x: string | number): string {
if (xAxisType === 'datetime' && typeof x === 'number') {
@@ -85,21 +85,21 @@ export default class LineChartOperations {
}
/**
- * With this static function, the LineChart component can ensure that the received data for the chart
- * is converted to the `LineChartData` type.
- * To support another interface in `LineChartProps.data`, one has to add another switch case here
- * that converts the new interface into `LineChartData`.
+ * With this static function, the ContinuousChart component can ensure that the received data for the chart
+ * is converted to the `ContinuousChartData` type.
+ * To support another interface in `ContinuousChartProps.data`, one has to add another switch case here
+ * that converts the new interface into `ContinuousChartData`.
*/
- public static convertToLineChartData(
- data: LineChartData | BalanceOfTradeGraph | CurrencyExchangeGraph | InflationGraphs
- ): LineChartData {
+ public static convertToContinuousChartData(
+ data: ContinuousChartData | BalanceOfTradeGraph | CurrencyExchangeGraph | InflationGraphs
+ ): ContinuousChartData {
switch (data.type) {
- case LineChartDataType.LINE_CHART_DATA:
+ case ContinuousChartDataType.LINE_CHART_DATA:
return data;
- case LineChartDataType.BALANCE_OF_TRADE_CHART:
+ case ContinuousChartDataType.BALANCE_OF_TRADE_CHART:
return {
- type: LineChartDataType.LINE_CHART_DATA,
+ type: ContinuousChartDataType.LINE_CHART_DATA,
xAxisType: 'datetime',
yAxisLabel: 'Mill',
lines: [
@@ -112,9 +112,9 @@ export default class LineChartOperations {
],
};
- case LineChartDataType.CURRENCY_EXCHANGE_CHART:
+ case ContinuousChartDataType.CURRENCY_EXCHANGE_CHART:
return {
- type: LineChartDataType.LINE_CHART_DATA,
+ type: ContinuousChartDataType.LINE_CHART_DATA,
xAxisType: 'datetime',
yAxisLabel: 'Exchange rate',
lines: [
@@ -127,9 +127,9 @@ export default class LineChartOperations {
],
};
- case LineChartDataType.INFLATION_CHARTS:
+ case ContinuousChartDataType.INFLATION_CHARTS:
return {
- type: LineChartDataType.LINE_CHART_DATA,
+ type: ContinuousChartDataType.LINE_CHART_DATA,
xAxisType: 'datetime',
yAxisLabel: 'Rate in %',
lines: [
@@ -151,9 +151,9 @@ export default class LineChartOperations {
};
default:
- // if the given type is not supported yet, an empty `LineChartData` instance is returned
+ // if the given type is not supported yet, an empty `ContinuousChartData` instance is returned
return {
- type: LineChartDataType.LINE_CHART_DATA,
+ type: ContinuousChartDataType.LINE_CHART_DATA,
xAxisType: 'linear',
lines: [],
};
@@ -161,12 +161,12 @@ export default class LineChartOperations {
}
/**
- * Given `LineChartData` get list of all distinct x values of all lines' data points.
+ * Given `ContinuousChartData` get list of all distinct x values of all lines' data points.
*/
- public static getDistinctXAxisValues(data: LineChartData): number[] {
+ public static getDistinctXAxisValues(data: ContinuousChartData): number[] {
const uniqueXValues = new Set();
- data.lines.forEach((line) => {
- line.dataPoints?.forEach((point) => {
+ data.lines.forEach((l) => {
+ l.dataPoints?.forEach((point) => {
uniqueXValues.add(point.x); // Add x-value to the Set
});
});
@@ -174,13 +174,13 @@ export default class LineChartOperations {
}
/**
- * With this static function, the LineChart component can build the `HighCharts.Options` object
- * for a line chart or bar chart, out of a given `LineChartData` instance.
+ * With this static function, the ContinuousChar component can build the `HighCharts.Options` object
+ * for a line chart or bar chart, out of a given `ContinuousChartData` instance.
*
* Setting the 'xAxisSelectedMinIdx' and 'xAxisSelectedMinIdx' the rendered x-axis range
* can be manipulated; important: if a min is defined a max must be defined as well and vice versa.
*
- * @param data `LineChartData` object, containing all data to be plotted in the chart
+ * @param data `ContinuousChartData` object, containing all data to be plotted in the chart
* @param barChart if true, bars are plotted instead of lines
* @param xAxisSelectedMinIdx index of selected x-axis range min value
* @param xAxisSelectedMaxIdx index of selected x-axis range max value
@@ -188,20 +188,20 @@ export default class LineChartOperations {
* or 'undefined' if there is no data available to be plotted in the chart (to be interpreted as "no data available")
*/
public static getHighChartOptions(
- data: LineChartData,
+ data: ContinuousChartData,
xAxisSelectedMinIdx?: number,
xAxisSelectedMaxIdx?: number,
barChart?: boolean
): Highcharts.Options | undefined {
// get selected x-axis range min and max values
- const xAxisDistinctValues = LineChartOperations.getDistinctXAxisValues(data);
+ const xAxisDistinctValues = ContinuousChartOperations.getDistinctXAxisValues(data);
const xAxisSelectedMin = xAxisSelectedMinIdx !== undefined ? xAxisDistinctValues[xAxisSelectedMinIdx] : undefined;
const xAxisSelectedMax = xAxisSelectedMaxIdx !== undefined ? xAxisDistinctValues[xAxisSelectedMaxIdx] : undefined;
// parsing all given data series
const series: SeriesOptionsType[] = [];
- const defaultLineColors = LineChartOperations.getLineColorList();
- const defaultPredictionsDashStyles = LineChartOperations.getPredictionsDashStyles();
+ const defaultLineColors = ContinuousChartOperations.getLineColorList();
+ const defaultPredictionsDashStyles = ContinuousChartOperations.getPredictionsDashStyles();
let atLeastOneSeriesAvailable = false;
for (let i = 0; i < data.lines.length; i += 1) {
const lineData = data.lines[i];
@@ -227,54 +227,40 @@ export default class LineChartOperations {
}
// collect series data
- const seriesData: Highcharts.PointOptionsObject[] = [];
+ const categoryData: Highcharts.PointOptionsObject[] = [];
lineData.dataPoints?.forEach((p) => {
// check if datapoint x is in selected x-axis range
if (xAxisSelectedMin !== undefined && xAxisSelectedMax !== undefined) {
if (p.x < xAxisSelectedMin || xAxisSelectedMax < p.x) return;
}
- seriesData.push({
+ categoryData.push({
x: p.x,
y: p.y,
});
});
// make sure data is sorted (required by highchart)
- seriesData.sort((a, b) => a.x! - b.x!);
+ categoryData.sort((a, b) => a.x! - b.x!);
- if (seriesData.length > 0) atLeastOneSeriesAvailable = true;
+ if (categoryData.length > 0) atLeastOneSeriesAvailable = true;
// build series object for highchart
if (barChart) {
// plot series as bars
- series.push({
- name: lineData.name,
- type: 'column',
- data: seriesData,
- color:
- // if the dashStyle is not solid we fill the bars with diagonal lines
- categoryDashStyle !== 'Solid'
- ? {
- pattern: {
- path: {
- d: 'M 0 0 L 8 8 M -8 8 L 8 -8',
- stroke: categoryColor,
- strokeWidth: 1,
- },
- width: 8,
- height: 8,
- },
- }
- : categoryColor,
- opacity: lineData.showRange ? 0.75 : 1,
- borderColor: categoryColor,
- dashStyle: 'Solid',
- });
+ series.push(
+ ContinuousChartOperations.createBarSeries(
+ lineData.name,
+ categoryData,
+ categoryDashStyle,
+ categoryColor,
+ lineData.showRange
+ )
+ );
} else {
// plot series as line
series.push({
type: 'line',
name: lineData.name,
- data: seriesData,
+ data: categoryData,
color: categoryColor,
dashStyle: categoryDashStyle,
});
@@ -327,41 +313,8 @@ export default class LineChartOperations {
if (!atLeastOneSeriesAvailable) return undefined;
// build all vertical lines and plot bands
- const verticalBands = data.verticalBands ? [...data.verticalBands] : [];
- const verticalLines = data.verticalLines ? [...data.verticalLines] : [];
- if (data.predictionVerticalLineX) {
- // get max x value
- const xMax = Math.max(...data.lines.flatMap((l) => l.dataPoints.map((p) => p.x)));
- verticalBands.push({
- xStart: data.predictionVerticalLineX,
- xEnd: xMax,
- label: 'Future',
- });
- }
- if (data.predictionVerticalLineX) {
- verticalLines.push({
- x: data.predictionVerticalLineX,
- });
- }
- const plotBands = verticalBands.map((b) => ({
- from: b.xStart,
- to: b.xEnd,
- color: b.color || 'rgba(140,140,140,0.07)',
- zIndex: 1,
- label: {
- text: b.label || '',
- style: {
- color: getTailwindColor('--nextui-secondary'),
- fontSize: '0.7rem',
- },
- },
- }));
- const plotLines = verticalLines.map((l) => ({
- value: l.x,
- color: l.color || getTailwindColor('--nextui-chartsGridLine'),
- dashStyle: l.dashStyle,
- zIndex: 2,
- }));
+ const plotBands = ContinuousChartOperations.buildPlotBands(data);
+ const plotLines = ContinuousChartOperations.buildVerticalPlotLines(data);
// constructing the final HighCharts.Options
return {
@@ -388,7 +341,7 @@ export default class LineChartOperations {
fontSize: '0.7rem',
},
formatter() {
- return LineChartOperations.chartXAxisFormatter(data.xAxisType, this.value);
+ return ContinuousChartOperations.chartXAxisFormatter(data.xAxisType, this.value);
},
},
lineColor: getTailwindColor('--nextui-chartsXAxisLine'),
@@ -419,7 +372,7 @@ export default class LineChartOperations {
tooltip: {
shared: true,
formatter() {
- return LineChartOperations.chartTooltipFormatter(data.xAxisType, this.x, this.points);
+ return ContinuousChartOperations.chartTooltipFormatter(data.xAxisType, this.x, this.points);
},
backgroundColor: getTailwindColor('--nextui-chartsLegendBackground'),
style: {
@@ -476,4 +429,89 @@ export default class LineChartOperations {
},
};
}
+
+ /**
+ * Helper function exclusively for ContinuousChartOperations.getHighChartOptions.
+ */
+ private static createBarSeries(
+ name: string,
+ data: Highcharts.PointOptionsObject[],
+ categoryDashStyle: DashStyleValue,
+ categoryColor: string | undefined,
+ showRange: boolean | undefined
+ ): Highcharts.SeriesOptionsType {
+ return {
+ name,
+ type: 'column',
+ data,
+ color:
+ // if the dashStyle is not solid we fill the bars with diagonal lines
+ categoryDashStyle !== 'Solid'
+ ? {
+ pattern: {
+ path: {
+ d: 'M 0 0 L 8 8 M -8 8 L 8 -8',
+ stroke: categoryColor,
+ strokeWidth: 1,
+ },
+ width: 8,
+ height: 8,
+ },
+ }
+ : categoryColor,
+ opacity: showRange ? 0.75 : 1,
+ borderColor: categoryColor,
+ dashStyle: 'Solid',
+ };
+ }
+
+ /**
+ * Helper function exclusively for ContinuousChartOperations.getHighChartOptions.
+ * Building all chart 'bands'.
+ */
+ private static buildPlotBands(chartData: ContinuousChartData) {
+ const verticalBands = chartData.verticalBands ? [...chartData.verticalBands] : [];
+ if (chartData.predictionVerticalLineX) {
+ // if a predictionVerticalLineX is given we create a grey band from predictionVerticalLineX to the end of the chart
+ const xMax = Math.max(...chartData.lines.flatMap((l) => l.dataPoints.map((p) => p.x)));
+ verticalBands.push({
+ xStart: chartData.predictionVerticalLineX,
+ xEnd: xMax,
+ label: 'Future',
+ });
+ }
+ return verticalBands.map((b) => ({
+ from: b.xStart,
+ to: b.xEnd,
+ color: b.color || 'rgba(140,140,140,0.07)',
+ zIndex: 1,
+ label: {
+ text: b.label || '',
+ style: {
+ color: getTailwindColor('--nextui-secondary'),
+ fontSize: '0.7rem',
+ },
+ },
+ }));
+ }
+
+ /**
+ * Helper function exclusively for ContinuousChartOperations.getHighChartOptions.
+ * Building all chart 'vertical lines'.
+ */
+ private static buildVerticalPlotLines(chartData: ContinuousChartData) {
+ const verticalLines = chartData.verticalLines ? [...chartData.verticalLines] : [];
+ if (chartData.predictionVerticalLineX) {
+ // if a predictionVerticalLineX is given we create a grey vertical "dividing" line at predictionVerticalLineX
+ verticalLines.push({
+ x: chartData.predictionVerticalLineX,
+ });
+ }
+ return verticalLines.map((l) => ({
+ value: l.x,
+ color: l.color || getTailwindColor('--nextui-chartsGridLine'),
+ dashStyle: l.dashStyle,
+ zIndex: 2,
+ }));
+ }
}
diff --git a/src/operations/comparison-portal/CountryComparisonOperations.tsx b/src/operations/comparison-portal/CountryComparisonOperations.tsx
index 9b54633b..2c6e9585 100644
--- a/src/operations/comparison-portal/CountryComparisonOperations.tsx
+++ b/src/operations/comparison-portal/CountryComparisonOperations.tsx
@@ -2,12 +2,12 @@ import { Spacer } from '@nextui-org/react';
import { UseQueryResult } from '@tanstack/react-query';
import { CategoricalChart } from '@/components/Charts/CategoricalChart';
-import { LineChart } from '@/components/Charts/LineChart';
+import { ContinuousChart } from '@/components/Charts/ContinuousChart';
import NoDataHint from '@/components/ComparisonPortal/NoDataHint';
import CustomInfoCircle from '@/components/CustomInfoCircle/CustomInfoCircle';
import { AccordionItemProps } from '@/domain/entities/accordions/Accordions';
import { CategoricalChartData } from '@/domain/entities/charts/CategoricalChartData';
-import { LineChartData } from '@/domain/entities/charts/LineChartData';
+import { ContinuousChartData } from '@/domain/entities/charts/ContinuousChartData.ts';
import { ChartData } from '@/domain/entities/common/ChartData.ts';
import { CountryComparisonChartData } from '@/domain/entities/comparison/CountryComparisonChartdata';
import { CountryComparisonData } from '@/domain/entities/comparison/CountryComparisonData';
@@ -15,16 +15,16 @@ import { CountryDataRecord } from '@/domain/entities/country/CountryData';
import { CountryIso3DataRecord } from '@/domain/entities/country/CountryIso3Data.ts';
import { CountryMapData } from '@/domain/entities/country/CountryMapData';
import { SNACKBAR_SHORT_DURATION } from '@/domain/entities/snackbar/Snackbar';
-import { LineChartDataType } from '@/domain/enums/LineChartDataType';
+import { ContinuousChartDataType } from '@/domain/enums/ContinuousChartDataType.ts';
import { SnackbarPosition, SnackbarStatus } from '@/domain/enums/Snackbar';
import { SnackbarProps } from '@/domain/props/SnackbarProps';
import { FcsAccordionOperations } from '@/operations/map/FcsAccordionOperations';
import { formatToMillion } from '@/utils/formatting.ts';
export class CountryComparisonOperations {
- static getFcsChartData(countryDataList: CountryDataRecord[], countryMapData: CountryMapData[]): LineChartData {
+ static getFcsChartData(countryDataList: CountryDataRecord[], countryMapData: CountryMapData[]): ContinuousChartData {
return this.chartWithoutEmptyLines({
- type: LineChartDataType.LINE_CHART_DATA,
+ type: ContinuousChartDataType.LINE_CHART_DATA,
xAxisType: 'datetime',
yAxisLabel: 'Mill',
lines: countryDataList.map((countryData) => ({
@@ -40,9 +40,9 @@ export class CountryComparisonOperations {
});
}
- static getRcsiChartData(countryDataList: CountryDataRecord[], countryMapData: CountryMapData[]): LineChartData {
+ static getRcsiChartData(countryDataList: CountryDataRecord[], countryMapData: CountryMapData[]): ContinuousChartData {
return this.chartWithoutEmptyLines({
- type: LineChartDataType.LINE_CHART_DATA,
+ type: ContinuousChartDataType.LINE_CHART_DATA,
xAxisType: 'datetime',
yAxisLabel: 'Mill',
lines: countryDataList.map((countryData) => ({
@@ -110,9 +110,9 @@ export class CountryComparisonOperations {
static getBalanceOfTradeData(
countryIso3DataList: CountryIso3DataRecord[],
selectedCountries: CountryMapData[]
- ): LineChartData {
+ ): ContinuousChartData {
return this.chartWithoutEmptyLines({
- type: LineChartDataType.LINE_CHART_DATA,
+ type: ContinuousChartDataType.LINE_CHART_DATA,
xAxisType: 'datetime',
yAxisLabel: 'Mill',
lines: countryIso3DataList.map((countryIso3Data) => ({
@@ -128,9 +128,9 @@ export class CountryComparisonOperations {
countryIso3DataList: CountryIso3DataRecord[],
selectedCountries: CountryMapData[],
type: 'headline' | 'food'
- ): LineChartData {
+ ): ContinuousChartData {
return this.chartWithoutEmptyLines({
- type: LineChartDataType.LINE_CHART_DATA,
+ type: ContinuousChartDataType.LINE_CHART_DATA,
xAxisType: 'datetime',
yAxisLabel: 'Rate in %',
lines: countryIso3DataList
@@ -152,7 +152,7 @@ export class CountryComparisonOperations {
return countryMapData.find((country) => country.properties.iso3 === iso3)?.properties.adm0_name || '';
}
- static chartWithoutEmptyLines(chart: LineChartData): LineChartData {
+ static chartWithoutEmptyLines(chart: ContinuousChartData): ContinuousChartData {
return {
...chart,
lines: chart.lines.filter((line) => line.dataPoints.length > 0),
@@ -250,12 +250,13 @@ export class CountryComparisonOperations {
{fcsChartData && (
<>
-
{rcsiChartData && (
<>
-
{balanceOfTradeData && (
<>
-
+
{headlineInflationData && (
<>
-
-
+