@mozaic-ds/chart 0.1.0-beta.3 → 0.1.0-beta.30

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 (59) hide show
  1. package/dist/mozaic-chart.js +4453 -2623
  2. package/dist/mozaic-chart.umd.cjs +17 -11
  3. package/dist/style.css +1 -1
  4. package/package.json +25 -10
  5. package/src/assets/base.css +1 -1
  6. package/src/assets/img/bubbles.svg +4 -0
  7. package/src/components/bar/BarChart.stories.ts +105 -103
  8. package/src/components/bar/BarChart.vue +257 -131
  9. package/src/components/bar/index.ts +3 -3
  10. package/src/components/bubble/BubbleChart.stories.ts +66 -0
  11. package/src/components/bubble/BubbleChart.vue +363 -0
  12. package/src/components/bubble/index.ts +8 -0
  13. package/src/components/doughnut/DoughnutChart.stories.ts +38 -36
  14. package/src/components/doughnut/DoughnutChart.vue +210 -111
  15. package/src/components/doughnut/index.ts +3 -3
  16. package/src/components/index.ts +4 -4
  17. package/src/components/line/LineChart.stories.ts +52 -27
  18. package/src/components/line/LineChart.vue +346 -254
  19. package/src/components/line/index.ts +3 -3
  20. package/src/components/mixed/MixedBarLineChart.stories.ts +91 -0
  21. package/src/components/mixed/MixedBarLineChart.vue +413 -0
  22. package/src/components/mixed/index.ts +8 -0
  23. package/src/components/radar/RadarChart.stories.ts +102 -102
  24. package/src/components/radar/RadarChart.vue +204 -165
  25. package/src/components/radar/index.ts +3 -3
  26. package/src/main.ts +14 -4
  27. package/src/plugin.ts +10 -8
  28. package/src/services/BarChartFunctions.ts +136 -35
  29. package/src/services/BubbleTooltipService.ts +67 -0
  30. package/src/services/ChartsCommonLegend.ts +309 -137
  31. package/src/services/ColorFunctions.ts +1 -1
  32. package/src/services/DoughnutChartFunctions.ts +132 -55
  33. package/src/services/FormatUtilities.ts +28 -14
  34. package/src/services/GenericTooltipService.ts +140 -65
  35. package/src/services/MixedBarLineFunctions.ts +262 -0
  36. package/src/services/PatternFunctions.ts +25 -18
  37. package/src/services/RadarChartFunctions.ts +33 -12
  38. package/src/services/patterns/ChartDesign.ts +35 -24
  39. package/src/services/patterns/patternCircles.ts +63 -36
  40. package/src/services/patterns/patternDashedDiagonals.ts +64 -57
  41. package/src/services/patterns/patternDiagonals.ts +138 -106
  42. package/src/services/patterns/patternSquares.ts +86 -80
  43. package/src/services/patterns/patternVerticalLines.ts +76 -69
  44. package/src/services/patterns/patternZigzag.ts +92 -85
  45. package/src/stories/Changelog.mdx +6 -0
  46. package/src/stories/Contributing.mdx +101 -0
  47. package/src/stories/GettingStarted.mdx +92 -0
  48. package/src/stories/SupportAndOnboarding.mdx +44 -0
  49. package/src/types/AxisDefinition.ts +4 -0
  50. package/src/types/BarData.ts +1 -0
  51. package/src/types/Chart.ts +9 -7
  52. package/src/types/DoughnutData.ts +8 -0
  53. package/src/types/GenericData.ts +10 -10
  54. package/src/types/LineChart.ts +4 -4
  55. package/src/types/MixedBarLineData.ts +7 -0
  56. package/src/types/RadarData.ts +33 -29
  57. package/src/types/TooltipChartType.ts +7 -6
  58. package/src/vite-env.d.ts +3 -3
  59. package/src/App.vue +0 -80
@@ -4,13 +4,12 @@
4
4
  <Bar
5
5
  v-if="chartData"
6
6
  ref="barChartRef"
7
+ :id="chartId"
7
8
  :data="chartData"
8
9
  :options="options"
9
10
  :plugins="htmlLegendPlugin"
10
- :style="{ width, height }"
11
- :chart-id="chartId"
12
- :cssClasses:="cssClasses"
13
- :styles="styles"
11
+ :class="cssClasses"
12
+ :style="[{ width, height, cursor: 'pointer' }, styles]"
14
13
  />
15
14
  </div>
16
15
  <div ref="legendContainer" />
@@ -18,17 +17,28 @@
18
17
  </template>
19
18
 
20
19
  <script setup lang="ts">
21
- import { computed, ref, watch, PropType } from 'vue';
22
20
  import type { Ref } from 'vue';
23
- import { Bar, Chart } from 'vue-chartjs';
21
+ import { computed, PropType, ref, watch } from 'vue';
22
+ import { Bar } from 'vue-chartjs';
24
23
  import barChartFunctions from '../../services/BarChartFunctions';
25
24
  import type { Context } from '../../services/GenericTooltipService';
26
25
  import { GenericTooltipService } from '../../services/GenericTooltipService';
27
26
  import { TooltipChartType } from '../../types/TooltipChartType';
28
- import { formatTicks, formatWithThousandsSeprators } from '../../services/FormatUtilities';
29
- import { BarElement, CategoryScale, Chart as ChartJS, Legend, LinearScale, Title, Tooltip, Plugin } from 'chart.js';
30
- import { ButtonName } from '../../types/ButtonName';
31
- import { BarData } from "../../types/BarData";
27
+ import {
28
+ formatTicks,
29
+ formatWithThousandsSeprators
30
+ } from '../../services/FormatUtilities';
31
+ import {
32
+ BarElement,
33
+ CategoryScale,
34
+ Chart as ChartJS,
35
+ Legend,
36
+ LinearScale,
37
+ Plugin,
38
+ Title,
39
+ Tooltip
40
+ } from 'chart.js';
41
+ import { BarData } from '../../types/BarData';
32
42
  import ChartDesign from '../../services/patterns/ChartDesign';
33
43
 
34
44
  ChartJS.register(
@@ -40,104 +50,171 @@ ChartJS.register(
40
50
  LinearScale
41
51
  );
42
52
 
43
- const {
44
- colourSets,
45
- patternsStandardList
46
- } = ChartDesign();
53
+ const { colourSets, patternsStandardList } = ChartDesign();
47
54
 
48
55
  const legendContainer = ref(null);
49
56
  const selectMode = ref(false);
50
57
  const barDataProps = defineProps({
51
- chartId: {
52
- type: String,
53
- default: "radar-chart",
54
- },
55
- rawTitle: {
56
- type: String,
57
- default: 'sales',
58
- },
59
- unit: {
60
- type: String,
61
- default: '%',
62
- },
63
- labels: {
64
- type: Array as PropType<string[]>,
65
- default: () => []
66
- },
67
- // colourSet props is used to choose the colour set of the charts as defined in the Figma prototypes.
68
- // 7 colour sets are currently defined :
69
- // - Default 0 corresponds to the current one
70
- // - 1 to 6 corresponds to the "new" colour sets : https://www.figma.com/file/Hn6PyvnR385Ta0XN3KqOI9/04.-Dataviz---Documentation-(read-only)?type=design&node-id=1-69316&mode=design&t=sDytQ5BipsryWkuA-0
71
- // Remark : All the sets are defined in /src/services/patterns/ChartDesign.ts
72
- colourSet: {
73
- type: Number,
74
- default: 0
75
- },
76
- // 6 patterns exist and are not randomly given but follow the order defined in patternsStandardList /src/services/patterns/ChartDesign.ts
77
- // Additionally, a pattern has only one possible colour per colour set as defined in the Figma prototype.
78
- // In some use cases, the chart may need to show a different orders of these patterns, this can be changed using the props newPatternsOrder
79
- newPatternsOrder: {
80
- type: Array as PropType<number[]>,
81
- default: () => [0,1,2,3,4,5]
82
- },
83
- datasets: {
84
- type: Array as PropType<BarData[]>,
85
- default: () => []
86
- },
87
- width: {
88
- type: String,
89
- default: '400px',
90
- },
91
- height: {
92
- type: String,
93
- default: '300px',
94
- },
95
- cssClasses: {
96
- default: "",
97
- type: String,
98
- },
99
- styles: {
100
- type: Object as PropType<Partial<CSSStyleDeclaration>>,
101
- default: () => {},
102
- },
103
- plugins: {
104
- type: Array as PropType<Plugin<"bar">[]>,
105
- default: () => [],
106
- },
107
- stacked: {
108
- type: Boolean,
109
- default: false,
110
- },
111
- // disable accessibility patterns
112
- disableAccessibility: {
113
- type: Boolean,
114
- default: false,
115
- },
116
- firstTooltip: {
117
- type: String,
118
- default: 'content'
119
- },
120
- secondTooltip: {
121
- type: String,
122
- default: 'content2'
123
- }
124
- })
58
+ /**
59
+ * Value of the id attribute present on the <canvas> tag element the chart
60
+ */
61
+ chartId: {
62
+ type: String,
63
+ default: 'radar-chart'
64
+ },
65
+ /**
66
+ * Unit of values on canvas Y axis
67
+ */
68
+ unit: {
69
+ type: String,
70
+ default: '%'
71
+ },
72
+ /**
73
+ * Labels used to label the index axis (default x axes). See [Data structures documentation](https://www.chartjs.org/docs/latest/general/data-structures.html)
74
+ */
75
+ labels: {
76
+ type: Array as PropType<string[]>,
77
+ default: () => []
78
+ },
79
+ /**
80
+ * Used to choose the colour set of the charts as defined in the Figma prototypes.
81
+ * 7 colour sets are currently defined:
82
+ * - Default 0 corresponds to the current one
83
+ * - 1 to 6 corresponds to the "new" [colour sets](https://www.figma.com/file/Hn6PyvnR385Ta0XN3KqOI9/04.-Dataviz---Documentation-(read-only)?type=design&node-id=1-69316&mode=design&t=sDytQ5BipsryWkuA-0)
84
+ * Note: All the sets are defined in /src/services/patterns/ChartDesign.ts
85
+ */
86
+ colourSet: {
87
+ type: Number,
88
+ default: 0
89
+ },
90
+ /**
91
+ * 6 patterns exist and are not randomly given but follow the order defined in [patternsStandardList](/src/services/patterns/ChartDesign.ts)
92
+ * Additionally, a pattern has only one possible colour per colour set as defined in the Figma prototype.
93
+ * In some use cases, the chart may need to show a different orders of these patterns, this can be changed using the props newPatternsOrder
94
+ */
95
+ newPatternsOrder: {
96
+ type: Array as PropType<number[]>,
97
+ default: () => [0, 1, 2, 3, 4, 5]
98
+ },
99
+ /**
100
+ * Value of the `datasets` key present in the `data` object passed to the Chart config
101
+ * If it's the stacked barchart (stacked : true in props), each dataset may contain the stack it belongs. stack Zero if not defined
102
+ */
103
+ datasets: {
104
+ type: Array as PropType<BarData[]>,
105
+ default: () => []
106
+ },
107
+ /**
108
+ * Value of the `width` css property used to define the width of the <canvas> element
109
+ */
110
+ width: {
111
+ type: String,
112
+ default: '400px'
113
+ },
114
+ /**
115
+ * Value of the `height` css property used to define the height of the <canvas> element
116
+ */
117
+ height: {
118
+ type: String,
119
+ default: '300px'
120
+ },
121
+ /**
122
+ * Add custom CSS classes to the <canvas> element
123
+ */
124
+ cssClasses: {
125
+ type: String,
126
+ default: undefined
127
+ },
128
+ /**
129
+ * Add custom CSS styles to the <canvas> element
130
+ */
131
+ styles: {
132
+ type: Object as PropType<Partial<CSSStyleDeclaration>>,
133
+ default: () => {}
134
+ },
135
+ /**
136
+ * Value of the `plugins` key passed to the Chart config
137
+ */
138
+ plugins: {
139
+ type: Array as PropType<Plugin<'bar'>[]>,
140
+ default: () => []
141
+ },
142
+ /**
143
+ * Activates "stacked" mode
144
+ */
145
+ stacked: {
146
+ type: Boolean,
147
+ default: false
148
+ },
149
+ /**
150
+ * Disable accessibility patterns
151
+ */
152
+ disableAccessibility: {
153
+ type: Boolean,
154
+ default: false
155
+ },
156
+ /**
157
+ * Enable hover feature (may cause strange behavior when used with width and height in %)
158
+ */
159
+ enableHoverFeature: {
160
+ type: Boolean,
161
+ default: false
162
+ },
163
+ /**
164
+ * X axis title
165
+ */
166
+ xAxisTitle: {
167
+ type: String,
168
+ default: null
169
+ },
170
+ /**
171
+ * Y axis title
172
+ */
173
+ yAxisTitle: {
174
+ type: String,
175
+ default: null
176
+ },
177
+ /**
178
+ * Label of the first line in the Tooltip
179
+ */
180
+ tooltipFirstLineLabel: {
181
+ type: String,
182
+ default: 'content'
183
+ },
184
+ /**
185
+ * Label of the second line in the Tooltip
186
+ */
187
+ tooltipSecondLineLabel: {
188
+ type: String,
189
+ default: 'content2'
190
+ }
191
+ });
125
192
 
126
193
  // computed to make the colors list reactive to the props
127
194
  const patternsColors = computed(() => {
128
195
  return barDataProps.newPatternsOrder.length !== patternsStandardList.length
129
- ? colourSets[barDataProps.colourSet]
130
- : barDataProps.newPatternsOrder.map((id) => {
131
- return colourSets[barDataProps.colourSet][id]
196
+ ? colourSets[barDataProps.colourSet]
197
+ : barDataProps.newPatternsOrder.map((id) => {
198
+ return colourSets[barDataProps.colourSet][id];
132
199
  });
133
200
  });
134
201
 
135
202
  // computed to make the patterns list reactive to the props
136
- const patternsOrderedList = computed(() => {return barDataProps.newPatternsOrder.length !== patternsStandardList.length
137
- ? patternsStandardList
138
- : barDataProps.newPatternsOrder.map((id)=> {return patternsStandardList[id]})});
203
+ const patternsOrderedList = computed(() => {
204
+ return barDataProps.newPatternsOrder.length !== patternsStandardList.length
205
+ ? patternsStandardList
206
+ : barDataProps.newPatternsOrder.map((id) => {
207
+ return patternsStandardList[id];
208
+ });
209
+ });
139
210
 
140
- const disablePattern = computed(() => {return barDataProps.disableAccessibility})
211
+ const disablePattern = computed(() => {
212
+ return barDataProps.disableAccessibility;
213
+ });
214
+
215
+ const enableHover = computed(() => {
216
+ return barDataProps.enableHoverFeature;
217
+ });
141
218
 
142
219
  const {
143
220
  onHoverIndex,
@@ -145,7 +222,7 @@ const {
145
222
  reloadChart,
146
223
  getOnHoverOptions,
147
224
  getStackedDatasets,
148
- privateGetHtmlLegendPlugin,
225
+ privateGetHtmlLegendPlugin
149
226
  } = barChartFunctions();
150
227
 
151
228
  const chartData = computed(() => {
@@ -156,9 +233,9 @@ const chartData = computed(() => {
156
233
  datasets: getStackedDatasets(
157
234
  barDataProps.datasets.map((dataset, index) => ({
158
235
  data: getChartData(index, indexOfOthersLabelIfNull.value),
159
- label: barDataProps.datasets[index].label
160
- })
161
- ),
236
+ label: barDataProps.datasets[index].label,
237
+ stack: dataset.stack || 0
238
+ })),
162
239
  barDataProps.stacked,
163
240
  barDataProps.disableAccessibility,
164
241
  patternsColors.value,
@@ -172,43 +249,53 @@ let xValue: string;
172
249
  let yValue: string;
173
250
 
174
251
  const gettooltipContent = (): void => {
175
- xValue = barDataProps.firstTooltip;
176
- yValue = barDataProps.secondTooltip;
252
+ xValue = barDataProps.tooltipFirstLineLabel;
253
+ yValue = barDataProps.tooltipSecondLineLabel;
177
254
  };
178
255
 
179
256
  const indexOfOthersLabelIfNull: Ref<number | null> = computed(() => {
180
257
  const labels = barDataProps.labels;
181
258
  if (labels.includes('other')) {
182
259
  const index = labels.indexOf('other');
183
- if (barDataProps.datasets[0].data[index].rate + '' === '0' &&
184
- barDataProps.datasets[1].data[index].rate + '' === '0') {
260
+ if (
261
+ barDataProps.datasets[0].data[index].rate + '' === '0' &&
262
+ barDataProps.datasets[1].data[index].rate + '' === '0'
263
+ ) {
185
264
  return index;
186
265
  }
187
266
  }
188
267
  return null;
189
268
  });
190
269
 
191
- const getChartData = (index: number, indexOfValueToHide: number | null, isStacked: boolean = false) => {
270
+ const getChartData = (
271
+ index: number,
272
+ indexOfValueToHide: number | null,
273
+ isStacked: boolean = false
274
+ ): number[] => {
192
275
  const data = Object.assign([], barDataProps.datasets[index].data);
193
276
  if (indexOfValueToHide && indexOfOthersLabelIfNull.value) {
194
277
  data.splice(indexOfOthersLabelIfNull.value, 1);
195
278
  }
196
279
 
197
- return data.map((data: { rate: number }) => data.rate);
280
+ return data.map(
281
+ (data: { rate?: number; amount?: number }) =>
282
+ (barDataProps.unit === '%' ? data.rate : data.amount) as number
283
+ );
198
284
  };
199
285
 
200
286
  const getTooltipData = (context: Context) => {
201
- const datasetIndex = context.tooltip.dataPoints[0].datasetIndex;
202
- const dataIndex = context.tooltip.dataPoints[0].dataIndex;
203
- const percent = parseFloat(context.tooltip.body[0].lines[0].split(':')[1].replace(',', '.')).toFixed(2) +
204
- '% ';
205
- if (!dataIndex || !datasetIndex) {
206
- return '';
207
- }
208
- const rawdata =
209
- barDataProps.datasets[datasetIndex].data[dataIndex];
210
- const data = formatWithThousandsSeprators(rawdata.amount);
211
- return percent + data + rawdata.amountUnit;
287
+ const datasetIndex = context.tooltip.dataPoints[0].datasetIndex as number;
288
+ const dataIndex = context.tooltip.dataPoints[0].dataIndex as number;
289
+ const rate: string = context.tooltip.body[0].lines[0].split(':')[1];
290
+ const rawdata = barDataProps.datasets[datasetIndex].data[dataIndex];
291
+ const percent = rawdata.rate
292
+ ? parseFloat(rate.replace(',', '.')).toFixed(2) + '% '
293
+ : '';
294
+ const amountUnit = rawdata.amountUnit ? rawdata.amountUnit : '';
295
+ const data = rawdata.amount
296
+ ? formatWithThousandsSeprators(rawdata.amount)
297
+ : '';
298
+ return percent + (percent && data ? '/ ' : '') + data + amountUnit;
212
299
  };
213
300
 
214
301
  const getChartLabels = (indexOfValueToHide: number | null) => {
@@ -222,25 +309,38 @@ const getChartLabels = (indexOfValueToHide: number | null) => {
222
309
 
223
310
  const options = computed(() => ({
224
311
  // eslint-disable-next-line
225
- onHover: getOnHoverOptions(),
312
+ onHover: barDataProps.enableHoverFeature ? getOnHoverOptions() : () => {},
226
313
  elements: {
227
314
  bar: {
228
315
  borderSkipped: false
229
316
  }
230
317
  },
318
+ maxBarThickness: 64,
319
+ categoryPercentage: 0.6,
231
320
  plugins: {
232
321
  responsive: true,
233
322
  maintainAspectRatio: false,
234
323
  legend: {
235
- display: false,
324
+ display: false
236
325
  },
237
326
  title: {
238
327
  display: false
239
328
  },
329
+ datalabels: {
330
+ display: false
331
+ },
240
332
  tooltip: {
241
333
  enabled: false,
242
334
  position: 'nearest' as const,
243
335
  external: function (context: Context) {
336
+ const dataIndex: number =
337
+ context.tooltip.dataPoints?.[0].dataIndex || 0;
338
+ if (
339
+ !barDataProps.labels[dataIndex] ||
340
+ barDataProps.labels[dataIndex] === ''
341
+ ) {
342
+ return;
343
+ }
244
344
  new GenericTooltipService().createTooltip(
245
345
  context,
246
346
  getTooltipData,
@@ -258,30 +358,56 @@ const options = computed(() => ({
258
358
  },
259
359
  scales: {
260
360
  x: {
261
- stacked: true,
361
+ stacked: barDataProps.stacked,
362
+ title: {
363
+ display: barDataProps.xAxisTitle !== null,
364
+ text: barDataProps.xAxisTitle
365
+ }
262
366
  },
263
367
  y: {
264
- stacked: true,
368
+ stacked: barDataProps.stacked,
369
+ title: {
370
+ display: barDataProps.yAxisTitle !== null,
371
+ text: barDataProps.yAxisTitle
372
+ },
265
373
  ticks: {
266
374
  callback: function (val: string | number) {
267
- return formatTicks(val as number, barDataProps.unit);
268
- },
269
- maxTicksLimit: 8,
375
+ return barDataProps.unit === '%'
376
+ ? formatTicks(val as number, barDataProps.unit)
377
+ : formatWithThousandsSeprators(val as number) +
378
+ ' ' +
379
+ barDataProps.unit;
380
+ }
270
381
  }
271
382
  }
272
383
  }
273
384
  }));
274
385
 
275
- const htmlLegendPlugin = ref(privateGetHtmlLegendPlugin(legendContainer, selectMode, disablePattern, patternsColors, patternsOrderedList));
276
-
277
- watch(onHoverIndex, () => {
278
- reloadChart();
279
- }, { deep: true });
280
-
281
-
386
+ const htmlLegendPlugin = computed(() => [
387
+ privateGetHtmlLegendPlugin(
388
+ legendContainer,
389
+ selectMode,
390
+ disablePattern,
391
+ patternsColors,
392
+ patternsOrderedList,
393
+ enableHover
394
+ )
395
+ ]);
282
396
 
397
+ watch(
398
+ onHoverIndex,
399
+ () => {
400
+ reloadChart();
401
+ },
402
+ { deep: true }
403
+ );
283
404
  </script>
284
405
 
406
+ <style lang="scss">
407
+ @import 'settings-tools/all-settings';
408
+ @import 'components/c.checkbox';
409
+ </style>
410
+
285
411
  <style scoped>
286
412
  .container {
287
413
  -moz-osx-font-smoothing: grayscale;
@@ -296,4 +422,4 @@ watch(onHoverIndex, () => {
296
422
  align-items: center;
297
423
  margin-bottom: 20px;
298
424
  }
299
- </style>
425
+ </style>
@@ -1,8 +1,8 @@
1
- import type { App } from "vue";
2
- import BarChart from "./BarChart.vue";
1
+ import type { App } from 'vue';
2
+ import BarChart from './BarChart.vue';
3
3
 
4
4
  BarChart.install = (app: App) => {
5
- app.component("BarChart", BarChart);
5
+ app.component('BarChart', BarChart);
6
6
  };
7
7
 
8
8
  export { BarChart };
@@ -0,0 +1,66 @@
1
+ // BubbleChart.stories.ts
2
+
3
+ import type { Meta, StoryObj } from '@storybook/vue3';
4
+ import BubbleChart from './BubbleChart.vue';
5
+
6
+ const meta = {
7
+ title: 'Charts/Bubble',
8
+ component: BubbleChart
9
+ } satisfies Meta<typeof BubbleChart>;
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof meta>;
13
+
14
+ export const Default = {
15
+ args: {
16
+ width: '800px',
17
+ height: '500px',
18
+ labels: ['Serie 1', 'Serie 2', 'Serie 3', 'Serie 4'],
19
+ colours: [0, 4, 1, 2],
20
+ shapes: ['rectRot', 'triangle', 'circle', 'rect'],
21
+ colourSet: 4,
22
+ datasets: [
23
+ [
24
+ {
25
+ x: 20000,
26
+ y: 25,
27
+ r: 5
28
+ },
29
+ {
30
+ x: 10000,
31
+ y: 505,
32
+ r: 15
33
+ }
34
+ ],
35
+ [
36
+ {
37
+ x: 10000,
38
+ y: 30,
39
+ r: 20
40
+ },
41
+ {
42
+ x: 10,
43
+ y: 300,
44
+ r: 200
45
+ }
46
+ ],
47
+ [
48
+ {
49
+ x: 5000,
50
+ y: 320,
51
+ r: 100
52
+ }
53
+ ],
54
+ [
55
+ {
56
+ x: 18000,
57
+ y: 8,
58
+ r: 30
59
+ }
60
+ ]
61
+ ],
62
+ xAxis: { title: 'Data 1', unit: '€' },
63
+ yAxis: { title: 'Data 2', unit: 'D' },
64
+ rAxis: { title: 'Data 3', unit: '%' }
65
+ }
66
+ } satisfies Story;