Skip to content

Commit

Permalink
Split DSA and Credit Rating charts
Browse files Browse the repository at this point in the history
  • Loading branch information
mustafasaifee42 committed Jun 11, 2024
1 parent c1e7c96 commit 86b261f
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 0 deletions.
2 changes: 2 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<link rel="stylesheet" href="https://undp-data.github.io/stylesheets-for-viz/style/StyleForGraph.css" crossorigin="anonymous" />
</head>
<body>
<div id="debtCreditRating" style="margin-bottom: 1rem;"></div>
<div id="debtDsaRating" style="margin-bottom: 1rem;"></div>
<div id="debtCreditDsaRating" style="margin-bottom: 1rem;"></div>
<div id="debtToGdp" style="margin-bottom: 1rem;"></div>
<div id="debtNetInterest" style="margin-bottom: 1rem;"></div>
Expand Down
57 changes: 57 additions & 0 deletions src/AppCreditRating.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { csv } from 'd3-fetch';
import { useEffect, useState } from 'react';
import {
CategoryData,
ChartSourceType,
CreditRatingType,
DsaRatingType,
} from './Types';
import './style.css';
import { DSACreditStackedBarChart } from './DSACreditStackedBarChart';

function App() {
const [creditRatingData, setCreditRatingData] = useState<
CreditRatingType[] | undefined
>();
const [dsaRatingData, setDsaRatingData] = useState<
DsaRatingType[] | undefined
>();
const [categoriesData, setCategoriesData] = useState<
CategoryData[] | undefined
>(undefined);
const [sourcesData, setSourcesData] = useState<ChartSourceType[]>([]);
const dataurl =
'https://raw.githubusercontent.com/UNDP-Data/dv-debt-all-data-repo/main/';
useEffect(() => {
Promise.all([
csv(`${dataurl}creditRating1.csv`),
csv(`${dataurl}dsaRating1.csv`),
csv(`${dataurl}categories1.csv`),
csv(`${dataurl}groups-sources1.csv`),
]).then(([creditData, dsaData, regions, sources]) => {
setCreditRatingData(creditData as any);
setDsaRatingData(dsaData as any);
setCategoriesData(regions as any);
setSourcesData(sources as any);
});
}, []);
return (
<div className='undp-container'>
{creditRatingData && dsaRatingData && categoriesData ? (
<DSACreditStackedBarChart
creditData={creditRatingData}
dsaData={dsaRatingData}
categories={categoriesData}
chartSource={
sourcesData.filter(d => d.graph === 'Credit and DSA Ratings')[0]
}
creditDsaSelection='Credit'
/>
) : null}
</div>
);
}

export default App;
57 changes: 57 additions & 0 deletions src/AppDSARating.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { csv } from 'd3-fetch';
import { useEffect, useState } from 'react';
import {
CategoryData,
ChartSourceType,
CreditRatingType,
DsaRatingType,
} from './Types';
import './style.css';
import { DSACreditStackedBarChart } from './DSACreditStackedBarChart';

function AppDsaRating() {
const [creditRatingData, setCreditRatingData] = useState<
CreditRatingType[] | undefined
>();
const [dsaRatingData, setDsaRatingData] = useState<
DsaRatingType[] | undefined
>();
const [categoriesData, setCategoriesData] = useState<
CategoryData[] | undefined
>(undefined);
const [sourcesData, setSourcesData] = useState<ChartSourceType[]>([]);
const dataurl =
'https://raw.githubusercontent.com/UNDP-Data/dv-debt-all-data-repo/main/';
useEffect(() => {
Promise.all([
csv(`${dataurl}creditRating1.csv`),
csv(`${dataurl}dsaRating1.csv`),
csv(`${dataurl}categories1.csv`),
csv(`${dataurl}groups-sources1.csv`),
]).then(([creditData, dsaData, regions, sources]) => {
setCreditRatingData(creditData as any);
setDsaRatingData(dsaData as any);
setCategoriesData(regions as any);
setSourcesData(sources as any);
});
}, []);
return (
<div className='undp-container'>
{creditRatingData && dsaRatingData && categoriesData ? (
<DSACreditStackedBarChart
creditData={creditRatingData}
dsaData={dsaRatingData}
categories={categoriesData}
chartSource={
sourcesData.filter(d => d.graph === 'Credit and DSA Ratings')[0]
}
creditDsaSelection='DSA'
/>
) : null}
</div>
);
}

export default AppDsaRating;
79 changes: 79 additions & 0 deletions src/DSACreditStackedBarChart/Graph.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { scaleLinear } from 'd3-scale';
import { stack } from 'd3-shape';
import UNDPColorModule from 'undp-viz-colors';

interface Props {
data: object[];
svgWidth: number;
}

export function Graph(props: Props) {
const { data, svgWidth } = props;
const margin = { top: 20, right: 30, bottom: 50, left: 60 };
const graphHeight = 500 - margin.top - margin.bottom;
const valueArray: number[] = data.map((d: any) => Number(d.number));
const percentageData = data.filter(d => (d as any).Value === 'Percentage');
const numberData = data.filter(d => (d as any).Value === 'Number')[0];
const subgroups = Object.keys(data[0]).slice(2);
const stackedData = stack().keys(subgroups)(percentageData as any);
const y = scaleLinear().domain([0, 100]).range([0, graphHeight]);
return (
<div>
{valueArray.length > 0 ? (
<svg width={`${svgWidth}px`} height='470px' id='creditRatingSvg'>
<g transform={`translate(${svgWidth / 2 - 250},${margin.top})`}>
<g>
{stackedData.map((d, i) => (
<g key={i} className='stackedRect'>
<rect
y={y(d[0][0])}
x={120}
height={y(d[0][1] - d[0][0])}
width={100}
fill={UNDPColorModule.sequentialColors.negativeColorsx05[i]}
opacity={0.8}
/>
{(numberData as any)[d.key] > 0 ? (
<>
<text
textAnchor='end'
className='chartLabelLarge'
y={y(d[0][0]) + y(d[0][1] - d[0][0]) / 2}
x={110}
>
{`${(d[0][1] - d[0][0]).toFixed(1)}% `}
</text>
<text
textAnchor='end'
className='chartLabel'
y={y(d[0][0]) + y(d[0][1] - d[0][0]) / 2 + 15}
x={110}
>
{`${Math.round((numberData as any)[d.key])} ${
(numberData as any)[d.key] > 1
? 'countries'
: 'country'
}`}
</text>
<text
className='chartLabel'
y={y(d[0][0]) + y(d[0][1] - d[0][0]) / 2 + 5}
x={230}
>
{d.key}
</text>
</>
) : null}
</g>
))}
</g>
</g>
</svg>
) : (
<div className='center-area-error-el'>No data available</div>
)}
</div>
);
}
96 changes: 96 additions & 0 deletions src/DSACreditStackedBarChart/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/* eslint-disable no-console */
import { useEffect, useRef, useState } from 'react';
import parse from 'html-react-parser';
import { Select } from 'antd';
import {
CategoryData,
ChartSourceType,
CreditRatingType,
DsaRatingType,
} from '../Types';
import { Graph } from './Graph';
import { DownloadImageButton } from '../Components/DownloadImageButton';
import { DownloadDataButton } from '../Components/DownloadDataButton';

interface Props {
creditData: CreditRatingType[];
dsaData: DsaRatingType[];
categories: CategoryData[];
chartSource: ChartSourceType;
creditDsaSelection: 'Credit' | 'DSA';
}

export function DSACreditStackedBarChart(props: Props) {
const { dsaData, creditData, categories, chartSource, creditDsaSelection } =
props;
const graphDiv = useRef<HTMLDivElement>(null);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [divToBeDownloaded, setDivToBeDownloaded] = useState<any>(null);
const [categorySelection, setCategorySelection] = useState('All developing');
const [selectedData, setSelectedData] = useState<object[]>(
creditData.filter(d => d.Group === categorySelection),
);
const containerRef = useRef<HTMLDivElement>(null);
const [svgWidth, setSvgWidth] = useState<number | 400>(400);
useEffect(() => {
const resizeObserver = new ResizeObserver(entries => {
setSvgWidth(entries[0].target.clientWidth);
});
if (containerRef.current) resizeObserver.observe(containerRef.current);
return () => resizeObserver.disconnect();
}, []);
useEffect(() => {
const data =
creditDsaSelection === 'Credit'
? creditData.filter(d => d.Group === categorySelection)
: dsaData.filter(d => d.Group === categorySelection);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
setSelectedData(data as any);
}, [categorySelection, creditDsaSelection]);
useEffect(() => {
setDivToBeDownloaded(graphDiv.current);
}, [graphDiv.current]);
return (
<div className='chart-container'>
<div ref={graphDiv}>
<div className='margin-bottom-07 flex-div flex-space-between flex-vert-align-center'>
<h6 className='undp-typography margin-bottom-00'>
{creditDsaSelection} rating: distribution of countries on rating
classes
</h6>
<div className='flex-div flex-vert-align-center no-shrink'>
<DownloadImageButton element={divToBeDownloaded} />
<DownloadDataButton link='https://github.com/UNDP-Data/dv-debt-all-data-repo/raw/main/ExcelData/CreditAndDSARating.xlsx' />
</div>
</div>
<div className='margin-bottom-07 flex-div flex-vert-align-center'>
<Select
options={categories.map(d => ({
label: d.description,
value: d.description,
}))}
className='undp-select'
style={{ width: '100%' }}
onChange={el => {
setCategorySelection(el);
}}
value={categorySelection}
/>
</div>
<div ref={containerRef}>
<Graph data={selectedData} svgWidth={svgWidth} />
</div>
{chartSource?.source ? (
<p className='source'>{`Source: ${
chartSource.source.split('.')[
creditDsaSelection === 'Credit' ? 0 : 1
]
}`}</p>
) : null}
{chartSource?.note ? (
<p className='source'>Note: {parse(chartSource.note)}</p>
) : null}
</div>
</div>
);
}
16 changes: 16 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import AppCompositionGroups from './AppCompositionGroups';
import AppCreditDsaRating from './AppCreditDsaRating';
import AppExternalDebtQuantiles from './AppExternalDebtQuantiles';
import AppAllCountries from './AppAllCountries';
import AppCreditRating from './AppCreditRating';
import AppDsaRating from './AppDSARating';

ReactDOM.createRoot(
document.getElementById('debtCreditDsaRating') as HTMLElement,
Expand All @@ -16,6 +18,20 @@ ReactDOM.createRoot(
<AppCreditDsaRating />
</React.StrictMode>,
);
ReactDOM.createRoot(
document.getElementById('debtCreditRating') as HTMLElement,
).render(
<React.StrictMode>
<AppCreditRating />
</React.StrictMode>,
);
ReactDOM.createRoot(
document.getElementById('debtDsaRating') as HTMLElement,
).render(
<React.StrictMode>
<AppDsaRating />
</React.StrictMode>,
);
ReactDOM.createRoot(document.getElementById('debtToGdp') as HTMLElement).render(
<React.StrictMode>
<App />
Expand Down

0 comments on commit 86b261f

Please sign in to comment.