@gooddata/sdk-ui-charts 11.40.0-alpha.5 → 11.40.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.
@@ -1,3 +1,3 @@
1
1
  import { type DataViewFacade, type IHeaderPredicate, type VisType } from "@gooddata/sdk-ui";
2
2
  import { type IUnwrappedAttributeHeadersWithItems } from "../../typings/mess.js";
3
- export declare function getDrillableSeries(dv: DataViewFacade, series: any[], drillableItems: IHeaderPredicate[], viewByAttributes: (IUnwrappedAttributeHeadersWithItems | undefined | null)[], stackByAttribute: IUnwrappedAttributeHeadersWithItems | undefined | null, type: VisType | undefined, forceDrillIntersection?: boolean): any;
3
+ export declare function getDrillableSeries(dv: DataViewFacade, series: any[], drillableItems: IHeaderPredicate[], viewByAttributes: (IUnwrappedAttributeHeadersWithItems | undefined | null)[], stackByAttribute: IUnwrappedAttributeHeadersWithItems | undefined | null, type: VisType | undefined, forceDrillIntersection?: boolean, isSingleSeriesBubble?: boolean): any;
@@ -40,7 +40,7 @@ function getStackBy(stackByAttribute, stackByIndex) {
40
40
  stackByAttributeDescriptor,
41
41
  };
42
42
  }
43
- export function getDrillableSeries(dv, series, drillableItems, viewByAttributes, stackByAttribute, type, forceDrillIntersection = false) {
43
+ export function getDrillableSeries(dv, series, drillableItems, viewByAttributes, stackByAttribute, type, forceDrillIntersection = false, isSingleSeriesBubble = false) {
44
44
  const [viewByChildAttribute, viewByParentAttribute] = viewByAttributes;
45
45
  const isMultiMeasureWithOnlyMeasures = isOneOfTypes(type, multiMeasuresAlternatingTypes) && !viewByChildAttribute;
46
46
  const measureGroup = findMeasureGroupInDimensions(dv.meta().dimensions());
@@ -77,10 +77,10 @@ export function getDrillableSeries(dv, series, drillableItems, viewByAttributes,
77
77
  measureHeaders = [measureGroup.items[measureIndex]];
78
78
  }
79
79
  const viewByIndex = isHeatmap(type) || isStackedTreemap ? pointData.x : pointIndex;
80
- // Scatter renders a single series, so the segment is resolved per point, not by seriesIndex (always 0).
80
+ // Scatter and single-series bubble render one series, so the segment is resolved per point, not by seriesIndex (always 0).
81
81
  const stackByIndex = isHeatmap(type) || isStackedTreemap
82
82
  ? pointData.y
83
- : isScatterPlot(type)
83
+ : isScatterPlot(type) || isSingleSeriesBubble
84
84
  ? pointIndex
85
85
  : seriesIndex;
86
86
  const { stackByHeader, stackByAttributeDescriptor } = getStackBy(stackByAttribute, stackByIndex);
@@ -1,6 +1,6 @@
1
1
  // (C) 2007-2026 GoodData Corporation
2
2
  import { VisualizationTypes } from "@gooddata/sdk-ui";
3
- import { DEFAULT_CATEGORIES_LIMIT, DEFAULT_DATA_POINTS_LIMIT, DEFAULT_SERIES_LIMIT, HEATMAP_DATA_POINTS_LIMIT, PIE_CHART_LIMIT, SANKEY_CHART_DATA_POINT_LIMIT, SANKEY_CHART_NODE_LIMIT, SOFT_DEFAULT_CATEGORIES_LIMIT, SOFT_DEFAULT_DATA_POINTS_LIMIT, SOFT_DEFAULT_SERIES_LIMIT, SOFT_PIE_CHART_LIMIT, SOFT_SANKEY_CHART_DATA_POINT_LIMIT, SOFT_SANKEY_CHART_NODE_LIMIT, SOFT_STACKED_COLUMN_BAR_SERIES_LIMIT, SOFT_WATERFALL_CHART_DATA_POINT_LIMIT, STACKED_COLUMN_BAR_SERIES_LIMIT, WATERFALL_CHART_DATA_POINT_LIMIT, } from "../../constants/limits.js";
3
+ import { COLUMN_BAR_TOTAL_DATA_POINTS_LIMIT, DEFAULT_CATEGORIES_LIMIT, DEFAULT_DATA_POINTS_LIMIT, DEFAULT_SERIES_LIMIT, HEATMAP_DATA_POINTS_LIMIT, PIE_CHART_LIMIT, SANKEY_CHART_DATA_POINT_LIMIT, SANKEY_CHART_NODE_LIMIT, SOFT_COLUMN_BAR_TOTAL_DATA_POINTS_LIMIT, SOFT_DEFAULT_CATEGORIES_LIMIT, SOFT_DEFAULT_DATA_POINTS_LIMIT, SOFT_DEFAULT_SERIES_LIMIT, SOFT_PIE_CHART_LIMIT, SOFT_SANKEY_CHART_DATA_POINT_LIMIT, SOFT_SANKEY_CHART_NODE_LIMIT, SOFT_STACKED_COLUMN_BAR_SERIES_LIMIT, SOFT_WATERFALL_CHART_DATA_POINT_LIMIT, STACKED_COLUMN_BAR_SERIES_LIMIT, WATERFALL_CHART_DATA_POINT_LIMIT, } from "../../constants/limits.js";
4
4
  import { isDataOfReasonableSize } from "../_chartCreators/highChartsCreators.js";
5
5
  import { isOneOfTypes, isTreemap } from "../_util/common.js";
6
6
  import { unsupportedNegativeValuesTypes } from "./chartCapabilities.js";
@@ -132,6 +132,20 @@ function getTreemapDataForValidation(data) {
132
132
  })),
133
133
  };
134
134
  }
135
+ function isColumnOrBar(type) {
136
+ return type === VisualizationTypes.COLUMN || type === VisualizationTypes.BAR;
137
+ }
138
+ function getTotalDataPoints(data) {
139
+ const series = data?.series;
140
+ if (!Array.isArray(series)) {
141
+ return 0;
142
+ }
143
+ return series.reduce((sum, serie) => sum + (serie.data?.length ?? 0), 0);
144
+ }
145
+ // Total rendered-points cap for column/bar, kept out of IChartLimits and checked alongside isDataOfReasonableSize.
146
+ function exceedsColumnBarTotalDataPoints(type, data, limit) {
147
+ return isColumnOrBar(type) && getTotalDataPoints(data) > limit;
148
+ }
135
149
  export function validateData(limits, chartOptions) {
136
150
  const type = chartOptions.type;
137
151
  const { isViewByTwoAttributes } = chartOptions;
@@ -142,8 +156,9 @@ export function validateData(limits, chartOptions) {
142
156
  if (isTreemap(type)) {
143
157
  dataToValidate = getTreemapDataForValidation(chartOptions.data);
144
158
  }
159
+ const totalDataTooLarge = !limits && exceedsColumnBarTotalDataPoints(type, dataToValidate, COLUMN_BAR_TOTAL_DATA_POINTS_LIMIT);
145
160
  return {
146
- dataTooLarge: !isDataOfReasonableSize(dataToValidate, finalLimits, isViewByTwoAttributes),
161
+ dataTooLarge: totalDataTooLarge || !isDataOfReasonableSize(dataToValidate, finalLimits, isViewByTwoAttributes),
147
162
  hasNegativeValue: cannotShowNegativeValues(type) && isNegativeValueIncluded(chartOptions.data?.series),
148
163
  };
149
164
  }
@@ -153,7 +168,8 @@ export function getIsFilteringRecommended(chartOptions) {
153
168
  const dataToValidate = isTreemap(type)
154
169
  ? getTreemapDataForValidation(chartOptions.data)
155
170
  : chartOptions.data;
156
- return !isDataOfReasonableSize(dataToValidate, limits, isViewByTwoAttributes);
171
+ return (exceedsColumnBarTotalDataPoints(type, dataToValidate, SOFT_COLUMN_BAR_TOTAL_DATA_POINTS_LIMIT) ||
172
+ !isDataOfReasonableSize(dataToValidate, limits, isViewByTwoAttributes));
157
173
  }
158
174
  /**
159
175
  * Creates a message for onDataTooLarge error, which contains the limits which were exceeded.
@@ -161,6 +177,7 @@ export function getIsFilteringRecommended(chartOptions) {
161
177
  export function getDataTooLargeErrorMessage(limits, chartOptions) {
162
178
  const { type, isViewByTwoAttributes } = chartOptions;
163
179
  const { series: seriesLimit, categories: categoriesLimit, nodes: nodesLimit, dataPoints: dataPointsLimit, } = limits || getChartLimits(type, chartOptions);
180
+ const totalDataPointsLimit = !limits && isColumnOrBar(type) ? COLUMN_BAR_TOTAL_DATA_POINTS_LIMIT : undefined;
164
181
  const dataToValidate = isTreemap(type)
165
182
  ? getTreemapDataForValidation(chartOptions.data)
166
183
  : chartOptions.data;
@@ -194,6 +211,9 @@ export function getDataTooLargeErrorMessage(limits, chartOptions) {
194
211
  }));
195
212
  result.push(limitLog("DataPointsMax", dataPointsLimit, dataPointsMax));
196
213
  }
214
+ if (totalDataPointsLimit !== undefined) {
215
+ result.push(limitLog("TotalDataPoints", totalDataPointsLimit, getTotalDataPoints(dataToValidate)));
216
+ }
197
217
  return result
198
218
  .filter((el) => el !== "")
199
219
  .join(" ")
@@ -221,7 +221,7 @@ export function getChartOptions(dataView, chartConfig, drillableItems, emptyHead
221
221
  const measureGroup = findMeasureGroupInDimensions(dimensions);
222
222
  const yAxes = getYAxes(dv, config, measureGroup, stackByAttribute);
223
223
  const seriesWithoutDrillability = getSeries(dv, measureGroup, viewByAttribute, viewByParentAttribute, stackByAttribute, type, colorStrategy, emptyHeaderTitle, theme, config.chartFill, config);
224
- const drillableSeries = getDrillableSeries(dv, seriesWithoutDrillability, drillableItems, [viewByAttribute, viewByParentAttribute], stackByAttribute, type, config.customTooltip?.enabled ?? false);
224
+ const drillableSeries = getDrillableSeries(dv, seriesWithoutDrillability, drillableItems, [viewByAttribute, viewByParentAttribute], stackByAttribute, type, config.customTooltip?.enabled ?? false, isBubbleChart(type) && (chartConfig?.enableSingleBubbleSeries ?? false));
225
225
  let initialSeries = assignYAxes(drillableSeries, yAxes);
226
226
  let categories = viewByParentAttribute
227
227
  ? getCategoriesForTwoAttributes(viewByAttribute, viewByParentAttribute, emptyHeaderTitle)
@@ -7,6 +7,7 @@ export declare const SANKEY_CHART_NODE_LIMIT = 10000;
7
7
  export declare const SANKEY_CHART_DATA_POINT_LIMIT = 10000;
8
8
  export declare const WATERFALL_CHART_DATA_POINT_LIMIT = 10000;
9
9
  export declare const STACKED_COLUMN_BAR_SERIES_LIMIT = 1000;
10
+ export declare const COLUMN_BAR_TOTAL_DATA_POINTS_LIMIT = 6000;
10
11
  /**
11
12
  * Soft limits are used for checking whether the chart should be recommended to be filtered out
12
13
  * due to too many data points.
@@ -22,3 +23,4 @@ export declare const SOFT_SANKEY_CHART_NODE_LIMIT = 50;
22
23
  export declare const SOFT_SANKEY_CHART_DATA_POINT_LIMIT = 500;
23
24
  export declare const SOFT_WATERFALL_CHART_DATA_POINT_LIMIT = 400;
24
25
  export declare const SOFT_STACKED_COLUMN_BAR_SERIES_LIMIT = 50;
26
+ export declare const SOFT_COLUMN_BAR_TOTAL_DATA_POINTS_LIMIT = 2000;
@@ -10,6 +10,9 @@ export const WATERFALL_CHART_DATA_POINT_LIMIT = 10000;
10
10
  // Stacked column/bar charts freeze the main thread well before the generic 10k series cap
11
11
  // (Highcharts can't draw more than ~1500 stacks), so they need a tighter limit than other chart types.
12
12
  export const STACKED_COLUMN_BAR_SERIES_LIMIT = 1000;
13
+ // Column/bar charts render one SVG element per data point, and the point count is categories x series.
14
+ // That total can hang the browser even when neither dimension alone hits its cap, so cap the total too.
15
+ export const COLUMN_BAR_TOTAL_DATA_POINTS_LIMIT = 6000;
13
16
  /**
14
17
  * Soft limits are used for checking whether the chart should be recommended to be filtered out
15
18
  * due to too many data points.
@@ -25,3 +28,4 @@ export const SOFT_SANKEY_CHART_NODE_LIMIT = 50;
25
28
  export const SOFT_SANKEY_CHART_DATA_POINT_LIMIT = 500;
26
29
  export const SOFT_WATERFALL_CHART_DATA_POINT_LIMIT = 400;
27
30
  export const SOFT_STACKED_COLUMN_BAR_SERIES_LIMIT = 50;
31
+ export const SOFT_COLUMN_BAR_TOTAL_DATA_POINTS_LIMIT = 2000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/sdk-ui-charts",
3
- "version": "11.40.0-alpha.5",
3
+ "version": "11.40.0",
4
4
  "description": "GoodData.UI SDK - Charts",
5
5
  "license": "LicenseRef-LICENSE",
6
6
  "author": "GoodData Corporation",
@@ -69,13 +69,13 @@
69
69
  "ts-invariant": "0.10.3",
70
70
  "tslib": "2.8.1",
71
71
  "uuid": "11.1.0",
72
- "@gooddata/sdk-backend-spi": "11.40.0-alpha.5",
73
- "@gooddata/sdk-model": "11.40.0-alpha.5",
74
- "@gooddata/sdk-ui": "11.40.0-alpha.5",
75
- "@gooddata/sdk-ui-theme-provider": "11.40.0-alpha.5",
76
- "@gooddata/sdk-ui-kit": "11.40.0-alpha.5",
77
- "@gooddata/sdk-ui-vis-commons": "11.40.0-alpha.5",
78
- "@gooddata/util": "11.40.0-alpha.5"
72
+ "@gooddata/sdk-backend-spi": "11.40.0",
73
+ "@gooddata/sdk-ui": "11.40.0",
74
+ "@gooddata/sdk-model": "11.40.0",
75
+ "@gooddata/sdk-ui-theme-provider": "11.40.0",
76
+ "@gooddata/sdk-ui-kit": "11.40.0",
77
+ "@gooddata/util": "11.40.0",
78
+ "@gooddata/sdk-ui-vis-commons": "11.40.0"
79
79
  },
80
80
  "devDependencies": {
81
81
  "@microsoft/api-documenter": "^7.17.0",
@@ -93,7 +93,7 @@
93
93
  "@typescript-eslint/eslint-plugin": "8.58.0",
94
94
  "@typescript-eslint/parser": "8.58.0",
95
95
  "@typescript/native-preview": "7.0.0-dev.20260202.1",
96
- "@vitest/eslint-plugin": "1.6.6",
96
+ "@vitest/eslint-plugin": "1.6.19",
97
97
  "dependency-cruiser": "17.3.10",
98
98
  "eslint": "^9.39.2",
99
99
  "eslint-import-resolver-typescript": "4.4.4",
@@ -107,7 +107,7 @@
107
107
  "eslint-plugin-react-hooks": "5.2.0",
108
108
  "eslint-plugin-sonarjs": "3.0.6",
109
109
  "full-icu": "^1.3.0",
110
- "happy-dom": "18.0.1",
110
+ "happy-dom": "20.9.0",
111
111
  "npm-run-all": "^4.1.5",
112
112
  "oxfmt": "0.52.0",
113
113
  "oxlint": "1.51.0",
@@ -120,11 +120,11 @@
120
120
  "typescript": "5.9.3",
121
121
  "vitest": "4.1.8",
122
122
  "vitest-dom": "0.1.1",
123
- "@gooddata/eslint-config": "11.40.0-alpha.5",
124
- "@gooddata/oxlint-config": "11.40.0-alpha.5",
125
- "@gooddata/reference-workspace": "11.40.0-alpha.5",
126
- "@gooddata/sdk-backend-mockingbird": "11.40.0-alpha.5",
127
- "@gooddata/stylelint-config": "11.40.0-alpha.5"
123
+ "@gooddata/eslint-config": "11.40.0",
124
+ "@gooddata/oxlint-config": "11.40.0",
125
+ "@gooddata/reference-workspace": "11.40.0",
126
+ "@gooddata/sdk-backend-mockingbird": "11.40.0",
127
+ "@gooddata/stylelint-config": "11.40.0"
128
128
  },
129
129
  "peerDependencies": {
130
130
  "react": "^18.0.0 || ^19.0.0",
@@ -142,8 +142,8 @@
142
142
  "dep-cruiser": "depcruise --validate .dependency-cruiser.js --output-type err-long src/",
143
143
  "format-check": "oxfmt --check .",
144
144
  "format-write": "oxfmt .",
145
- "lint": "oxlint . --type-aware --quiet && eslint .",
146
- "lint-fix": "oxlint . --type-aware --quiet --fix && eslint . --fix",
145
+ "lint": "oxlint . --quiet && eslint .",
146
+ "lint-fix": "oxlint . --quiet --fix && eslint . --fix",
147
147
  "scss": "sass --load-path=node_modules styles/scss:styles/css",
148
148
  "stylelint": "stylelint '**/*.scss'",
149
149
  "stylelint-fix": "stylelint '**/*.scss' --fix",