@react-magma/charts 13.1.1-next.0 → 13.2.0-next.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 (35) hide show
  1. package/dist/charts.js +224 -115
  2. package/dist/charts.js.map +1 -1
  3. package/dist/charts.modern.module.js +224 -115
  4. package/dist/charts.modern.module.js.map +1 -1
  5. package/dist/charts.umd.js +574 -293
  6. package/dist/charts.umd.js.map +1 -1
  7. package/dist/components/CarbonChart/CarbonChart.d.ts +5 -0
  8. package/dist/components/CarbonChart/CarbonChartBar.stories.d.ts +27 -9
  9. package/package.json +3 -3
  10. package/src/components/CarbonChart/CarbonChart.test.js +185 -0
  11. package/src/components/CarbonChart/CarbonChart.tsx +262 -120
  12. package/src/components/CarbonChart/CarbonChartArea.stories.tsx +1 -1
  13. package/src/components/CarbonChart/CarbonChartAreaStacked.stories.tsx +1 -1
  14. package/src/components/CarbonChart/CarbonChartBar.stories.tsx +9 -2
  15. package/src/components/CarbonChart/CarbonChartBarFloating.stories.tsx +1 -1
  16. package/src/components/CarbonChart/CarbonChartBarGrouped.stories.tsx +1 -1
  17. package/src/components/CarbonChart/CarbonChartBarStacked.stories.tsx +1 -1
  18. package/src/components/CarbonChart/CarbonChartBoxplot.stories.tsx +1 -1
  19. package/src/components/CarbonChart/CarbonChartBubble.stories.tsx +1 -1
  20. package/src/components/CarbonChart/CarbonChartBullet.stories.tsx +1 -1
  21. package/src/components/CarbonChart/CarbonChartCombo.stories.tsx +1 -1
  22. package/src/components/CarbonChart/CarbonChartDonut.stories.tsx +3 -1
  23. package/src/components/CarbonChart/CarbonChartGauge.stories.tsx +1 -1
  24. package/src/components/CarbonChart/CarbonChartHistogram.stories.tsx +1 -1
  25. package/src/components/CarbonChart/CarbonChartLine.stories.tsx +1 -1
  26. package/src/components/CarbonChart/CarbonChartLollipop.stories.tsx +1 -1
  27. package/src/components/CarbonChart/CarbonChartMeter.stories.tsx +1 -1
  28. package/src/components/CarbonChart/CarbonChartPie.stories.tsx +1 -1
  29. package/src/components/CarbonChart/CarbonChartRadar.stories.tsx +1 -1
  30. package/src/components/CarbonChart/CarbonChartScatter.stories.tsx +1 -1
  31. package/src/components/CarbonChart/CarbonChartSparkline.stories.tsx +1 -1
  32. package/src/components/CarbonChart/CarbonChartStep.stories.tsx +1 -1
  33. package/src/components/ChartTable/ChartMoreOptionsButton.tsx +1 -0
  34. package/dist/components/ChartTable/ChartTable.test.d.ts +0 -1
  35. /package/src/components/ChartTable/{ChartTable.test.tsx → ChartTable.test.js} +0 -0
@@ -103,6 +103,11 @@ export interface ChartToolbarConfig {
103
103
  * @default 2
104
104
  */
105
105
  tableHeaderLevel?: 1 | 2 | 3 | 4 | 5 | 6;
106
+ /**
107
+ * Heading level for the chart title.
108
+ * @default 2
109
+ */
110
+ titleLevel?: 1 | 2 | 3 | 4 | 5 | 6;
106
111
  }
107
112
 
108
113
  export interface CarbonChartProps extends React.HTMLAttributes<HTMLDivElement> {
@@ -490,6 +495,9 @@ const CarbonChartWrapper = styled.div<{
490
495
  : props.theme.colors.focus} !important;
491
496
  outline-offset: 0;
492
497
  }
498
+ circle.dot:focus {
499
+ outline: none !important;
500
+ }
493
501
  .cds--overflow-menu-options__btn:focus,
494
502
  .cds--overflow-menu:focus,
495
503
  .cds--overflow-menu__trigger:focus,
@@ -594,6 +602,17 @@ const CarbonChartWrapper = styled.div<{
594
602
  overflow: visible;
595
603
  }
596
604
 
605
+ circle.dot:focus {
606
+ outline: none;
607
+ stroke: ${props =>
608
+ props.isInverse
609
+ ? props.theme.colors.focusInverse
610
+ : props.theme.colors.focus} !important;
611
+ stroke-width: 6px !important;
612
+ stroke-opacity: 1 !important;
613
+ paint-order: stroke fill;
614
+ }
615
+
597
616
  .cds--cc--chart-wrapper text {
598
617
  font-size: ${props => props.theme.typeScale.size02.fontSize};
599
618
  }
@@ -637,6 +656,11 @@ interface ColorsObject {
637
656
  [key: string]: string;
638
657
  }
639
658
 
659
+ interface ExtendedChartOptions extends ChartOptions {
660
+ title?: string;
661
+ colors?: string[];
662
+ }
663
+
640
664
  const ToolbarWrapper = styled.div<{
641
665
  isFullscreen?: boolean;
642
666
  isInverse?: boolean;
@@ -838,86 +862,124 @@ function downloadImage(
838
862
 
839
863
  const svgRect = svg.getBoundingClientRect();
840
864
  const legendItems = readLegendItems(wrapper);
841
- const scale = 2;
842
865
 
843
- // Measure legend height
844
- const tempCanvas = document.createElement('canvas');
845
- const tempCtx = tempCanvas.getContext('2d');
846
- const fontSize = 13 * scale;
847
- const swatchSize = 12 * scale;
848
- const gap = 8 * scale;
849
- const itemGap = 16 * scale;
850
- const paddingX = 16 * scale;
851
- const canvasWidth = svgRect.width * scale;
852
-
853
- let legendHeight = 0;
854
- if (legendItems.length > 0 && tempCtx) {
855
- tempCtx.font = `${fontSize}px sans-serif`;
856
- let x = paddingX;
857
- let rows = 1;
858
- for (const item of legendItems) {
859
- const textWidth = tempCtx.measureText(item.label).width;
860
- const itemWidth = swatchSize + gap + textWidth + itemGap;
861
- if (x + itemWidth > canvasWidth - paddingX && x > paddingX) {
862
- x = paddingX;
863
- rows++;
866
+ const doWork = () => {
867
+ const scale = 2;
868
+
869
+ // Measure legend height
870
+ const tempCanvas = document.createElement('canvas');
871
+ const tempCtx = tempCanvas.getContext('2d');
872
+ const fontSize = 13 * scale;
873
+ const swatchSize = 12 * scale;
874
+ const gap = 8 * scale;
875
+ const itemGap = 16 * scale;
876
+ const paddingX = 16 * scale;
877
+ const canvasWidth = svgRect.width * scale;
878
+
879
+ let legendHeight = 0;
880
+ if (legendItems.length > 0 && tempCtx) {
881
+ tempCtx.font = `${fontSize}px sans-serif`;
882
+ let x = paddingX;
883
+ let rows = 1;
884
+ for (const item of legendItems) {
885
+ const textWidth = tempCtx.measureText(item.label).width;
886
+ const itemWidth = swatchSize + gap + textWidth + itemGap;
887
+ if (x + itemWidth > canvasWidth - paddingX && x > paddingX) {
888
+ x = paddingX;
889
+ rows++;
890
+ }
891
+ x += itemWidth;
864
892
  }
865
- x += itemWidth;
893
+ legendHeight = rows * (fontSize + gap) + gap * 2;
866
894
  }
867
- legendHeight = rows * (fontSize + gap) + gap * 2;
868
- }
869
895
 
870
- const width = svgRect.width * scale;
871
- const height = svgRect.height * scale + legendHeight;
896
+ const width = svgRect.width * scale;
897
+ const height = svgRect.height * scale + legendHeight;
872
898
 
873
- const clone = svg.cloneNode(true) as SVGSVGElement;
874
- clone.setAttribute('width', String(svgRect.width));
875
- clone.setAttribute('height', String(svgRect.height));
876
- clone.setAttribute('viewBox', `0 0 ${svgRect.width} ${svgRect.height}`);
877
- clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
899
+ const clone = svg.cloneNode(true) as SVGSVGElement;
900
+ clone.setAttribute('width', String(svgRect.width));
901
+ clone.setAttribute('height', String(svgRect.height));
902
+ clone.setAttribute('viewBox', `0 0 ${svgRect.width} ${svgRect.height}`);
903
+ clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
878
904
 
879
- inlineStyles(svg, clone);
905
+ inlineStyles(svg, clone);
880
906
 
881
- const serializer = new XMLSerializer();
882
- const svgString = serializer.serializeToString(clone);
883
- const svgBlob = new Blob([svgString], {
884
- type: 'image/svg+xml;charset=utf-8',
885
- });
886
- const url = URL.createObjectURL(svgBlob);
887
-
888
- const mimeType = format === 'jpg' ? 'image/jpeg' : 'image/png';
889
- const ext = format === 'jpg' ? 'jpg' : 'png';
890
-
891
- const img = new Image();
892
- img.onload = () => {
893
- const canvas = document.createElement('canvas');
894
- canvas.width = width;
895
- canvas.height = height;
896
- const ctx = canvas.getContext('2d');
897
- if (!ctx) return;
898
-
899
- ctx.fillStyle = '#ffffff';
900
- ctx.fillRect(0, 0, canvas.width, canvas.height);
901
- ctx.drawImage(img, 0, 0, svgRect.width * scale, svgRect.height * scale);
902
- URL.revokeObjectURL(url);
903
-
904
- if (legendItems.length > 0) {
905
- drawLegend(ctx, legendItems, svgRect.height * scale + gap, width, scale);
906
- }
907
+ const serializer = new XMLSerializer();
908
+ const svgString = serializer.serializeToString(clone);
909
+ const svgBlob = new Blob([svgString], {
910
+ type: 'image/svg+xml;charset=utf-8',
911
+ });
912
+ const url = URL.createObjectURL(svgBlob);
913
+
914
+ const mimeType = format === 'jpg' ? 'image/jpeg' : 'image/png';
915
+ const ext = format === 'jpg' ? 'jpg' : 'png';
916
+
917
+ const img = new Image();
918
+ img.onload = () => {
919
+ const canvas = document.createElement('canvas');
920
+ canvas.width = width;
921
+ canvas.height = height;
922
+ const ctx = canvas.getContext('2d');
923
+ if (!ctx) return;
924
+
925
+ ctx.fillStyle = '#ffffff';
926
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
927
+ ctx.drawImage(img, 0, 0, svgRect.width * scale, svgRect.height * scale);
928
+ URL.revokeObjectURL(url);
929
+
930
+ if (legendItems.length > 0) {
931
+ drawLegend(
932
+ ctx,
933
+ legendItems,
934
+ svgRect.height * scale + gap,
935
+ width,
936
+ scale
937
+ );
938
+ }
907
939
 
908
- canvas.toBlob(blob => {
909
- if (!blob) return;
910
- const imgUrl = URL.createObjectURL(blob);
911
- const a = document.createElement('a');
912
- a.href = imgUrl;
913
- a.download = `${title || 'chart'}.${ext}`;
914
- a.click();
915
- URL.revokeObjectURL(imgUrl);
916
- }, mimeType);
940
+ canvas.toBlob(blob => {
941
+ if (!blob) return;
942
+ const imgUrl = URL.createObjectURL(blob);
943
+ const a = document.createElement('a');
944
+ a.href = imgUrl;
945
+ a.download = `${title || 'chart'}.${ext}`;
946
+ a.click();
947
+ URL.revokeObjectURL(imgUrl);
948
+ }, mimeType);
949
+ };
950
+ img.onerror = () => URL.revokeObjectURL(url);
951
+ img.src = url;
917
952
  };
918
- img.src = url;
953
+
954
+ // Defer to idle time with a 2-second deadline so it always runs.
955
+ if (typeof requestIdleCallback === 'function') {
956
+ requestIdleCallback(doWork, { timeout: 2000 });
957
+ } else {
958
+ setTimeout(doWork, 0);
959
+ }
919
960
  }
920
961
 
962
+ const ALL_CHARTS: Record<CarbonChartType, React.ComponentType<any>> = {
963
+ area: AreaChart,
964
+ areaStacked: StackedAreaChart,
965
+ bar: SimpleBarChart,
966
+ barGrouped: GroupedBarChart,
967
+ barStacked: StackedBarChart,
968
+ donut: DonutChart,
969
+ line: LineChart,
970
+ lollipop: LollipopChart,
971
+ pie: PieChart,
972
+ radar: RadarChart,
973
+ boxplot: BoxplotChart,
974
+ bubble: BubbleChart,
975
+ bullet: BulletChart,
976
+ gauge: GaugeChart,
977
+ histogram: HistogramChart,
978
+ meter: MeterChart,
979
+ scatter: ScatterChart,
980
+ combo: ComboChart,
981
+ };
982
+
921
983
  interface InternalToolbarProps {
922
984
  config: ChartToolbarConfig;
923
985
  dataSet: Array<Record<string, unknown>>;
@@ -986,7 +1048,11 @@ function CarbonChartToolbar({
986
1048
  isInverse={isInverse}
987
1049
  theme={theme}
988
1050
  >
989
- <ChartTitle isInverse={isInverse} theme={theme}>
1051
+ <ChartTitle
1052
+ as={`h${config.titleLevel ?? 2}` as keyof JSX.IntrinsicElements}
1053
+ isInverse={isInverse}
1054
+ theme={theme}
1055
+ >
990
1056
  {resolvedTitle}
991
1057
  </ChartTitle>
992
1058
  <ToolbarActions theme={theme}>
@@ -1078,8 +1144,15 @@ export const CarbonChart = React.forwardRef<HTMLDivElement, CarbonChartProps>(
1078
1144
  }
1079
1145
  };
1080
1146
  document.addEventListener('fullscreenchange', onFullscreenChange);
1081
- return () =>
1147
+ return () => {
1082
1148
  document.removeEventListener('fullscreenchange', onFullscreenChange);
1149
+ // Restore height if the component unmounts while in fullscreen.
1150
+ const chartHolder =
1151
+ internalRef.current?.querySelector<HTMLElement>('.cds--chart-holder');
1152
+ if (chartHolder && document.fullscreenElement) {
1153
+ chartHolder.style.height = savedHeightRef.current;
1154
+ }
1155
+ };
1083
1156
  }, [fullscreenEnabled]);
1084
1157
 
1085
1158
  const openTableModal = React.useCallback(
@@ -1092,9 +1165,9 @@ export const CarbonChart = React.forwardRef<HTMLDivElement, CarbonChartProps>(
1092
1165
 
1093
1166
  const closeTableModal = React.useCallback(() => {
1094
1167
  setIsTableOpen(false);
1095
- setTimeout(() => {
1168
+ requestAnimationFrame(() => {
1096
1169
  lastTableTriggerRef.current?.focus();
1097
- }, 0);
1170
+ });
1098
1171
  }, []);
1099
1172
 
1100
1173
  const toggleFullscreen = React.useCallback(() => {
@@ -1110,64 +1183,47 @@ export const CarbonChart = React.forwardRef<HTMLDivElement, CarbonChartProps>(
1110
1183
  }, []);
1111
1184
 
1112
1185
  const chartTitle: string =
1113
- (options as any).title || toolbarI18n.defaultTitle;
1186
+ (options as ExtendedChartOptions).title || toolbarI18n.defaultTitle;
1114
1187
 
1115
1188
  const handleModalDownloadCsv = React.useCallback(() => {
1116
1189
  downloadCsv(dataSet as Array<Record<string, unknown>>, chartTitle);
1117
1190
  }, [dataSet, chartTitle]);
1118
1191
 
1119
1192
  useCarbonModalFocusManagement(internalRef);
1120
- const allCharts = {
1121
- area: AreaChart,
1122
- areaStacked: StackedAreaChart,
1123
- bar: SimpleBarChart,
1124
- barGrouped: GroupedBarChart,
1125
- barStacked: StackedBarChart,
1126
- donut: DonutChart,
1127
- line: LineChart,
1128
- lollipop: LollipopChart,
1129
- pie: PieChart,
1130
- radar: RadarChart,
1131
- boxplot: BoxplotChart,
1132
- bubble: BubbleChart,
1133
- bullet: BulletChart,
1134
- gauge: GaugeChart,
1135
- histogram: HistogramChart,
1136
- meter: MeterChart,
1137
- scatter: ScatterChart,
1138
- combo: ComboChart,
1139
- };
1140
1193
 
1141
- function buildColors() {
1194
+ const colorScale = React.useMemo(() => {
1142
1195
  const scaleColorsObj: ColorsObject = {};
1143
-
1144
- const allGroups = dataSet.map(item => {
1145
- return 'group' in item ? item['group'] : null;
1146
- });
1147
- const uniqueGroups = allGroups.filter(
1148
- (g, index) => allGroups.indexOf(g) === index
1149
- );
1150
- const customColors = ((options as any).colors as string[]) || [];
1196
+ const customColors = (options as ExtendedChartOptions).colors || [];
1151
1197
  const allColors = [...customColors, ...theme.chartColors];
1152
1198
  const allInverseColors = [...customColors, ...theme.chartColorsInverse];
1199
+ const allGroups = dataSet.map(item =>
1200
+ 'group' in item ? (item as Record<string, unknown>)['group'] : null
1201
+ );
1202
+ const uniqueGroups = Array.from(new Set(allGroups));
1153
1203
 
1154
- uniqueGroups.forEach((group, i) => {
1155
- if (uniqueGroups.length <= allColors.length) {
1156
- return (scaleColorsObj[group || ('null' as any)] = isInverse
1204
+ if (uniqueGroups.length <= allColors.length) {
1205
+ for (let i = 0; i < uniqueGroups.length; i++) {
1206
+ const group = uniqueGroups[i];
1207
+ scaleColorsObj[String(group ?? 'null')] = isInverse
1157
1208
  ? allInverseColors[i]
1158
- : allColors[i]);
1209
+ : allColors[i];
1159
1210
  }
1160
- return {};
1161
- });
1211
+ }
1162
1212
 
1163
1213
  return scaleColorsObj;
1164
- }
1214
+ }, [
1215
+ dataSet,
1216
+ options,
1217
+ theme.chartColors,
1218
+ theme.chartColorsInverse,
1219
+ isInverse,
1220
+ ]);
1165
1221
 
1166
1222
  const newOptions = {
1167
1223
  ...options,
1168
1224
  theme: isInverse ? ChartTheme.G100 : ChartTheme.WHITE,
1169
1225
  color: {
1170
- scale: buildColors(),
1226
+ scale: colorScale,
1171
1227
  },
1172
1228
  tooltip: {
1173
1229
  ...(options?.tooltip || {}),
@@ -1178,21 +1234,107 @@ export const CarbonChart = React.forwardRef<HTMLDivElement, CarbonChartProps>(
1178
1234
  ...(chartToolbar ? { toolbar: { enabled: false } } : {}),
1179
1235
  };
1180
1236
 
1181
- const ChartType = allCharts[type] as any;
1237
+ const ChartType = ALL_CHARTS[type];
1182
1238
 
1183
- // Adding aria-label to main SVG container
1184
1239
  React.useEffect(() => {
1185
- if (ariaLabel) {
1186
- document.querySelectorAll('.graph-frame ').forEach(div => {
1187
- div.setAttribute('aria-label', ariaLabel);
1188
- });
1189
- }
1190
- });
1240
+ if (!ariaLabel) return;
1241
+ const rafId = requestAnimationFrame(() => {
1242
+ const svgEl = internalRef.current?.querySelector('.graph-frame');
1243
+ if (!svgEl) return;
1244
+ svgEl.setAttribute('aria-label', ariaLabel);
1245
+ });
1246
+ return () => cancelAnimationFrame(rafId);
1247
+ }, [ariaLabel]);
1248
+
1249
+ // Make Carbon Charts data points keyboard-focusable.
1250
+ React.useEffect(() => {
1251
+ const wrapper = internalRef.current;
1252
+ if (!wrapper) return;
1253
+
1254
+ const isDot = (el: EventTarget | null): el is SVGCircleElement =>
1255
+ el instanceof Element &&
1256
+ el.nodeName.toLowerCase() === 'circle' &&
1257
+ el.classList.contains('dot');
1258
+
1259
+ const onFocusIn = (e: FocusEvent) => {
1260
+ if (!isDot(e.target)) return;
1261
+ const dot = e.target as SVGCircleElement;
1262
+ dot.style.opacity = '1';
1263
+ const { left, top, width, height } = dot.getBoundingClientRect();
1264
+ const cx = left + width / 2;
1265
+ const cy = top + height / 2;
1266
+ dot.dispatchEvent(
1267
+ new MouseEvent('mouseover', {
1268
+ bubbles: true,
1269
+ clientX: cx,
1270
+ clientY: cy,
1271
+ screenX: cx,
1272
+ screenY: cy,
1273
+ })
1274
+ );
1275
+ dot.dispatchEvent(
1276
+ new MouseEvent('mousemove', {
1277
+ bubbles: true,
1278
+ clientX: cx,
1279
+ clientY: cy,
1280
+ screenX: cx,
1281
+ screenY: cy,
1282
+ })
1283
+ );
1284
+ };
1191
1285
 
1192
- const groupsLength = Object.keys(buildColors()).length;
1286
+ const onFocusOut = (e: FocusEvent) => {
1287
+ if (!isDot(e.target)) return;
1288
+ const dot = e.target;
1289
+ dot.style.opacity = '';
1290
+ dot.dispatchEvent(new MouseEvent('mouseout', { bubbles: true }));
1291
+ };
1292
+
1293
+ const onKeyDown = (e: KeyboardEvent) => {
1294
+ if (!isDot(e.target)) return;
1295
+ if (e.key !== 'Enter' && e.key !== ' ') return;
1296
+ e.preventDefault();
1297
+ const dot = e.target;
1298
+ const { left, top, width, height } = dot.getBoundingClientRect();
1299
+ const cx = left + width / 2;
1300
+ const cy = top + height / 2;
1301
+ dot.dispatchEvent(
1302
+ new MouseEvent('click', {
1303
+ bubbles: true,
1304
+ clientX: cx,
1305
+ clientY: cy,
1306
+ })
1307
+ );
1308
+ };
1309
+
1310
+ const rafId = requestAnimationFrame(() => {
1311
+ wrapper
1312
+ .querySelectorAll<SVGCircleElement>('circle.dot')
1313
+ .forEach(dot => {
1314
+ if (!dot.hasAttribute('tabindex')) {
1315
+ dot.setAttribute('tabindex', '0');
1316
+ }
1317
+ });
1318
+ });
1319
+
1320
+ wrapper.addEventListener('focusin', onFocusIn);
1321
+ wrapper.addEventListener('focusout', onFocusOut);
1322
+ wrapper.addEventListener('keydown', onKeyDown);
1323
+
1324
+ return () => {
1325
+ cancelAnimationFrame(rafId);
1326
+ wrapper.removeEventListener('focusin', onFocusIn);
1327
+ wrapper.removeEventListener('focusout', onFocusOut);
1328
+ wrapper.removeEventListener('keydown', onKeyDown);
1329
+ };
1330
+ }, [type]);
1331
+
1332
+ const groupsLength = Object.keys(colorScale).length;
1193
1333
 
1194
1334
  const showTable = chartToolbar?.showAsTable !== false;
1195
1335
 
1336
+ if (!ChartType) return null;
1337
+
1196
1338
  return (
1197
1339
  <FullscreenRoot ref={mergedRef} isInverse={isInverse} theme={theme}>
1198
1340
  <CarbonChartWrapper
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -20,12 +20,19 @@ export default {
20
20
  options: CarbonChartType,
21
21
  },
22
22
  },
23
+ titleLevel: {
24
+ control: { type: 'select' },
25
+ options: [1, 2, 3, 4, 5, 6],
26
+ description: 'Heading level for the chart title (h1–h6).',
27
+ },
23
28
  },
24
29
  } as Meta;
25
30
 
26
- const Template: StoryFn<CarbonChartProps> = args => (
31
+ const Template: StoryFn<
32
+ CarbonChartProps & { titleLevel?: 1 | 2 | 3 | 4 | 5 | 6 }
33
+ > = ({ titleLevel, ...args }) => (
27
34
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
35
+ <CarbonChart {...args} chartToolbar={{ titleLevel }} />
29
36
  </Card>
30
37
  );
31
38
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,9 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args}>Sample text</CarbonChart>
28
+ <CarbonChart {...args} chartToolbar={{}}>
29
+ Sample text
30
+ </CarbonChart>
29
31
  </Card>
30
32
  );
31
33
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31
 
@@ -25,7 +25,7 @@ export default {
25
25
 
26
26
  const Template: StoryFn<CarbonChartProps> = args => (
27
27
  <Card isInverse={args.isInverse} style={{ padding: '12px' }}>
28
- <CarbonChart {...args} />
28
+ <CarbonChart {...args} chartToolbar={{}} />
29
29
  </Card>
30
30
  );
31
31