Skip to content

Commit

Permalink
feat: Set up ComboLine and ComboLineColumn charts
Browse files Browse the repository at this point in the history
  • Loading branch information
bprusinowski committed Sep 29, 2023
1 parent 934e9cc commit 0aac329
Show file tree
Hide file tree
Showing 10 changed files with 492 additions and 0 deletions.
81 changes: 81 additions & 0 deletions app/charts/combo/chart-combo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Box } from "@mui/material";
import { memo } from "react";

import { ChartLoadingWrapper } from "@/charts/chart-loading-wrapper";
import { ComboLineColumnChart } from "@/charts/combo/combo-line-column-state";
import { ComboLineChart } from "@/charts/combo/combo-line-state";
import { extractComponentIris } from "@/charts/shared/chart-helpers";
import { ComboConfig, DataSource, QueryFilters } from "@/config-types";
import {
useComponentsQuery,
useDataCubeMetadataQuery,
useDataCubeObservationsQuery,
} from "@/graphql/query-hooks";
import { useLocale } from "@/locales/use-locale";

import { ChartProps } from "../shared/ChartProps";

type ChartComboVisualizationProps = {
dataSetIri: string;
dataSource: DataSource;
chartConfig: ComboConfig;
queryFilters: QueryFilters;
published: boolean;
};

export const ChartComboVisualization = (
props: ChartComboVisualizationProps
) => {
const { dataSetIri, dataSource, chartConfig, queryFilters, published } =
props;
const locale = useLocale();
const componentIris = published
? extractComponentIris(chartConfig)
: undefined;
const commonQueryVariables = {
iri: dataSetIri,
sourceType: dataSource.type,
sourceUrl: dataSource.url,
locale,
};
const [metadataQuery] = useDataCubeMetadataQuery({
variables: commonQueryVariables,
});
const [componentsQuery] = useComponentsQuery({
variables: {
...commonQueryVariables,
componentIris,
},
});
const [observationsQuery] = useDataCubeObservationsQuery({
variables: {
...commonQueryVariables,
componentIris,
filters: queryFilters,
},
});

return (
<ChartLoadingWrapper
metadataQuery={metadataQuery}
componentsQuery={componentsQuery}
observationsQuery={observationsQuery}
chartConfig={chartConfig}
Component={ChartCombo}
/>
);
};

export const ChartCombo = memo((props: ChartProps<ComboConfig>) => {
const { chartConfig } = props;

return chartConfig.chartSubtype === "line" ? (
<ComboLineChart aspectRatio={0.4} {...props}>
<Box>ComboLineChart</Box>
</ComboLineChart>
) : (
<ComboLineColumnChart aspectRatio={0.4} {...props}>
<Box>ComboLineColumnChart</Box>
</ComboLineColumnChart>
);
});
102 changes: 102 additions & 0 deletions app/charts/combo/combo-line-column-state-props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React from "react";

import { BaseYGetter, sortData } from "@/charts/combo/combo-state-props";
import { getLabelWithUnit } from "@/charts/shared/chart-helpers";
import {
BaseVariables,
ChartStateData,
TemporalXVariables,
useBaseVariables,
useChartData,
useTemporalXVariables,
} from "@/charts/shared/chart-state";
import { ComboConfig } from "@/configurator";

import { ChartProps } from "../shared/ChartProps";

type NumericalYComboLineColumnVariables = {
y: {
axisMode: "dual";
line: BaseYGetter & {
orientation: "left" | "right";
};
column: BaseYGetter & {
orientation: "left" | "right";
};
};
};

export type ComboLineColumnStateVariables = BaseVariables &
TemporalXVariables &
NumericalYComboLineColumnVariables;

export const useComboLineColumnStateVariables = (
props: ChartProps<ComboConfig> & { aspectRatio: number }
): ComboLineColumnStateVariables => {
const { chartConfig, dimensionsByIri, measuresByIri } = props;
const { fields } = chartConfig;
const { x } = fields;

const baseVariables = useBaseVariables(chartConfig);
const temporalXVariables = useTemporalXVariables(x, {
dimensionsByIri,
});

if (chartConfig.chartSubtype !== "line-column") {
throw new Error("This hook is only for line-column charts!");
}

const lineIri = chartConfig.fields.y.lineComponentIri;
const lineAxisOrientation = chartConfig.fields.y.lineAxisOrientation;
const columnIri = chartConfig.fields.y.columnComponentIri;
const numericalYVariables: NumericalYComboLineColumnVariables = {
y: {
axisMode: "dual",
line: {
iri: lineIri,
label: getLabelWithUnit(measuresByIri[lineIri]),
orientation: lineAxisOrientation,
getY: (d) => {
return d[lineIri] !== null ? Number(d[lineIri]) : null;
},
},
column: {
iri: columnIri,
label: getLabelWithUnit(measuresByIri[columnIri]),
orientation: lineAxisOrientation === "left" ? "right" : "left",
getY: (d) => {
return d[columnIri] !== null ? Number(d[columnIri]) : null;
},
},
},
};

return {
...baseVariables,
...temporalXVariables,
...numericalYVariables,
};
};

export const useComboLineColumnStateData = (
chartProps: ChartProps<ComboConfig> & { aspectRatio: number },
variables: ComboLineColumnStateVariables
): ChartStateData => {
const { chartConfig, observations } = chartProps;
// FIXME: handle properly.
const plottableData = observations;
const sortedPlottableData = React.useMemo(() => {
return sortData(plottableData, {
getX: variables.getX,
});
}, [plottableData, variables.getX]);
const data = useChartData(sortedPlottableData, {
chartConfig,
getXAsDate: variables.getX,
});

return {
...data,
allData: sortedPlottableData,
};
};
69 changes: 69 additions & 0 deletions app/charts/combo/combo-line-column-state.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { ScaleLinear, ScaleOrdinal, ScaleTime } from "d3";

import {
ComboLineColumnStateVariables,
useComboLineColumnStateData,
useComboLineColumnStateVariables,
} from "@/charts/combo/combo-line-column-state-props";
import {
ChartContext,
ChartStateData,
CommonChartState,
InteractiveXTimeRangeState,
} from "@/charts/shared/chart-state";
import { TooltipInfo } from "@/charts/shared/interaction/tooltip";
import { InteractionProvider } from "@/charts/shared/use-interaction";
import { Observer } from "@/charts/shared/use-width";
import { ComboConfig } from "@/configurator";
import { Observation } from "@/domain/data";

import { ChartProps } from "../shared/ChartProps";

export type ComboLineColumnState = CommonChartState &
ComboLineColumnStateVariables &
InteractiveXTimeRangeState & {
xKey: string;
chartType: "combo";
xScale: ScaleTime<number, number>;
yScale: ScaleLinear<number, number>;
colors: ScaleOrdinal<string, string>;
chartWideData: ArrayLike<Observation>;
getAnnotationInfo: (d: Observation) => TooltipInfo;
};

const useComboLineColumnState = (
chartProps: ChartProps<ComboConfig> & { aspectRatio: number },
variables: ComboLineColumnStateVariables,
data: ChartStateData
): ComboLineColumnState => {
return {} as unknown as ComboLineColumnState;
};

const ComboLineColumnChartProvider = (
props: React.PropsWithChildren<
ChartProps<ComboConfig> & { aspectRatio: number }
>
) => {
const { children, ...chartProps } = props;
const variables = useComboLineColumnStateVariables(chartProps);
const data = useComboLineColumnStateData(chartProps, variables);
const state = useComboLineColumnState(chartProps, variables, data);

return (
<ChartContext.Provider value={state}>{children}</ChartContext.Provider>
);
};

export const ComboLineColumnChart = (
props: React.PropsWithChildren<
ChartProps<ComboConfig> & { aspectRatio: number }
>
) => {
return (
<Observer>
<InteractionProvider>
<ComboLineColumnChartProvider {...props} />
</InteractionProvider>
</Observer>
);
};
9 changes: 9 additions & 0 deletions app/charts/combo/combo-line-column.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ComboLineColumnState } from "@/charts/combo/combo-line-column-state";
import { useChartState } from "@/charts/shared/chart-state";

export const ComboLineColumn = () => {
const state = useChartState() as ComboLineColumnState;
console.log(state);

return null;
};
123 changes: 123 additions & 0 deletions app/charts/combo/combo-line-state-props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import React from "react";

import { BaseYGetter, sortData } from "@/charts/combo/combo-state-props";
import { getLabelWithUnit } from "@/charts/shared/chart-helpers";
import {
BaseVariables,
ChartStateData,
TemporalXVariables,
useBaseVariables,
useChartData,
useTemporalXVariables,
} from "@/charts/shared/chart-state";
import { ComboConfig } from "@/configurator";

import { ChartProps } from "../shared/ChartProps";

type NumericalYComboLineVariables = {
y:
| {
axisMode: "single";
lines: BaseYGetter[];
}
| {
axisMode: "dual";
lineLeft: BaseYGetter;
lineRight: BaseYGetter;
};
};

export type ComboLineStateVariables = BaseVariables &
TemporalXVariables &
NumericalYComboLineVariables;

export const useComboLineStateVariables = (
props: ChartProps<ComboConfig> & { aspectRatio: number }
): ComboLineStateVariables => {
const { chartConfig, dimensionsByIri, measuresByIri } = props;
const { fields } = chartConfig;
const { x } = fields;

const baseVariables = useBaseVariables(chartConfig);
const temporalXVariables = useTemporalXVariables(x, {
dimensionsByIri,
});

if (chartConfig.chartSubtype !== "line") {
throw new Error("This hook is only for line charts!");
}

let numericalYVariables: NumericalYComboLineVariables;
switch (chartConfig.fields.y.axisMode) {
case "single": {
numericalYVariables = {
y: {
axisMode: "single",
lines: chartConfig.fields.y.componentIris.map((iri) => ({
iri,
label: getLabelWithUnit(measuresByIri[iri]),
getY: (d) => {
return d[iri] !== null ? Number(d[iri]) : null;
},
})),
},
};
break;
}
case "dual":
const leftIri = chartConfig.fields.y.leftAxisComponentIri;
const rightIri = chartConfig.fields.y.rightAxisComponentIri;
numericalYVariables = {
y: {
axisMode: "dual",
lineLeft: {
iri: leftIri,
label: getLabelWithUnit(measuresByIri[leftIri]),
getY: (d) => {
return d[leftIri] !== null ? Number(d[leftIri]) : null;
},
},
lineRight: {
iri: rightIri,
label: getLabelWithUnit(measuresByIri[rightIri]),
getY: (d) => {
return d[rightIri] !== null ? Number(d[rightIri]) : null;
},
},
},
};
break;
default:
const _exhaustiveCheck: never = chartConfig.fields.y;
return _exhaustiveCheck;
}

return {
...baseVariables,
...temporalXVariables,
...numericalYVariables,
};
};

export const useComboLineStateData = (
chartProps: ChartProps<ComboConfig> & { aspectRatio: number },
variables: ComboLineStateVariables
): ChartStateData => {
const { chartConfig, observations } = chartProps;
// FIXME: handle properly.
const plottableData = observations;
const sortedPlottableData = React.useMemo(() => {
return sortData(plottableData, {
getX: variables.getX,
});
}, [plottableData, variables.getX]);
const data = useChartData(sortedPlottableData, {
chartConfig,
getXAsDate: variables.getX,
});

return {
...data,
allData: sortedPlottableData,
};
};
Loading

0 comments on commit 0aac329

Please sign in to comment.