Skip to content

Commit

Permalink
refactor: [M3-8771] - Add more customization to legends and charts (#…
Browse files Browse the repository at this point in the history
…11145)

* refactor: [M3-8771] - Add more customization to legends and charts

* Legend whitespace

* Addtl props

* Added changeset: Add more customization to legends and charts

* Pass hasFixedLegendHeight

* Review updates @hkhalil-akamai @hana-akamai

* Refactor and update test

* @hana-akamai review notes

* @nikhagra-akamai review updates

* round to nearest 1000 for values over 100k and under 1M

* Update packages/manager/src/components/LineGraph/MetricsDisplay.tsx

---------

Co-authored-by: Jaalah Ramos <jaalah.ramos@gmail.com>
Co-authored-by: Hana Xu <hxu@akamai.com>
Co-authored-by: Hana Xu <115299789+hana-akamai@users.noreply.github.com>
  • Loading branch information
4 people authored Nov 1, 2024
1 parent 8ed5a49 commit 747ce0b
Show file tree
Hide file tree
Showing 12 changed files with 232 additions and 114 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Tech Stories
---

Add more customization to legends and charts ([#11145](https://github.com/linode/manager/pull/11145))
47 changes: 34 additions & 13 deletions packages/manager/src/components/AreaChart/AreaChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,25 @@ interface AreaChartProps {
fillOpacity?: number;

/**
* maximum height of the chart container
* The height of chart container.
*/
height: number;
height?: number;

/**
* Sets the height of the legend. Overflow scroll if the content exceeds the height.
*/
legendHeight?: string;

/**
* list of legends rows to be displayed
*/
legendRows?: Omit<MetricsDisplayRow[], 'handleLegendClick'>;

/**
* The sizes of whitespace around the container.
*/
margin?: { bottom: number; left: number; right: number; top: number };

/**
* true to display legends rows else false to hide
* @default false
Expand All @@ -106,6 +116,11 @@ interface AreaChartProps {
*/
variant?: 'area' | 'line';

/**
* The width of chart container.
*/
width?: number;

/**
* x-axis properties
*/
Expand All @@ -118,12 +133,15 @@ export const AreaChart = (props: AreaChartProps) => {
ariaLabel,
data,
fillOpacity,
height,
height = '100%',
legendHeight,
legendRows,
margin = { bottom: 0, left: -20, right: 0, top: 0 },
showLegend,
timezone,
unit,
variant,
width = '100%',
xAxis,
} = props;

Expand Down Expand Up @@ -174,7 +192,7 @@ export const AreaChart = (props: AreaChartProps) => {
return null;
};

const CustomLegend = () => {
const CustomLegend = ({ legendHeight }: { legendHeight?: string }) => {
if (legendRows) {
const legendRowsWithClickHandler = legendRows.map((legendRow) => ({
...legendRow,
Expand All @@ -185,6 +203,7 @@ export const AreaChart = (props: AreaChartProps) => {
<StyledBottomLegend>
<MetricsDisplay
hiddenRows={activeSeries}
legendHeight={legendHeight}
rows={legendRowsWithClickHandler}
/>
</StyledBottomLegend>
Expand All @@ -195,10 +214,16 @@ export const AreaChart = (props: AreaChartProps) => {

const accessibleDataKeys = areas.map((area) => area.dataKey);

const legendStyles = {
bottom: 0,
left: 0,
width: '100%',
};

return (
<>
<ResponsiveContainer height={height} width="100%">
<_AreaChart aria-label={ariaLabel} data={data}>
<ResponsiveContainer height={height} width={width}>
<_AreaChart aria-label={ariaLabel} data={data} margin={margin}>
<CartesianGrid
stroke={theme.color.grey7}
strokeDasharray="3 3"
Expand Down Expand Up @@ -237,18 +262,14 @@ export const AreaChart = (props: AreaChartProps) => {
handleLegendClick(dataKey as string);
}
}}
wrapperStyle={{
left: 25,
}}
iconType="square"
wrapperStyle={legendStyles}
/>
)}
{showLegend && legendRows && (
<Legend
wrapperStyle={{
left: 20,
}}
content={<CustomLegend />}
content={<CustomLegend legendHeight={legendHeight} />}
wrapperStyle={legendStyles}
/>
)}
{areas.map(({ color, dataKey }) => (
Expand Down
3 changes: 2 additions & 1 deletion packages/manager/src/components/AreaChart/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ describe('humanizeLargeData', () => {
it('should return the value as an abbreviated string if the value is >= 1000', () => {
expect(humanizeLargeData(999)).toBe('999');
expect(humanizeLargeData(1125)).toBe('1.1K');
expect(humanizeLargeData(231434)).toBe('231.4K');
expect(humanizeLargeData(55555)).toBe('55.6K');
expect(humanizeLargeData(231434)).toBe('231K');
expect(humanizeLargeData(1010000)).toBe('1M');
expect(humanizeLargeData(12345678900)).toBe('12.3B');
expect(humanizeLargeData(1543212345678)).toBe('1.5T');
Expand Down
3 changes: 3 additions & 0 deletions packages/manager/src/components/AreaChart/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export const humanizeLargeData = (value: number) => {
if (value >= 1000000) {
return +(value / 1000000).toFixed(1) + 'M';
}
if (value >= 100000) {
return +(value / 1000).toFixed(0) + 'K';
}
if (value >= 1000) {
return +(value / 1000).toFixed(1) + 'K';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ export const StyledTableCell = styled(TableCell, {
alignItems: 'center',
display: 'flex',
justifyContent: 'flex-start',
textAlign: 'left',
[theme.breakpoints.down('sm')]: {
padding: 0,
},
whiteSpace: 'nowrap',
},
}));

Expand All @@ -40,6 +42,7 @@ export const StyledButton = styled(Button, {
backgroundColor: hidden
? theme.color.disabledText
: theme.graphs[legendColor],
flexShrink: 0,
},
}),
}));
58 changes: 47 additions & 11 deletions packages/manager/src/components/LineGraph/MetricsDisplay.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { screen } from '@testing-library/react';
import * as React from 'react';

import {
MetricsDisplay,
metricsBySection,
} from 'src/components/LineGraph/MetricsDisplay';
import { MetricsDisplay } from 'src/components/LineGraph/MetricsDisplay';
import { formatPercentage } from 'src/utilities/statMetrics';
import { renderWithTheme } from 'src/utilities/testHelpers';

Expand Down Expand Up @@ -119,12 +117,50 @@ describe('CPUMetrics', () => {
});

describe('metrics by section', () => {
it('returns expected metric data', () => {
const metrics = { average: 5, last: 8, length: 10, max: 10, total: 80 };
expect(metricsBySection(metrics)).toHaveLength(3);
expect(metricsBySection(metrics)).toBeInstanceOf(Array);
expect(metricsBySection(metrics)[0]).toEqual(metrics.max);
expect(metricsBySection(metrics)[1]).toEqual(metrics.average);
expect(metricsBySection(metrics)[2]).toEqual(metrics.last);
const sampleMetrics = { average: 5, last: 8, length: 10, max: 10, total: 80 };
const defaultProps = {
rows: [
{
data: sampleMetrics,
format: (n: number) => n.toString(),
legendColor: 'blue' as const,
legendTitle: 'Test Metric',
},
],
};

it('renders metric data in correct order and format', () => {
renderWithTheme(<MetricsDisplay {...defaultProps} />);

// Check if headers are rendered in correct order
const headers = screen.getAllByRole('columnheader');
expect(headers[1]).toHaveTextContent('Max');
expect(headers[2]).toHaveTextContent('Avg');
expect(headers[3]).toHaveTextContent('Last');

// Check if metric values are rendered in correct order
const cells = screen.getAllByRole('cell');
expect(cells[1]).toHaveTextContent('10'); // max
expect(cells[2]).toHaveTextContent('5'); // average
expect(cells[3]).toHaveTextContent('8'); // last
});

it('formats metric values using provided format function', () => {
const formatFn = (n: number) => `${n}%`;
renderWithTheme(
<MetricsDisplay
rows={[
{
...defaultProps.rows[0],
format: formatFn,
},
]}
/>
);

const cells = screen.getAllByRole('cell');
expect(cells[1]).toHaveTextContent('10%');
expect(cells[2]).toHaveTextContent('5%');
expect(cells[3]).toHaveTextContent('8%');
});
});
Loading

0 comments on commit 747ce0b

Please sign in to comment.