@gravity-ui/charts 1.30.1 → 1.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/cjs/components/AxisX/AxisX.js +6 -4
  2. package/dist/cjs/components/AxisX/prepare-axis-data.js +2 -0
  3. package/dist/cjs/components/AxisX/types.d.ts +1 -0
  4. package/dist/cjs/components/AxisY/AxisY.js +4 -2
  5. package/dist/cjs/components/AxisY/prepare-axis-data.js +2 -0
  6. package/dist/cjs/components/AxisY/types.d.ts +1 -0
  7. package/dist/cjs/components/ChartInner/index.js +7 -1
  8. package/dist/cjs/components/ChartInner/types.d.ts +6 -0
  9. package/dist/cjs/components/index.d.ts +1 -0
  10. package/dist/cjs/components/index.js +4 -4
  11. package/dist/cjs/components/utils.js +2 -2
  12. package/dist/cjs/constants/line-styles.d.ts +6 -0
  13. package/dist/cjs/constants/line-styles.js +7 -0
  14. package/dist/cjs/hooks/useAxis/types.d.ts +2 -0
  15. package/dist/cjs/hooks/useAxis/utils.d.ts +1 -0
  16. package/dist/cjs/hooks/useAxis/utils.js +2 -1
  17. package/dist/cjs/hooks/useChartOptions/title.js +1 -1
  18. package/dist/cjs/hooks/useSeries/prepare-line.js +8 -1
  19. package/dist/cjs/hooks/useSeries/types.d.ts +2 -1
  20. package/dist/cjs/hooks/useShapes/area/prepare-data.d.ts +1 -0
  21. package/dist/cjs/hooks/useShapes/area/prepare-data.js +2 -2
  22. package/dist/cjs/hooks/useShapes/bar-x/prepare-data.d.ts +1 -0
  23. package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +15 -6
  24. package/dist/cjs/hooks/useShapes/index.js +2 -0
  25. package/dist/cjs/hooks/useShapes/line/index.js +1 -1
  26. package/dist/cjs/hooks/useShapes/line/prepare-data.js +2 -1
  27. package/dist/cjs/hooks/useShapes/line/types.d.ts +2 -1
  28. package/dist/cjs/types/chart/axis.d.ts +3 -0
  29. package/dist/cjs/types/chart/line.d.ts +3 -1
  30. package/dist/cjs/types/chart/series.d.ts +6 -1
  31. package/dist/cjs/utils/chart/index.d.ts +1 -1
  32. package/dist/cjs/utils/chart/index.js +23 -19
  33. package/dist/esm/components/AxisX/AxisX.js +6 -4
  34. package/dist/esm/components/AxisX/prepare-axis-data.js +2 -0
  35. package/dist/esm/components/AxisX/types.d.ts +1 -0
  36. package/dist/esm/components/AxisY/AxisY.js +4 -2
  37. package/dist/esm/components/AxisY/prepare-axis-data.js +2 -0
  38. package/dist/esm/components/AxisY/types.d.ts +1 -0
  39. package/dist/esm/components/ChartInner/index.js +7 -1
  40. package/dist/esm/components/ChartInner/types.d.ts +6 -0
  41. package/dist/esm/components/index.d.ts +1 -0
  42. package/dist/esm/components/index.js +4 -4
  43. package/dist/esm/components/utils.js +2 -2
  44. package/dist/esm/constants/line-styles.d.ts +6 -0
  45. package/dist/esm/constants/line-styles.js +7 -0
  46. package/dist/esm/hooks/useAxis/types.d.ts +2 -0
  47. package/dist/esm/hooks/useAxis/utils.d.ts +1 -0
  48. package/dist/esm/hooks/useAxis/utils.js +2 -1
  49. package/dist/esm/hooks/useChartOptions/title.js +1 -1
  50. package/dist/esm/hooks/useSeries/prepare-line.js +8 -1
  51. package/dist/esm/hooks/useSeries/types.d.ts +2 -1
  52. package/dist/esm/hooks/useShapes/area/prepare-data.d.ts +1 -0
  53. package/dist/esm/hooks/useShapes/area/prepare-data.js +2 -2
  54. package/dist/esm/hooks/useShapes/bar-x/prepare-data.d.ts +1 -0
  55. package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +15 -6
  56. package/dist/esm/hooks/useShapes/index.js +2 -0
  57. package/dist/esm/hooks/useShapes/line/index.js +1 -1
  58. package/dist/esm/hooks/useShapes/line/prepare-data.js +2 -1
  59. package/dist/esm/hooks/useShapes/line/types.d.ts +2 -1
  60. package/dist/esm/types/chart/axis.d.ts +3 -0
  61. package/dist/esm/types/chart/line.d.ts +3 -1
  62. package/dist/esm/types/chart/series.d.ts +6 -1
  63. package/dist/esm/utils/chart/index.d.ts +1 -1
  64. package/dist/esm/utils/chart/index.js +23 -19
  65. package/package.json +1 -1
@@ -112,7 +112,7 @@ export const AxisX = (props) => {
112
112
  .attr('fill', (d) => d.color)
113
113
  .attr('opacity', (d) => d.opacity);
114
114
  plotBandsSelection.each(function () {
115
- var _a, _b;
115
+ var _a, _b, _c;
116
116
  const plotBandSelection = select(this);
117
117
  const band = plotBandSelection.datum();
118
118
  const label = band.label;
@@ -125,7 +125,8 @@ export const AxisX = (props) => {
125
125
  .style('font-weight', (_b = label.style.fontWeight) !== null && _b !== void 0 ? _b : '')
126
126
  .style('dominant-baseline', 'text-before-edge')
127
127
  .style('text-anchor', 'start')
128
- .style('transform', `translate(${label.x}px, ${label.y}px) rotate(${label.rotate}deg)`);
128
+ .style('transform', `translate(${label.x}px, ${label.y}px) rotate(${label.rotate}deg)`)
129
+ .attr('data-qa', (_c = label.qa) !== null && _c !== void 0 ? _c : null);
129
130
  }
130
131
  });
131
132
  };
@@ -153,7 +154,7 @@ export const AxisX = (props) => {
153
154
  .attr('stroke-dasharray', (d) => getLineDashArray(d.dashStyle, d.lineWidth))
154
155
  .attr('opacity', (d) => d.opacity);
155
156
  plotLinesSelection.each(function () {
156
- var _a, _b;
157
+ var _a, _b, _c;
157
158
  const itemSelection = select(this);
158
159
  const plotLine = itemSelection.datum();
159
160
  const label = plotLine.label;
@@ -166,7 +167,8 @@ export const AxisX = (props) => {
166
167
  .style('font-weight', (_b = label.style.fontWeight) !== null && _b !== void 0 ? _b : '')
167
168
  .style('dominant-baseline', 'text-before-edge')
168
169
  .style('text-anchor', 'start')
169
- .style('transform', `translate(${label.x}px, ${label.y}px) rotate(${label.rotate}deg)`);
170
+ .style('transform', `translate(${label.x}px, ${label.y}px) rotate(${label.rotate}deg)`)
171
+ .attr('data-qa', (_c = label.qa) !== null && _c !== void 0 ? _c : null);
170
172
  }
171
173
  });
172
174
  };
@@ -263,6 +263,7 @@ export async function prepareXAxisData({ axis, yAxis, scale, boundsWidth, bounds
263
263
  x: plotBand.label.padding,
264
264
  y: plotBand.label.padding + ((_f = labelSize === null || labelSize === void 0 ? void 0 : labelSize.width) !== null && _f !== void 0 ? _f : 0),
265
265
  rotate: -90,
266
+ qa: plotBand.label.qa,
266
267
  }
267
268
  : null,
268
269
  });
@@ -289,6 +290,7 @@ export async function prepareXAxisData({ axis, yAxis, scale, boundsWidth, bounds
289
290
  x: plotLineValue - plotLine.label.padding - size.height,
290
291
  y: plotLine.label.padding + size.width,
291
292
  rotate: -90,
293
+ qa: plotLine.label.qa,
292
294
  };
293
295
  }
294
296
  plotLines.push({
@@ -47,6 +47,7 @@ export type AxisPlotLineLabel = {
47
47
  style: BaseTextStyle;
48
48
  x: number;
49
49
  y: number;
50
+ qa?: string;
50
51
  };
51
52
  export type AxisPlotLineData = {
52
53
  layerPlacement: PlotLayerPlacement;
@@ -118,7 +118,7 @@ export const AxisY = (props) => {
118
118
  .attr('fill', (d) => d.color)
119
119
  .attr('opacity', (d) => d.opacity);
120
120
  plotBandsSelection.each(function () {
121
- var _a, _b;
121
+ var _a, _b, _c;
122
122
  const plotBandSelection = select(this);
123
123
  const band = plotBandSelection.datum();
124
124
  const label = band.label;
@@ -130,6 +130,7 @@ export const AxisY = (props) => {
130
130
  .style('font-size', label.style.fontSize)
131
131
  .style('font-weight', (_b = label.style.fontWeight) !== null && _b !== void 0 ? _b : '')
132
132
  .style('dominant-baseline', 'text-before-edge')
133
+ .attr('data-qa', (_c = label.qa) !== null && _c !== void 0 ? _c : null)
133
134
  .attr('x', label.x)
134
135
  .attr('y', label.y);
135
136
  }
@@ -159,7 +160,7 @@ export const AxisY = (props) => {
159
160
  .attr('stroke-dasharray', (d) => getLineDashArray(d.dashStyle, d.lineWidth))
160
161
  .attr('opacity', (d) => d.opacity);
161
162
  plotLinesSelection.each(function () {
162
- var _a, _b;
163
+ var _a, _b, _c;
163
164
  const itemSelection = select(this);
164
165
  const plotLine = itemSelection.datum();
165
166
  const label = plotLine.label;
@@ -171,6 +172,7 @@ export const AxisY = (props) => {
171
172
  .style('font-size', label.style.fontSize)
172
173
  .style('font-weight', (_b = label.style.fontWeight) !== null && _b !== void 0 ? _b : '')
173
174
  .style('dominant-baseline', 'text-before-edge')
175
+ .attr('data-qa', (_c = label.qa) !== null && _c !== void 0 ? _c : null)
174
176
  .attr('x', label.x)
175
177
  .attr('y', label.y);
176
178
  }
@@ -229,6 +229,7 @@ export async function prepareYAxisData({ axis, split, scale, top: topOffset, wid
229
229
  style: plotBand.label.style,
230
230
  x: plotBand.label.padding,
231
231
  y: plotBand.label.padding,
232
+ qa: plotBand.label.qa,
232
233
  }
233
234
  : null,
234
235
  });
@@ -254,6 +255,7 @@ export async function prepareYAxisData({ axis, split, scale, top: topOffset, wid
254
255
  style: plotLine.label.style,
255
256
  x: plotLine.label.padding,
256
257
  y: Math.max(0, plotLineValue - size.height - plotLine.label.padding),
258
+ qa: plotLine.label.qa,
257
259
  };
258
260
  }
259
261
  plotLines.push({
@@ -58,6 +58,7 @@ export type AxisPlotLineLabel = {
58
58
  style: BaseTextStyle;
59
59
  x: number;
60
60
  y: number;
61
+ qa?: string;
61
62
  };
62
63
  export type AxisPlotLineData = {
63
64
  layerPlacement: PlotLayerPlacement;
@@ -26,7 +26,7 @@ const b = block('chart');
26
26
  const DEBOUNCED_VALUE_DELAY = 10;
27
27
  export const ChartInner = (props) => {
28
28
  var _a, _b, _c, _d, _e, _f;
29
- const { width, height, data } = props;
29
+ const { width, height, data, onReady } = props;
30
30
  const svgRef = React.useRef(null);
31
31
  const resetZoomButtonRef = React.useRef(null);
32
32
  const [htmlLayout, setHtmlLayout] = React.useState(null);
@@ -206,6 +206,12 @@ export const ChartInner = (props) => {
206
206
  updateRangeSliderState,
207
207
  xScale,
208
208
  ]);
209
+ const areShapesReady = shapes.length > 0;
210
+ React.useEffect(() => {
211
+ if (areShapesReady) {
212
+ onReady === null || onReady === void 0 ? void 0 : onReady({ dimensions: { width, height } });
213
+ }
214
+ }, [height, areShapesReady, onReady, width]);
209
215
  const chartContent = (React.createElement(React.Fragment, null,
210
216
  React.createElement("defs", null,
211
217
  React.createElement("clipPath", { id: clipPathId },
@@ -3,4 +3,10 @@ export type ChartInnerProps = {
3
3
  width: number;
4
4
  height: number;
5
5
  data: ChartData;
6
+ onReady?: (args: {
7
+ dimensions: {
8
+ width: number;
9
+ height: number;
10
+ };
11
+ }) => void;
6
12
  };
@@ -15,5 +15,6 @@ export interface ChartProps {
15
15
  data: ChartData;
16
16
  lang?: string;
17
17
  onResize?: ChartOnResize;
18
+ onReady?: ChartOnResize;
18
19
  }
19
20
  export declare const Chart: React.ForwardRefExoticComponent<ChartProps & React.RefAttributes<ChartRef>>;
@@ -7,7 +7,7 @@ import { validateData } from '../validation';
7
7
  import { ChartInner } from './ChartInner';
8
8
  export * from './Tooltip/ChartTooltipContent';
9
9
  export const Chart = React.forwardRef(function Chart(props, forwardedRef) {
10
- const { data, lang, onResize } = props;
10
+ const { data, lang, onResize, onReady } = props;
11
11
  const validatedData = React.useRef();
12
12
  const ref = React.useRef(null);
13
13
  const debounced = React.useRef();
@@ -36,8 +36,8 @@ export const Chart = React.forwardRef(function Chart(props, forwardedRef) {
36
36
  }), [debuncedHandleResize]);
37
37
  React.useEffect(() => {
38
38
  // dimensions initialize
39
- debuncedHandleResize();
40
- }, [debuncedHandleResize]);
39
+ handleResize();
40
+ }, [handleResize]);
41
41
  React.useEffect(() => {
42
42
  const selection = select(window);
43
43
  // https://github.com/d3/d3-selection/blob/main/README.md#handling-events
@@ -62,5 +62,5 @@ export const Chart = React.forwardRef(function Chart(props, forwardedRef) {
62
62
  width: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) || '100%',
63
63
  height: (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) || '100%',
64
64
  position: 'relative',
65
- } }, (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) && (React.createElement(ChartInner, { height: dimensions === null || dimensions === void 0 ? void 0 : dimensions.height, width: dimensions === null || dimensions === void 0 ? void 0 : dimensions.width, data: data }))));
65
+ } }, (dimensions === null || dimensions === void 0 ? void 0 : dimensions.height) && (dimensions === null || dimensions === void 0 ? void 0 : dimensions.width) && (React.createElement(ChartInner, { height: dimensions === null || dimensions === void 0 ? void 0 : dimensions.height, width: dimensions === null || dimensions === void 0 ? void 0 : dimensions.width, data: data, onReady: onReady }))));
66
66
  });
@@ -49,8 +49,8 @@ export function appendLinePathElement({ svgRootElement, height, width, x = 0, li
49
49
  .attr('d', legendSymbolGenerator(points))
50
50
  .attr('fill', 'none')
51
51
  .attr('stroke-width', lineWidth)
52
- .attr('class', className !== null && className !== void 0 ? className : null)
53
- .style('stroke', color !== null && color !== void 0 ? color : '');
52
+ .attr('stroke', color !== null && color !== void 0 ? color : '')
53
+ .attr('class', className !== null && className !== void 0 ? className : null);
54
54
  if (dashStyle) {
55
55
  pathElement.attr('stroke-dasharray', getLineDashArray(dashStyle, lineWidth));
56
56
  }
@@ -18,3 +18,9 @@ export declare enum LineCap {
18
18
  Square = "square",
19
19
  None = "none"
20
20
  }
21
+ export declare enum LineJoin {
22
+ Round = "round",
23
+ Bevel = "bevel",
24
+ Miter = "miter",
25
+ None = "unset"
26
+ }
@@ -18,3 +18,10 @@ export var LineCap;
18
18
  LineCap["Square"] = "square";
19
19
  LineCap["None"] = "none";
20
20
  })(LineCap || (LineCap = {}));
21
+ export var LineJoin;
22
+ (function (LineJoin) {
23
+ LineJoin["Round"] = "round";
24
+ LineJoin["Bevel"] = "bevel";
25
+ LineJoin["Miter"] = "miter";
26
+ LineJoin["None"] = "unset";
27
+ })(LineJoin || (LineJoin = {}));
@@ -13,6 +13,7 @@ export type PreparedAxisPlotBand = Required<AxisPlotBand> & {
13
13
  text: string;
14
14
  style: BaseTextStyle;
15
15
  padding: number;
16
+ qa?: string;
16
17
  };
17
18
  };
18
19
  type PreparedAxisCrosshair = Required<AxisCrosshair>;
@@ -27,6 +28,7 @@ export type PreparedAxisPlotLine = {
27
28
  text: string;
28
29
  style: BaseTextStyle;
29
30
  padding: number;
31
+ qa?: string;
30
32
  };
31
33
  };
32
34
  export type PreparedRangeSlider = DeepRequired<Omit<ChartAxisRangeSlider, 'defaultRange'>> & {
@@ -7,4 +7,5 @@ export declare function prepareAxisPlotLabel(d: AxisPlot): {
7
7
  fontColor: string;
8
8
  };
9
9
  padding: number;
10
+ qa: string | undefined;
10
11
  };
@@ -1,9 +1,10 @@
1
1
  import { DEFAULT_AXIS_LABEL_FONT_SIZE } from '../../constants';
2
2
  export function prepareAxisPlotLabel(d) {
3
- var _a, _b, _c, _d, _e;
3
+ var _a, _b, _c, _d, _e, _f;
4
4
  return {
5
5
  text: (_b = (_a = d.label) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : '',
6
6
  style: Object.assign({ fontSize: DEFAULT_AXIS_LABEL_FONT_SIZE, fontColor: 'var(--g-color-text-secondary)' }, (_c = d.label) === null || _c === void 0 ? void 0 : _c.style),
7
7
  padding: (_e = (_d = d.label) === null || _d === void 0 ? void 0 : _d.padding) !== null && _e !== void 0 ? _e : 5,
8
+ qa: (_f = d.label) === null || _f === void 0 ? void 0 : _f.qa,
8
9
  };
9
10
  }
@@ -14,7 +14,7 @@ export const getPreparedTitle = ({ title, }) => {
14
14
  ? getHorizontalSvgTextHeight({ text: titleText, style: titleStyle }) + TITLE_PADDINGS
15
15
  : 0;
16
16
  const preparedTitle = titleText
17
- ? { text: titleText, style: titleStyle, height: titleHeight }
17
+ ? { text: titleText, style: titleStyle, height: titleHeight, qa: title === null || title === void 0 ? void 0 : title.qa }
18
18
  : undefined;
19
19
  return preparedTitle;
20
20
  };
@@ -1,6 +1,6 @@
1
1
  import get from 'lodash/get';
2
2
  import merge from 'lodash/merge';
3
- import { DASH_STYLE, DEFAULT_DATALABELS_STYLE, LineCap, seriesRangeSliderOptionsDefaults, } from '../../constants';
3
+ import { DASH_STYLE, DEFAULT_DATALABELS_STYLE, LineCap, LineJoin, seriesRangeSliderOptionsDefaults, } from '../../constants';
4
4
  import { getUniqId } from '../../utils';
5
5
  import { DEFAULT_DATALABELS_PADDING, DEFAULT_HALO_OPTIONS, DEFAULT_LEGEND_SYMBOL_PADDING, DEFAULT_POINT_MARKER_OPTIONS, } from './constants';
6
6
  export const DEFAULT_LEGEND_SYMBOL_SIZE = 16;
@@ -12,6 +12,12 @@ function prepareLinecap(dashStyle, series, seriesOptions) {
12
12
  const lineCapFromSeriesOptions = get(seriesOptions, 'line.linecap', defaultLineCap);
13
13
  return get(series, 'linecap', lineCapFromSeriesOptions);
14
14
  }
15
+ function prepareLinejoin(dashStyle, series, seriesOptions) {
16
+ var _a, _b, _c;
17
+ const defaultLinejoin = dashStyle === DASH_STYLE.Solid ? LineJoin.Round : LineJoin.None;
18
+ const linejoinFromSeriesOptions = (_b = (_a = seriesOptions === null || seriesOptions === void 0 ? void 0 : seriesOptions.line) === null || _a === void 0 ? void 0 : _a.linejoin) !== null && _b !== void 0 ? _b : defaultLinejoin;
19
+ return ((_c = series === null || series === void 0 ? void 0 : series.linejoin) !== null && _c !== void 0 ? _c : linejoinFromSeriesOptions);
20
+ }
15
21
  function prepareLineLegendSymbol(series, seriesOptions) {
16
22
  var _a;
17
23
  const symbolOptions = ((_a = series.legend) === null || _a === void 0 ? void 0 : _a.symbol) || {};
@@ -90,6 +96,7 @@ export function prepareLineSeries(args) {
90
96
  marker: prepareMarker(series, seriesOptions),
91
97
  dashStyle: dashStyle,
92
98
  linecap: prepareLinecap(dashStyle, series, seriesOptions),
99
+ linejoin: prepareLinejoin(dashStyle, series, seriesOptions),
93
100
  opacity: get(series, 'opacity', null),
94
101
  cursor: get(series, 'cursor', null),
95
102
  yAxis: get(series, 'yAxis', 0),
@@ -1,4 +1,4 @@
1
- import type { DashStyle, LayoutAlgorithm, LineCap, SeriesOptionsDefaults, SymbolType } from '../../constants';
1
+ import type { DashStyle, LayoutAlgorithm, LineCap, LineJoin, SeriesOptionsDefaults, SymbolType } from '../../constants';
2
2
  import type { AreaSeries, AreaSeriesData, BarXSeries, BarXSeriesData, BarYSeries, BarYSeriesData, BaseTextStyle, ChartLegend, ChartSeries, ChartSeriesRangeSliderOptions, ConnectorCurve, ConnectorShape, FunnelSeries, FunnelSeriesData, HeatmapSeries, HeatmapSeriesData, LineSeries, LineSeriesData, LineSeriesLineBaseStyle, PathLegendSymbolOptions, PieSeries, PieSeriesData, RadarSeries, RadarSeriesCategory, RadarSeriesData, RectLegendSymbolOptions, SankeySeries, SankeySeriesData, ScatterSeries, ScatterSeriesData, SymbolLegendSymbolOptions, TreemapSeries, TreemapSeriesData, ValueFormat, WaterfallSeries, WaterfallSeriesData } from '../../types';
3
3
  export type RectLegendSymbol = {
4
4
  shape: 'rect';
@@ -222,6 +222,7 @@ export type PreparedLineSeries = {
222
222
  };
223
223
  dashStyle: DashStyle;
224
224
  linecap: LineCap;
225
+ linejoin: LineJoin;
225
226
  opacity: number | null;
226
227
  yAxis: number;
227
228
  } & BasePreparedSeries & BasePreparedAxisRelatedSeries;
@@ -12,4 +12,5 @@ export declare const prepareAreaData: (args: {
12
12
  boundsHeight: number;
13
13
  split: PreparedSplit;
14
14
  isOutsideBounds: (x: number, y: number) => boolean;
15
+ isRangeSlider?: boolean;
15
16
  }) => Promise<PreparedAreaData[]>;
@@ -74,7 +74,7 @@ async function prepareDataLabels({ series, points, xMax, yAxisTop, }) {
74
74
  }
75
75
  export const prepareAreaData = async (args) => {
76
76
  var _a, _b;
77
- const { series, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight, split, isOutsideBounds, } = args;
77
+ const { series, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight, split, isOutsideBounds, isRangeSlider, } = args;
78
78
  const [_xMin, xRangeMax] = xScale.range();
79
79
  const xMax = xRangeMax;
80
80
  const result = [];
@@ -147,7 +147,7 @@ export const prepareAreaData = async (args) => {
147
147
  }, []);
148
148
  const labels = [];
149
149
  const htmlElements = [];
150
- if (s.dataLabels.enabled) {
150
+ if (s.dataLabels.enabled && !isRangeSlider) {
151
151
  const labelsData = await prepareDataLabels({ series: s, points, xMax, yAxisTop });
152
152
  labels.push(...labelsData.svgLabels);
153
153
  htmlElements.push(...labelsData.htmlLabels);
@@ -12,4 +12,5 @@ export declare const prepareBarXData: (args: {
12
12
  yScale: (ChartScale | undefined)[];
13
13
  boundsHeight: number;
14
14
  split: PreparedSplit;
15
+ isRangeSlider?: boolean;
15
16
  }) => Promise<PreparedBarXData[]>;
@@ -35,7 +35,7 @@ async function getLabelData(d) {
35
35
  // eslint-disable-next-line complexity
36
36
  export const prepareBarXData = async (args) => {
37
37
  var _a, _b, _c, _d;
38
- const { series, seriesOptions, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight, split, } = args;
38
+ const { series, seriesOptions, xAxis, xScale, yAxis, yScale, boundsHeight: plotHeight, split, isRangeSlider, } = args;
39
39
  const stackGap = seriesOptions['bar-x'].stackGap;
40
40
  const categories = (_a = xAxis === null || xAxis === void 0 ? void 0 : xAxis.categories) !== null && _a !== void 0 ? _a : [];
41
41
  const sortingOptions = get(seriesOptions, 'bar-x.dataSorting');
@@ -99,7 +99,8 @@ export const prepareBarXData = async (args) => {
99
99
  const currentGroupWidth = rectWidth * stacks.length + rectGap * (stacks.length - 1);
100
100
  for (let groupItemIndex = 0; groupItemIndex < stacks.length; groupItemIndex++) {
101
101
  const yValues = stacks[groupItemIndex];
102
- let stackHeight = 0;
102
+ let positiveStackHeight = 0;
103
+ let negativeStackHeight = 0;
103
104
  const stackItems = [];
104
105
  const sortedData = sortKey
105
106
  ? sort(yValues, (a, b) => comparator(get(a, sortKey), get(b, sortKey)))
@@ -117,6 +118,7 @@ export const prepareBarXData = async (args) => {
117
118
  if (xAxis.type === 'category') {
118
119
  const xBandScale = xScale;
119
120
  const xBandScaleDomain = xBandScale.domain();
121
+ // eslint-disable-next-line max-depth
120
122
  if (xBandScaleDomain.indexOf(xValue) === -1) {
121
123
  continue;
122
124
  }
@@ -141,7 +143,9 @@ export const prepareBarXData = async (args) => {
141
143
  }
142
144
  const barData = {
143
145
  x,
144
- y: yAxisTop + (yDataValue > 0 ? y - stackHeight : base),
146
+ y: yDataValue > 0
147
+ ? yAxisTop + y - positiveStackHeight
148
+ : yAxisTop + base + negativeStackHeight,
145
149
  width: rectWidth,
146
150
  height: shapeHeight,
147
151
  opacity: get(yValue.data, 'opacity', null),
@@ -151,11 +155,16 @@ export const prepareBarXData = async (args) => {
151
155
  isLastStackItem,
152
156
  };
153
157
  stackItems.push(barData);
154
- stackHeight += height;
158
+ if (yDataValue > 0) {
159
+ positiveStackHeight += height;
160
+ }
161
+ else {
162
+ negativeStackHeight += height;
163
+ }
155
164
  }
156
165
  if (series.some((s) => s.stacking === 'percent')) {
157
166
  let acc = 0;
158
- const ratio = plotHeight / (stackHeight - stackItems.length);
167
+ const ratio = plotHeight / (positiveStackHeight - stackItems.length);
159
168
  stackItems.forEach((item) => {
160
169
  item.height = item.height * ratio;
161
170
  item.y = plotHeight - item.height - acc;
@@ -168,7 +177,7 @@ export const prepareBarXData = async (args) => {
168
177
  }
169
178
  for (let i = 0; i < result.length; i++) {
170
179
  const barData = result[i];
171
- if (barData.series.dataLabels.enabled) {
180
+ if (barData.series.dataLabels.enabled && !isRangeSlider) {
172
181
  const label = await getLabelData(barData);
173
182
  if (barData.series.dataLabels.html && label) {
174
183
  barData.htmlElements.push({
@@ -58,6 +58,7 @@ export const useShapes = (args) => {
58
58
  yScale,
59
59
  boundsHeight,
60
60
  split,
61
+ isRangeSlider,
61
62
  });
62
63
  shapes.push(React.createElement(BarXSeriesShapes, { key: SERIES_TYPE.BarX, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
63
64
  shapesData.push(...preparedData);
@@ -124,6 +125,7 @@ export const useShapes = (args) => {
124
125
  boundsHeight,
125
126
  split,
126
127
  isOutsideBounds,
128
+ isRangeSlider,
127
129
  });
128
130
  shapes.push(React.createElement(AreaSeriesShapes, { key: SERIES_TYPE.Area, dispatcher: dispatcher, seriesOptions: seriesOptions, preparedData: preparedData, htmlLayout: htmlLayout, clipPathId: clipPathId }));
129
131
  shapesData.push(...preparedData);
@@ -34,7 +34,7 @@ export const LineSeriesShapes = (args) => {
34
34
  .attr('fill', 'none')
35
35
  .attr('stroke', (d) => d.color)
36
36
  .attr('stroke-width', (d) => d.lineWidth)
37
- .attr('stroke-linejoin', (d) => d.linecap)
37
+ .attr('stroke-linejoin', (d) => d.linejoin)
38
38
  .attr('stroke-linecap', (d) => d.linecap)
39
39
  .attr('stroke-dasharray', (d) => getLineDashArray(d.dashStyle, d.lineWidth))
40
40
  .attr('opacity', (d) => d.opacity)
@@ -44,7 +44,7 @@ export const prepareLineData = async (args) => {
44
44
  });
45
45
  const htmlElements = [];
46
46
  const labels = [];
47
- if (s.dataLabels.enabled) {
47
+ if (s.dataLabels.enabled && !isRangeSlider) {
48
48
  if (s.dataLabels.html) {
49
49
  const list = await Promise.all(points.reduce((result, p) => {
50
50
  if (p.y === null) {
@@ -108,6 +108,7 @@ export const prepareLineData = async (args) => {
108
108
  lineWidth: (_b = (isRangeSlider ? s.rangeSlider.lineWidth : undefined)) !== null && _b !== void 0 ? _b : s.lineWidth,
109
109
  dashStyle: s.dashStyle,
110
110
  linecap: s.linecap,
111
+ linejoin: s.linejoin,
111
112
  opacity: (_c = (isRangeSlider ? s.rangeSlider.opacity : undefined)) !== null && _c !== void 0 ? _c : s.opacity,
112
113
  };
113
114
  acc.push(result);
@@ -1,4 +1,4 @@
1
- import type { DashStyle, LineCap } from '../../../constants';
1
+ import type { DashStyle, LineCap, LineJoin } from '../../../constants';
2
2
  import type { HtmlItem, LabelData, LineSeriesData, LineSeriesLineBaseStyle } from '../../../types';
3
3
  import type { PreparedLineSeries } from '../../useSeries/types';
4
4
  export type PointData = {
@@ -30,4 +30,5 @@ export type PreparedLineData = {
30
30
  color: string;
31
31
  dashStyle: DashStyle;
32
32
  linecap: LineCap;
33
+ linejoin: LineJoin;
33
34
  } & Required<LineSeriesLineBaseStyle>;
@@ -194,6 +194,9 @@ export interface AxisPlot {
194
194
  * @default 5
195
195
  */
196
196
  padding?: number;
197
+ /** Can be used for the UI automated test.
198
+ * It is assigned as a data-qa attribute to an element. */
199
+ qa?: string;
197
200
  };
198
201
  }
199
202
  export interface AxisPlotLine extends AxisPlot {
@@ -1,4 +1,4 @@
1
- import type { DashStyle, LineCap, SERIES_TYPE } from '../../constants';
1
+ import type { DashStyle, LineCap, LineJoin, SERIES_TYPE } from '../../constants';
2
2
  import type { MeaningfulAny } from '../misc';
3
3
  import type { BaseSeries, BaseSeriesData, BaseSeriesLegend } from './base';
4
4
  import type { RectLegendSymbolOptions } from './legend';
@@ -53,6 +53,8 @@ export interface LineSeries<T = MeaningfulAny> extends BaseSeries, LineSeriesLin
53
53
  dashStyle?: DashStyle;
54
54
  /** Option for line cap style */
55
55
  linecap?: `${LineCap}`;
56
+ /** Defines the shape to be used at the corners of the line */
57
+ linejoin?: `${LineJoin}`;
56
58
  /** Individual series legend options. Has higher priority than legend options in widget data */
57
59
  legend?: BaseSeriesLegend & {
58
60
  symbol?: RectLegendSymbolOptions;
@@ -1,5 +1,5 @@
1
1
  import type React from 'react';
2
- import type { DashStyle, LineCap } from '../../constants';
2
+ import type { DashStyle, LineCap, LineJoin } from '../../constants';
3
3
  import type { MeaningfulAny } from '../misc';
4
4
  import type { AreaSeries, AreaSeriesData } from './area';
5
5
  import type { BarXSeries, BarXSeriesData } from './bar-x';
@@ -211,6 +211,11 @@ export interface ChartSeriesOptions {
211
211
  * @default 'round' when dashStyle is not 'solid', 'none' when dashStyle is not 'solid'
212
212
  * */
213
213
  linecap?: `${LineCap}`;
214
+ /** Defines the shape to be used at the corners of the line
215
+ *
216
+ * @default 'round' when dashStyle is not 'solid', 'unset' when dashStyle is not 'solid'
217
+ * */
218
+ linejoin?: `${LineJoin}`;
214
219
  };
215
220
  area?: {
216
221
  /** Pixel width of the graph line.
@@ -46,8 +46,8 @@ export declare function isSeriesWithCategoryValues(series: UnknownSeries): serie
46
46
  export declare const getDomainDataXBySeries: (series: UnknownSeries[]) => ({} | undefined)[];
47
47
  export declare function getDefaultMaxXAxisValue(series: UnknownSeries[]): 0 | undefined;
48
48
  export declare function getDefaultMinXAxisValue(series: UnknownSeries[]): number | undefined;
49
- export declare function getDefaultMinYAxisValue(series?: UnknownSeries[]): number | undefined;
50
49
  export declare const getDomainDataYBySeries: (series: UnknownSeries[]) => unknown[];
50
+ export declare function getDefaultMinYAxisValue(series?: UnknownSeries[]): number | undefined;
51
51
  export declare const getSeriesNames: (series: ChartSeries[]) => string[];
52
52
  export declare const getOnlyVisibleSeries: <T extends {
53
53
  visible: boolean;
@@ -46,7 +46,8 @@ function getDomainDataForStackedSeries(seriesList, keyAttr = 'x', valueAttr = 'y
46
46
  const acc = [];
47
47
  const stackedSeries = group(seriesList, getSeriesStackId);
48
48
  Array.from(stackedSeries).forEach(([_stackId, seriesStack]) => {
49
- const values = {};
49
+ const positiveValues = {};
50
+ const negativeValues = {};
50
51
  seriesStack.forEach((singleSeries) => {
51
52
  const data = new Map();
52
53
  singleSeries.data.forEach((point) => {
@@ -65,10 +66,15 @@ function getDomainDataForStackedSeries(seriesList, keyAttr = 'x', valueAttr = 'y
65
66
  data.set(key, value);
66
67
  });
67
68
  Array.from(data).forEach(([key, value]) => {
68
- values[key] = (values[key] || 0) + value;
69
+ if (value >= 0) {
70
+ positiveValues[key] = (positiveValues[key] || 0) + value;
71
+ }
72
+ if (value < 0) {
73
+ negativeValues[key] = (negativeValues[key] || 0) + value;
74
+ }
69
75
  });
70
76
  });
71
- acc.push(...Object.values(values));
77
+ acc.push(...Object.values(negativeValues), ...Object.values(positiveValues));
72
78
  });
73
79
  return acc;
74
80
  }
@@ -107,22 +113,6 @@ export function getDefaultMinXAxisValue(series) {
107
113
  }
108
114
  return undefined;
109
115
  }
110
- export function getDefaultMinYAxisValue(series) {
111
- if (series === null || series === void 0 ? void 0 : series.some((s) => CHART_SERIES_WITH_VOLUME_ON_Y_AXIS.includes(s.type))) {
112
- if (series.some((s) => s.type === SERIES_TYPE.Waterfall)) {
113
- const seriesData = series.map((s) => s.data).flat();
114
- const minSubTotal = seriesData.reduce((res, d) => Math.min(res, getWaterfallPointSubtotal(d, seriesData) || 0), 0);
115
- return Math.min(0, minSubTotal);
116
- }
117
- return series.reduce((minValue, s) => {
118
- // https://github.com/gravity-ui/charts/issues/160
119
- // @ts-expect-error
120
- const minYValue = s.data.reduce((res, d) => Math.min(res, get(d, 'y', 0)), 0);
121
- return Math.min(minValue, minYValue);
122
- }, 0);
123
- }
124
- return undefined;
125
- }
126
116
  export const getDomainDataYBySeries = (series) => {
127
117
  const groupedSeries = group(series, (item) => item.type);
128
118
  const items = Array.from(groupedSeries).reduce((acc, [type, seriesList]) => {
@@ -151,6 +141,20 @@ export const getDomainDataYBySeries = (series) => {
151
141
  }, []);
152
142
  return Array.from(new Set(items));
153
143
  };
144
+ export function getDefaultMinYAxisValue(series) {
145
+ if (series === null || series === void 0 ? void 0 : series.some((s) => CHART_SERIES_WITH_VOLUME_ON_Y_AXIS.includes(s.type))) {
146
+ if (series.some((s) => s.type === SERIES_TYPE.Waterfall)) {
147
+ const seriesData = series.map((s) => s.data).flat();
148
+ const minSubTotal = seriesData.reduce((res, d) => Math.min(res, getWaterfallPointSubtotal(d, seriesData) || 0), 0);
149
+ return Math.min(0, minSubTotal);
150
+ }
151
+ const domainData = getDomainDataYBySeries(series);
152
+ return domainData.reduce((minValue, d) => {
153
+ return Math.min(minValue, d);
154
+ }, 0);
155
+ }
156
+ return undefined;
157
+ }
154
158
  // Uses to get all series names array (except `pie` charts)
155
159
  export const getSeriesNames = (series) => {
156
160
  return series.reduce((acc, s) => {