Skip to content

Commit

Permalink
fix(D3 plugin): fix right chart margin (#286)
Browse files Browse the repository at this point in the history
* fix(D3 plugin): fix right chart margin

* text-overflow

* fix

* fix review
  • Loading branch information
kuzmadom authored Sep 11, 2023
1 parent 6399916 commit 7d81921
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 37 deletions.
3 changes: 2 additions & 1 deletion src/plugins/d3/__stories__/scatter-performance.json
Original file line number Diff line number Diff line change
Expand Up @@ -32636,7 +32636,8 @@
"yLabel": "109 300"
},
"category": "Mbour",
"y": 109300
"y": 109300,
"radius": 10
}
],
"custom": {
Expand Down
47 changes: 31 additions & 16 deletions src/plugins/d3/renderer/components/AxisX.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {AxisScale, AxisDomain} from 'd3';
import {block} from '../../../../utils/cn';

import type {ChartScale, PreparedAxis} from '../hooks';
import {formatAxisTickLabel, parseTransformStyle} from '../utils';
import {formatAxisTickLabel, parseTransformStyle, setEllipsisForOverflowText} from '../utils';

const b = block('d3-axis');
const EMPTY_SPACE_BETWEEN_LABELS = 10;
Expand All @@ -15,10 +15,11 @@ type Props = {
width: number;
height: number;
scale: ChartScale;
chartWidth: number;
};

// FIXME: add overflow ellipsis for the labels that out of boundaries
export const AxisX = ({axis, width, height, scale}: Props) => {
export const AxisX = ({axis, width, height, scale, chartWidth}: Props) => {
const ref = React.useRef<SVGGElement>(null);

React.useEffect(() => {
Expand Down Expand Up @@ -68,20 +69,7 @@ export const AxisX = ({axis, width, height, scale}: Props) => {
svgElement.select('.tick').remove();
}

if (axis.title.text) {
const textY =
axis.title.height + parseInt(axis.labels.style.fontSize) + axis.labels.padding;

svgElement
.append('text')
.attr('class', b('title'))
.attr('text-anchor', 'middle')
.attr('x', width / 2)
.attr('y', textY)
.attr('font-size', axis.title.style.fontSize)
.text(axis.title.text);
}

// remove overlapping labels
let elementX = 0;
svgElement
.selectAll('.tick')
Expand All @@ -96,6 +84,33 @@ export const AxisX = ({axis, width, height, scale}: Props) => {
return false;
})
.remove();

// add an ellipsis to the labels on the right that go beyond the boundaries of the chart
svgElement.selectAll('.tick text').each(function () {
const node = this as unknown as SVGTextElement;
const textRect = node.getBoundingClientRect();

if (textRect.right > chartWidth) {
const maxWidth = textRect.width - (textRect.right - chartWidth) * 2;
select(node).call(setEllipsisForOverflowText, maxWidth);
}
});

// add an axis header if necessary
if (axis.title.text) {
const textY =
axis.title.height + parseInt(axis.labels.style.fontSize) + axis.labels.padding;

svgElement
.append('text')
.attr('class', b('title'))
.attr('text-anchor', 'middle')
.attr('x', width / 2)
.attr('y', textY)
.attr('font-size', axis.title.style.fontSize)
.text(axis.title.text)
.call(setEllipsisForOverflowText, width);
}
}, [axis, width, height, scale]);

return <g ref={ref} />;
Expand Down
1 change: 1 addition & 0 deletions src/plugins/d3/renderer/components/Chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export const Chart = (props: Props) => {
width={boundsWidth}
height={boundsHeight}
scale={xScale}
chartWidth={width}
/>
</g>
</React.Fragment>
Expand Down
16 changes: 10 additions & 6 deletions src/plugins/d3/renderer/hooks/useAxisScales/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,17 @@ const createScales = (args: Args) => {
let xScale: ChartScale | undefined;
let yScale: ChartScale | undefined;

const xAxisMinPadding = boundsWidth * xAxis.maxPadding;
const xRange = [0, boundsWidth - xAxisMinPadding];

switch (xType) {
case 'linear': {
const domain = getDomainDataXBySeries(visibleSeries);
const range = [0, boundsWidth - boundsWidth * xAxis.maxPadding];

if (isNumericalArrayData(domain)) {
const [domainXMin, xMax] = extent(domain) as [number, number];
const xMinValue = typeof xMin === 'number' ? xMin : domainXMin;
xScale = scaleLinear().domain([xMinValue, xMax]).range(range).nice();
xScale = scaleLinear().domain([xMinValue, xMax]).range(xRange).nice();
}

break;
Expand All @@ -94,22 +96,24 @@ const createScales = (args: Args) => {
series: visibleSeries,
});
xScale = scaleBand().domain(filteredCategories).range([0, boundsWidth]);

if (xScale.step() / 2 < xAxisMinPadding) {
xScale.range(xRange);
}
}

break;
}
case 'datetime': {
const range = [0, boundsWidth - boundsWidth * xAxis.maxPadding];

if (xTimestamps) {
const [xMin, xMax] = extent(xTimestamps) as [number, number];
xScale = scaleUtc().domain([xMin, xMax]).range(range).nice();
xScale = scaleUtc().domain([xMin, xMax]).range(xRange).nice();
} else {
const domain = getDomainDataXBySeries(visibleSeries);

if (isNumericalArrayData(domain)) {
const [xMin, xMax] = extent(domain) as [number, number];
xScale = scaleUtc().domain([xMin, xMax]).range(range).nice();
xScale = scaleUtc().domain([xMin, xMax]).range(xRange).nice();
}
}

Expand Down
18 changes: 4 additions & 14 deletions src/plugins/d3/renderer/hooks/useChartOptions/chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,20 +124,10 @@ const getMarginLeft = (args: {
return marginLeft;
};

const getMarginRight = (args: {
chart: ChartKitWidgetData['chart'];
hasAxisRelatedSeries: boolean;
series: ChartKitWidgetData['series'];
preparedXAxis: PreparedAxis;
}) => {
const {chart, hasAxisRelatedSeries, series, preparedXAxis} = args;
let marginRight = get(chart, 'margin.right', 0);

if (hasAxisRelatedSeries) {
marginRight += getAxisLabelMaxWidth({axis: preparedXAxis, series: series.data}) / 2;
}
const getMarginRight = (args: {chart: ChartKitWidgetData['chart']}) => {
const {chart} = args;

return marginRight;
return get(chart, 'margin.right', 0);
};

export const getPreparedChart = (args: {
Expand All @@ -158,7 +148,7 @@ export const getPreparedChart = (args: {
preparedXAxis,
});
const marginLeft = getMarginLeft({chart, hasAxisRelatedSeries, series, preparedY1Axis});
const marginRight = getMarginRight({chart, hasAxisRelatedSeries, series, preparedXAxis});
const marginRight = getMarginRight({chart});

return {
margin: {
Expand Down
1 change: 1 addition & 0 deletions src/plugins/d3/renderer/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type {FormatNumberOptions} from '../../../shared';
import {DEFAULT_AXIS_LABEL_FONT_SIZE} from '../constants';

export * from './math';
export * from './text';

const CHARTS_WITHOUT_AXIS: ChartKitWidgetSeries['type'][] = ['pie'];

Expand Down
17 changes: 17 additions & 0 deletions src/plugins/d3/renderer/utils/text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {Selection} from 'd3-selection';

export function setEllipsisForOverflowText(
selection: Selection<SVGTextElement, any, null, undefined>,
maxWidth: number,
) {
let text = selection.text();
selection.text(null).attr('text-anchor', 'left').append('title').text(text);
const tSpan = selection.append('tspan').text(text);

let textLength = tSpan.node()?.getComputedTextLength() || 0;
while (textLength > maxWidth && text.length > 1) {
text = text.slice(0, -1);
tSpan.text(text + '…');
textLength = tSpan.node()?.getComputedTextLength() || 0;
}
}

0 comments on commit 7d81921

Please sign in to comment.