Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/f 37 document every chart we have implement generic line chart #21

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@nextui-org/kbd": "2.0.34",
"@nextui-org/link": "2.0.35",
"@nextui-org/listbox": "^2.1.27",
"@nextui-org/modal": "^2.0.41",
"@nextui-org/navbar": "2.0.37",
"@nextui-org/popover": "^2.1.29",
"@nextui-org/react": "^2.4.8",
Expand Down
205 changes: 199 additions & 6 deletions src/app/elements/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,215 @@
import CustomAccordion from '@/components/Accordions/Accordion';
import { CustomButton } from '@/components/Buttons/CustomButton';
import { Chart } from '@/components/Charts/Chart';
import container from '@/container';
import CountryRepository from '@/domain/repositories/CountryRepository';
import { LineChart } from '@/components/Charts/LineChart';
import { BalanceOfTradeGraph } from '@/domain/entities/charts/BalanceOfTradeGraph.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 AccordionsOperations from '@/operations/accordions/AccordionOperations';

/**
* You can use this page to try and show off your components.
* It's not accessible from the UI, but you can reach it by manually navigating to /elements
*/
export default async function Elements() {
const countryData = await container.resolve<CountryRepository>('CountryRepository').getCountryData(50);
const simpleAndSmallLineChartData: LineChartData = {
type: 'LineChartData',
xAxisType: 'linear',
lines: [
{
name: 'Category A',
dataPoints: [
{ x: '0', y: 1 },
{ x: '1', y: 2 },
{ x: '2', y: 4 },
{ x: '3', y: 4 },
],
},
],
};

const simpleLineChartData: LineChartData = {
type: 'LineChartData',
xAxisType: 'linear',
yAxisLabel: 'yield',
roundLines: true,
lines: [
{
name: 'Category A',
dataPoints: [
{ x: '0', y: 1 },
{ x: '1', y: 2 },
{ x: '2', y: 4 },
{ x: '3', y: 8 },
],
},
],
};

const maxedOutLineChartData: LineChartData = {
type: 'LineChartData',
xAxisType: 'linear',
yAxisLabel: 'yield',
lines: [
{
name: 'Category A',
showRange: true,
dataPoints: [
{ x: '0', y: 1, yRangeMin: 0, yRangeMax: 2 },
{ x: '1', y: 3, yRangeMin: 2, yRangeMax: 4 },
{ x: '2', y: 4, yRangeMin: 3.5, yRangeMax: 4.5 },
{ x: '3', y: 8, yRangeMin: 7.5, yRangeMax: 8.5 },
],
},
{
name: 'Category B',
dataPoints: [
{ x: '0', y: 4 },
{ x: '1', y: 7 },
{ x: '2', y: 5 },
{ x: '3', y: 2 },
],
},
{
name: 'Category C',
dataPoints: [
{ x: '0', y: 7 },
{ x: '1', y: 2 },
{ x: '2', y: 3 },
{ x: '3', y: 3 },
],
},
],
};

const balanceOfTradeGraphData: BalanceOfTradeGraph = {
type: 'BalanceOfTradeGraph',
data: [
{
x: '2023-10-01',
y: -5345789,
},
{
x: '2023-12-01',
y: -1235478,
},
{
x: '2024-02-01',
y: 690234,
},
{
x: '2024-04-01',
y: 3945574,
},
],
};

const currencyExchangeGraphData: CurrencyExchangeGraph = {
type: 'CurrencyExchangeGraph',
name: 'Exchange Rate (USD/NGN)',
source: '',
updated: '',
data: [
{
x: '2023-10-01',
y: 1.421234,
},
{
x: '2023-12-01',
y: 1.597552,
},
{
x: '2024-02-01',
y: 1.687564,
},
{
x: '2024-04-01',
y: 1.665345,
},
],
};

const inflationGraphsData: InflationGraphs = {
type: 'InflationGraphs',
headline: {
data: [
{
x: '2023-10-01',
y: 26.2,
},
{
x: '2023-12-01',
y: 30.2,
},
{
x: '2024-02-01',
y: 37.1,
},
{
x: '2024-04-01',
y: 37.9,
},
],
},
food: {
data: [
{
x: '2023-10-01',
y: 25.2,
},
{
x: '2023-12-01',
y: 29.2,
},
{
x: '2024-02-01',
y: 36.1,
},
{
x: '2024-04-01',
y: 36.9,
},
],
},
};

return (
<div>
<Chart chartData={countryData.fcsGraph} />;<CustomButton variant="solid">Test</CustomButton>
<div className="w-full flex flex-col items-center justify-center">
<CustomButton variant="solid">Test</CustomButton>
<CustomButton variant="bordered">Test</CustomButton>
<CustomButton variant="flat">Test</CustomButton>
<CustomAccordion items={AccordionsOperations.getAccordionData()} />;
<div className="w-full h-fit flex flex-row flex-wrap gap-10 justify-around px-8 pt-40 pb-16 border-b border-gray-800">
<div className="w-250px h-fit">
<LineChart data={simpleAndSmallLineChartData} small />
</div>
<div className="w-250px h-fit">
<LineChart
title="Small Rounded Line Chart"
description="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy."
data={simpleLineChartData}
expandable
small
/>
</div>
<div className="w-400px h-fit">
<LineChart
title="Maxed Out Line Chart"
description="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."
data={maxedOutLineChartData}
expandable
/>
</div>
<div className="w-400px h-fit">
<LineChart title="Balance of trade" data={balanceOfTradeGraphData} expandable />
</div>
<div className="w-400px h-fit">
<LineChart title="Currency exchange" data={currencyExchangeGraphData} expandable />
</div>
<div className="w-400px h-fit">
<LineChart title="Headline and food inflation" data={inflationGraphsData} expandable />
</div>
</div>
</div>
);
}
2 changes: 0 additions & 2 deletions src/components/Charts/Chart.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/* eslint-disable react/destructuring-assignment */

'use client';

import * as Highcharts from 'highcharts';
Expand Down
120 changes: 120 additions & 0 deletions src/components/Charts/LineChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
'use client';

import { Button } from '@nextui-org/button';
import { Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, useDisclosure } from '@nextui-org/modal';
import * as Highcharts from 'highcharts';
import { HighchartsReact } from 'highcharts-react-official';
// eslint-disable-next-line import/no-extraneous-dependencies
import { Maximize4 } from 'iconsax-react';

import { LineChartData } from '@/domain/entities/charts/LineChartData';
import LineChartProps from '@/domain/props/LineChartProps';
import LineChartOperations from '@/operations/charts/LineChartOperations';

/**
* The LineChart component is a box that primarily renders a title, description text, and a line chart.
* 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 line chart 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.
*
* The data to be displayed in the chart can be provided in different types (see `LineChartProps.data`).
* However, the preferred type is `LineChartData`.
*
* To enable the LineChart component to support an additional `data` type, the following steps are required:
* 1. Define an interface and add it to `LineChartProps.data`.
* 2. Add another switch case in `LineChartOperations.convertToLineChartData` to convert the new interface to `LineChartData`.
*
* @param title chart title (optional)
* @param description chart description text (optional)
* @param expandable when selected, the user is given the option to open the chart in a larger modal (optional)
* @param small when selected, all components in the line chart box become slightly smaller (optional)
* @param data the actual data to be used in the chart
*/
export function LineChart({ title, description, expandable, small, data }: LineChartProps) {
const TITLE_TEXT_SIZE = small ? 'text-sm' : 'text-md';
const DESCRIPTION_TEXT_SIZE = small ? 'text-xs' : 'text-sm';
const CHART_HEIGHT = small ? 12 : 16;
const ICON_BUTTON_SIZE = small ? 3 : 4;
const HEADER_BOTTOM_PADDING = title ? 3 : 0;
const JSON_DOWNLOAD_FILE_NAME = `hunger_map_line_chart_json-${title}.json`;

// full screen modal state handling
const { isOpen, onOpen, onOpenChange } = useDisclosure();

// convert data to `LineChartData` and build chart options for 'Highcharts'
const lineChartData: LineChartData = LineChartOperations.convertToLineChartData(data);
const chartOptions: Highcharts.Options = LineChartOperations.getHighChartData(lineChartData);

// trigger download of the given line chart `data` as a json file
const downloadDataJson = () => {
// 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
const link = document.createElement('a');
link.href = jsonString;
link.download = JSON_DOWNLOAD_FILE_NAME;
link.click();
};
plaume8 marked this conversation as resolved.
Show resolved Hide resolved

// Full screen modal that can be opened if `expandable==true`. Offers a larger chart and a download button.
const fullScreenModal = (
<Modal size="5xl" isOpen={isOpen} backdrop="blur" scrollBehavior="inside" onOpenChange={onOpenChange}>
<ModalContent>
<ModalHeader className="flex flex-col gap-1">{title}</ModalHeader>
<ModalBody>
<p className="w-full h-fit text-md font-normal">{description}</p>
<div className="py-6">
<HighchartsReact
highcharts={Highcharts}
options={chartOptions}
containerProps={{ style: { width: '100%', height: '40vh', borderRadius: '0 0 0.5rem 0.5rem' } }}
/>
</div>
</ModalBody>
<ModalFooter>
<Button color="primary" onPress={downloadDataJson}>
Download data as JSON
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
plaume8 marked this conversation as resolved.
Show resolved Hide resolved

// Button to trigger the full screen modal; rendered if `expandable==true`
const fullScreenButton = expandable ? (
<Button isIconOnly variant="light" size="sm" onClick={onOpen}>
<Maximize4 className={`h-${ICON_BUTTON_SIZE} w-${ICON_BUTTON_SIZE}`} />
</Button>
) : null;

// Description text element should only be rendered if description is available
const descriptionText = description ? (
<p className={`w-full h-fit pb-4 ${DESCRIPTION_TEXT_SIZE} px-3`}> {description} </p>
) : null;
plaume8 marked this conversation as resolved.
Show resolved Hide resolved

return (
<>
<div className="w-full h-fit flex-col rounded-md bg-background">
<div
className={`w-full h-fit flex flex-row justify-between items-start gap-3 pl-3 pb-${HEADER_BOTTOM_PADDING}`}
>
<p className={`${TITLE_TEXT_SIZE} font-normal pt-2 flex flex-row items-center`}> {title} </p>
plaume8 marked this conversation as resolved.
Show resolved Hide resolved
{fullScreenButton}
</div>
{descriptionText}
<HighchartsReact
highcharts={Highcharts}
options={chartOptions}
containerProps={{
style: {
width: '100%',
height: `${CHART_HEIGHT}rem`,
borderRadius: '0 0 0.375rem 0.375rem',
},
}}
/>
</div>
{fullScreenModal}
</>
);
}
1 change: 1 addition & 0 deletions src/domain/entities/charts/BalanceOfTradeGraph.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ChartData } from '../common/ChartData';

export interface BalanceOfTradeGraph {
type: 'BalanceOfTradeGraph';
data: ChartData[];
}
1 change: 1 addition & 0 deletions src/domain/entities/charts/CurrencyExchangeGraph.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChartData } from '../common/ChartData';

export interface CurrencyExchangeGraph {
type: 'CurrencyExchangeGraph';
name: string;
source: string;
updated: string;
Expand Down
1 change: 1 addition & 0 deletions src/domain/entities/charts/FcsChartData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface FcsChartData {
type: 'FcsChartData';
x: string;
fcs: number;
fcsHigh: number;
Expand Down
1 change: 1 addition & 0 deletions src/domain/entities/charts/InflationGraphs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChartData } from '../common/ChartData';

export interface InflationGraphs {
type: 'InflationGraphs';
headline: {
data: ChartData[];
};
Expand Down
Loading
Loading