Skip to content

Commit

Permalink
Improve chart labels
Browse files Browse the repository at this point in the history
  • Loading branch information
frozenhelium committed Mar 12, 2024
1 parent 35fa8fc commit 61c3ba8
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 65 deletions.
46 changes: 39 additions & 7 deletions lib/src/components/BarChartContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import styles from './styles.module.css';

type TextNodeProps = Omit<DefaultTextNodeProps, 'as'>;

const DEFAULT_CHART_HEIGHT = 320;

export interface Props {
className?: string;
title?: TextNodeProps;
Expand All @@ -15,6 +17,8 @@ export interface Props {
chartHeight?: number;
svgClassName?: string;
containerRef?: React.RefObject<HTMLDivElement>;
xAxisLabel?: TextNodeProps;
yAxisLabel?: TextNodeProps;
}

function BarChartContainer(props: Props) {
Expand All @@ -23,7 +27,9 @@ function BarChartContainer(props: Props) {
title,
subTitle,
children,
chartHeight,
chartHeight = DEFAULT_CHART_HEIGHT,
xAxisLabel,
yAxisLabel,
svgClassName,
containerRef,
} = props;
Expand All @@ -43,14 +49,40 @@ function BarChartContainer(props: Props) {
{...subTitle}
/>
)}
<div ref={containerRef}>
<svg
className={_cs(styles.svg, svgClassName)}
style={isDefined(chartHeight) ? { height: `${chartHeight}px}` } : undefined}
<div className={styles.chartLayoutContainer}>
{isDefined(xAxisLabel) && (
<TextNode
// eslint-disable-next-line react/jsx-props-no-spreading
{...xAxisLabel}
style={{
height: `${chartHeight}px`,
writingMode: 'vertical-rl',
transform: 'rotate(180deg)',
textAlign: 'center',
...xAxisLabel.style,
}}
/>
)}
<div
className={styles.chartContainer}
ref={containerRef}
style={{ height: `${chartHeight}px` }}
>
{children}
</svg>
<svg className={_cs(styles.svg, svgClassName)}>
{children}
</svg>
</div>
</div>
{isDefined(yAxisLabel) && (
<TextNode
// eslint-disable-next-line react/jsx-props-no-spreading
{...yAxisLabel}
style={{
textAlign: 'center',
...yAxisLabel.style,
}}
/>
)}
</div>
);
}
Expand Down
14 changes: 11 additions & 3 deletions lib/src/components/BarChartContainer/styles.module.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
.bar-chart-container {
.svg {
width: 100%;
height: 20rem;
.chart-layout-container {
display: flex;

.chart-container {
flex-grow: 1;

.svg {
width: 100%;
height: 100%;
}
}
}
}
67 changes: 13 additions & 54 deletions lib/src/components/ChartAxes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import React, {
useCallback,
useRef,
} from 'react';
import { _cs, isDefined } from '@togglecorp/fujs';

import TextNode, { Props as DefaultTextNodeProps } from '../TextNode';
import { isDefined } from '@togglecorp/fujs';

import {
type Rect,
Expand All @@ -14,8 +12,6 @@ import {

import styles from './styles.module.css';

type AxisLabelProps = Omit<DefaultTextNodeProps, 'as'>;

type Key = string | number;

interface TickX {
Expand Down Expand Up @@ -43,8 +39,6 @@ export interface Props {
tooltipSelector?: (key: Key, i: number) => React.ReactNode;
onHover?: (key: Key | undefined, i: number | undefined) => void;
onClick?: (key: Key, i: number) => void;
yAxisLabel?: AxisLabelProps & { width?: number };
xAxisLabel?: AxisLabelProps & { height?: number };

xAxisGridLineStyle?: React.CSSProperties;
yAxisGridLineStyle?: React.CSSProperties;
Expand All @@ -68,8 +62,6 @@ function ChartAxes(props: Props) {
tooltipSelector,
onHover,
onClick,
yAxisLabel,
xAxisLabel,
xAxisGridLineStyle,
yAxisGridLineStyle,
xAxisLineStyle,
Expand Down Expand Up @@ -128,9 +120,7 @@ function ChartAxes(props: Props) {
}

const xAxisDiff = dataAreaSize.width / xAxisTicks.length;

const yAxisLabelWidth = yAxisLabel?.width ?? 20;
const xAxisLabelHeight = xAxisLabel?.height ?? 20;
const xAxisGapDiff = dataAreaSize.width / (xAxisTicks.length - 1);

const yAxisAreaX1 = chartMargin.left;
const yAxisAreaX2 = chartMargin.left + yAxisWidth;
Expand All @@ -145,22 +135,6 @@ function ChartAxes(props: Props) {

return (
<g className={styles.chartAxes}>
{isDefined(yAxisLabel) && (
<foreignObject
x={0}
y={chartSize.height - yAxisLabelWidth}
width={chartSize.height}
height={yAxisLabelWidth}
className={styles.yAxisLabelContainer}
style={{ transformOrigin: `0 ${chartSize.height - yAxisLabelWidth}px` }}
>
<TextNode
// eslint-disable-next-line react/jsx-props-no-spreading
{...yAxisLabel}
className={_cs(styles.yAxisLabel, yAxisLabel.className)}
/>
</foreignObject>
)}
<g>
{yAxisTicks.map((pointData, i) => (
<Fragment key={pointData.y}>
Expand Down Expand Up @@ -205,6 +179,16 @@ function ChartAxes(props: Props) {
))}
</g>
<g>
{yAxisLineStyle && (
<line
className={styles.yAxisGridLine}
style={yAxisLineStyle}
x1={dataAreaOffset.left - xAxisGapDiff}
y1={chartAreaY1}
x2={dataAreaOffset.left - xAxisGapDiff}
y2={xAxisAreaY1}
/>
)}
{xAxisTicks.map((pointData, i) => {
const tick = pointData;

Expand All @@ -218,17 +202,7 @@ function ChartAxes(props: Props) {

return (
<Fragment key={tick.x}>
{i === 0 && yAxisLineStyle && (
<line
className={styles.yAxisGridLine}
style={yAxisLineStyle}
x1={x}
y1={chartAreaY1}
x2={x}
y2={xAxisAreaY1}
/>
)}
{i !== 0 && yAxisGridLineStyle && (
{yAxisGridLineStyle && (
<line
className={styles.yAxisGridLine}
style={yAxisGridLineStyle}
Expand Down Expand Up @@ -277,21 +251,6 @@ function ChartAxes(props: Props) {
);
})}
</g>
{isDefined(xAxisLabel) && (
<foreignObject
x={dataAreaOffset.left}
y={chartSize.height - xAxisLabelHeight - dataAreaOffset.top}
width={dataAreaSize.width}
height={xAxisLabelHeight}
className={styles.xAxisLabelContainer}
>
<TextNode
// eslint-disable-next-line react/jsx-props-no-spreading
{...xAxisLabel}
className={_cs(styles.xAxisLabel, xAxisLabel.className)}
/>
</foreignObject>
)}
</g>
);
}
Expand Down
37 changes: 37 additions & 0 deletions storybook/src/stories/CategoricalBarChart.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ type Story = StoryObj<BarChartPropsForStory>;
export const Simple: Story = {
args: {
data: chartData,
title: {
children: 'Categorical Bar Chart',
style: {
fontFamily: 'Source Sans Pro, sans-serif',
margin: '0 0 0.5rem 0',
},
},
subTitle: {
children: 'Bar graphs/charts provide a visual presentation of categorical data. Categorical data is a grouping of data into discrete groups, such as months of the year, age group, shoe sizes, and animals. These categories are usually qualitative. In a column (vertical) bar chart, categories appear along the horizontal axis and the height of the bar corresponds to the value of each category.',
style: {
color: 'gray',
fontFamily: 'Source Sans Pro, sans-serif',
marginBottom: '2rem',
},
},
xAxisLabel: {
children: 'Hello this is X-Axis label',
},
yAxisLabel: {
children: 'Hello this is Y-Axis label',
},
chartOptions: {
keySelector: ({ id }) => id,
xValueSelector: ({ x }) => x,
Expand All @@ -57,5 +78,21 @@ export const Simple: Story = {
},
yValueKeys: ['y1', 'y2', 'y3'],
colorSelector: (key) => colorMap[key],
chartAxesOptions: {
xAxisLineStyle: {
stroke: 'teal',
strokeWidth: '2pt',
},
xAxisGridLineStyle: {
stroke: 'lightgray',
},
yAxisLineStyle: {
stroke: 'teal',
strokeWidth: '2pt',
},
yAxisGridLineStyle: {
stroke: 'lightgray',
},
},
},
};
9 changes: 8 additions & 1 deletion storybook/src/stories/NumericBarChart.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const Simple: Story = {
children: 'A numeric bar chart or bar graph is a chart or graph that presents numeric data with rectangular bars with heights or lengths proportional to the values that they represent. The bars can be plotted vertically or horizontally. A vertical bar chart is sometimes called a column chart.',
style: {
textAlign: 'right',
color: 'tomato',
color: 'gray',
},
},
data: chartData,
Expand Down Expand Up @@ -87,6 +87,13 @@ export const Simple: Story = {
xAxisGridLineStyle: {
stroke: 'lightgray',
},
yAxisLineStyle: {
stroke: 'teal',
strokeWidth: '2pt',
},
yAxisGridLineStyle: {
stroke: 'lightgray',
},
},
},
};

0 comments on commit 61c3ba8

Please sign in to comment.