Skip to content

Commit

Permalink
Merge pull request #112 from bento-platform/feat/maps
Browse files Browse the repository at this point in the history
  • Loading branch information
davidlougheed authored Sep 28, 2023
2 parents 14ef4ed + ffbb1d6 commit 709bf6e
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 84 deletions.
55 changes: 49 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bento_public",
"version": "0.14.1",
"version": "0.15.0",
"description": "A publicly accessible portal for clinical datasets, where users are able to see high-level statistics of the data available through predefined variables of interest and search the data using limited variables at a time. This portal allows users to gain a generic understanding of the data available (secure and firewalled) without the need to access it directly. Initially, this portal facilitates the search in English language only, but the French language will be added at a later time.",
"main": "index.js",
"scripts": {
Expand All @@ -19,17 +19,19 @@
"@reduxjs/toolkit": "^1.9.3",
"antd": "^5.6.2",
"axios": "^1.4.0",
"bento-charts": "^2.3.0",
"bento-charts": "^2.4.1",
"css-loader": "^6.8.1",
"dotenv": "^16.3.1",
"i18next": "^23.2.2",
"i18next-browser-languagedetector": "^7.0.2",
"i18next-http-backend": "^2.2.1",
"leaflet": "^1.9.4",
"less": "^4.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^13.0.0",
"react-icons": "^4.9.0",
"react-leaflet": "^4.2.1",
"react-redux": "^8.1.1",
"react-router-dom": "^6.13.0",
"recharts": "^2.7.1",
Expand Down
104 changes: 66 additions & 38 deletions src/js/components/Overview/Chart.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,84 @@
import React from 'react';
import React, { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { chartTypes } from '@/constants/overviewConstants';
import { useNavigate } from 'react-router-dom';
import { BarChart, PieChart } from 'bento-charts';
import { ChoroplethMap } from 'bento-charts/dist/maps';

import { CHART_HEIGHT } from '@/constants/overviewConstants';
import { ChartData } from '@/types/data';
import { useNavigate } from 'react-router-dom';
import { CHART_TYPE_BAR, CHART_TYPE_CHOROPLETH, CHART_TYPE_PIE, ChartConfig } from '@/types/chartConfig';

const Chart = ({ chartType, data, units, id }: ChartProps) => {
const Chart = memo(({ chartConfig, data, units, id }: ChartProps) => {
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const translateMap = ({ x, y }: { x: string; y: number }) => ({ x: t(x), y });
const removeMissing = ({ x }: { x: string }) => x !== 'missing';

const renderChartSwitch = () => {
switch (chartType) {
case chartTypes.BAR:
// bar charts can be rendered slightly larger as they do not clip
return (
<BarChart
data={data}
height={CHART_HEIGHT + 30}
units={units}
preFilter={removeMissing}
dataMap={translateMap}
onClick={(d) => {
navigate(`/${i18n.language}/search?${id}=${d.payload.x}`);
}}
/>
);
case chartTypes.PIE:
return (
<PieChart
data={data}
height={CHART_HEIGHT}
preFilter={removeMissing}
dataMap={translateMap}
onClick={(d) => {
navigate(`/${i18n.language}/search?${id}=${d.name}`);
}}
/>
);
default:
return <p>chart type doesnt exists</p>;
const { chart_type: type } = chartConfig;

switch (type) {
case CHART_TYPE_BAR:
// bar charts can be rendered slightly larger as they do not clip
return (
<BarChart
data={data}
height={CHART_HEIGHT + 50}
units={units}
preFilter={removeMissing}
dataMap={translateMap}
onClick={(d) => {
navigate(`/${i18n.language}/search?${id}=${d.payload.x}`);
}}
/>
);
case CHART_TYPE_PIE:
return (
<PieChart
data={data}
height={CHART_HEIGHT}
preFilter={removeMissing}
dataMap={translateMap}
onClick={(d) => {
navigate(`/${i18n.language}/search?${id}=${d.name}`);
}}
/>
);
case CHART_TYPE_CHOROPLETH: {
// map charts can be rendered at full height as they do not clip
const { category_prop: categoryProp, features, center, zoom, color_mode: colorMode } = chartConfig;
return (
<ChoroplethMap
data={data}
height={CHART_HEIGHT + 50}
preFilter={removeMissing}
dataMap={translateMap}
categoryProp={categoryProp}
features={features}
center={center}
zoom={zoom}
colorMode={colorMode}
onClick={(d) => {
const val = d.properties?.[categoryProp];
if (val === undefined) return;
navigate(`/${i18n.language}/search?${id}=${val}`);
}}
renderPopupBody={(_f, d) => (
<>
Count: {d} {units}
</>
)}
/>
);
}
};
default:
return <p>chart type does not exist</p>;
}
});

return <>{renderChartSwitch()}</>;
};
Chart.displayName = 'Chart';

export interface ChartProps {
chartType: string;
chartConfig: ChartConfig;
data: ChartData[];
units: string;
id: string;
Expand Down
42 changes: 28 additions & 14 deletions src/js/components/Overview/Drawer/ChartTree.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { Tree, TreeProps } from 'antd';
import { useTranslation } from 'react-i18next';
Expand All @@ -7,27 +7,41 @@ import { rearrange, setDisplayedCharts } from '@/features/data/data.store';
import { NON_DEFAULT_TRANSLATION } from '@/constants/configConstants';
import { ChartDataField } from '@/types/data';

interface MappedChartItem {
title: string;
key: string;
}

const ChartTree = ({ charts, section }: ChartTreeProps) => {
const dispatch = useDispatch();
const { t } = useTranslation(NON_DEFAULT_TRANSLATION);

const allCharts = charts.map(({ title, id }) => ({ title: t(title), key: id }));
const allCharts: MappedChartItem[] = useMemo(
() => charts.map(({ field: { title }, id }) => ({ title: t(title), key: id })),
[charts]
);

const onChartDrop: TreeProps['onDrop'] = (info) => {
const originalLocation = parseInt(info.dragNode.pos.substring(2));
const newLocation = info.dropPosition - 1;
const onChartDrop: TreeProps['onDrop'] = useMemo(() => {
const fn: TreeProps['onDrop'] = (event) => {
const originalLocation = parseInt(event.dragNode.pos.substring(2));
const newLocation = event.dropPosition - 1;

const data = [...allCharts];
const element = data.splice(originalLocation, 1)[0];
data.splice(newLocation, 0, element);
dispatch(rearrange({ section, arrangement: data.map((e) => e.key) }));
};
const data = [...(allCharts ?? [])];
const element = data.splice(originalLocation, 1)[0];
data.splice(newLocation, 0, element);
dispatch(rearrange({ section, arrangement: data.map((e) => e.key) }));
};
return fn;
}, [dispatch, allCharts, section]);

const checkedKeys = charts.filter((e) => e.isDisplayed).map((e) => e.id);
const checkedKeys = useMemo(() => charts.filter((e) => e.isDisplayed).map((e) => e.id), [charts]);

const onCheck: TreeProps['onCheck'] = (checkedKeysValue) => {
dispatch(setDisplayedCharts({ section, charts: checkedKeysValue }));
};
const onCheck = useMemo(() => {
const fn: TreeProps['onCheck'] = (checkedKeysValue) => {
dispatch(setDisplayedCharts({ section, charts: checkedKeysValue }));
};
return fn;
}, [dispatch, section]);

return (
<Tree
Expand Down
16 changes: 11 additions & 5 deletions src/js/components/Overview/MakeChartCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { memo } from 'react';
import Chart from './Chart';
import { Card, Button, Tooltip, Space, Typography, Row } from 'antd';
import { CloseOutlined, TeamOutlined, QuestionOutlined } from '@ant-design/icons';
Expand All @@ -9,11 +9,15 @@ import { ChartDataField } from '@/types/data';

const CARD_STYLE = { width: '430px', height: '415px', margin: '5px 0', borderRadius: '11px' };

const MakeChartCard = ({ section, chart, onRemoveChart }: MakeChartCardProps) => {
const MakeChartCard = memo(({ section, chart, onRemoveChart }: MakeChartCardProps) => {
const { t } = useTranslation(NON_DEFAULT_TRANSLATION);
const { t: td } = useTranslation(DEFAULT_TRANSLATION);

const { title, data, chartType, config, id, description } = chart;
const {
data,
field: { id, description, title, config },
chartConfig,
} = chart;

const extraOptionsData = [
{
Expand Down Expand Up @@ -54,7 +58,7 @@ const MakeChartCard = ({ section, chart, onRemoveChart }: MakeChartCardProps) =>
<div key={id} style={{ height: '100%', width: '430px' }}>
<Card title={t(title)} style={CARD_STYLE} size="small" extra={<Space size="small">{ed}</Space>}>
{data.filter((e) => !(e.x === 'missing')).length !== 0 ? (
<Chart chartType={chartType} data={data} units={config?.units || ''} id={id} />
<Chart chartConfig={chartConfig} data={data} units={config?.units || ''} id={id} />
) : (
<Row style={{ height: '350px ' }} justify="center" align="middle">
<CustomEmpty text="No Data" />
Expand All @@ -63,7 +67,9 @@ const MakeChartCard = ({ section, chart, onRemoveChart }: MakeChartCardProps) =>
</Card>
</div>
);
};
});

MakeChartCard.displayName = 'MakeChartCard';

export interface MakeChartCardProps {
section: string;
Expand Down
Loading

0 comments on commit 709bf6e

Please sign in to comment.