@oliasoft-open-source/charts-library 2.1.0 → 2.1.2

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 (29) hide show
  1. package/.storybook/main.js +18 -22
  2. package/.storybook/preview.js +37 -0
  3. package/.storybook/storybook.less +8 -0
  4. package/package.json +2 -1
  5. package/src/components/bar-chart/bar-chart-prop-types.js +7 -0
  6. package/src/components/bar-chart/bar-chart.jsx +7 -2
  7. package/src/components/bar-chart/{basic.stories.jsx → bar-chart.stories.jsx} +108 -371
  8. package/src/components/line-chart/Controls/Controls.jsx +2 -0
  9. package/src/components/line-chart/Controls/Layer.jsx +52 -49
  10. package/src/components/line-chart/line-chart-prop-types.js +10 -0
  11. package/src/components/line-chart/line-chart.jsx +91 -10
  12. package/src/components/line-chart/line-chart.module.less +6 -0
  13. package/src/components/line-chart/line-chart.stories.jsx +393 -0
  14. package/src/components/line-chart/state/line-chart-reducer.js +26 -15
  15. package/src/components/pie-chart/pie-chart.stories.jsx +234 -0
  16. package/src/components/scatter-chart/{scatter.stories.jsx → scatter-chart.stories.jsx} +25 -79
  17. package/src/helpers/chart-consts.js +2 -0
  18. package/src/helpers/chart-interface.ts +9 -0
  19. package/src/helpers/chart-utils.js +24 -4
  20. package/src/helpers/get-custom-legend-plugin-example.js +81 -0
  21. package/src/components/bar-chart/charts.stories.jsx +0 -119
  22. package/src/components/line-chart/basic.stories.jsx +0 -735
  23. package/src/components/line-chart/charts.stories.jsx +0 -264
  24. package/src/components/line-chart/stories/shapes/cubes.stories.jsx +0 -326
  25. package/src/components/line-chart/stories/shapes/pyramid.stories.jsx +0 -189
  26. package/src/components/line-chart/stories/shapes/round.stories.jsx +0 -339
  27. package/src/components/line-chart/stories/shapes/triangle.stories.jsx +0 -166
  28. package/src/components/pie-chart/basic.stories.jsx +0 -390
  29. package/src/components/pie-chart/charts.stories.jsx +0 -66
@@ -0,0 +1,234 @@
1
+ import React from 'react';
2
+ import { PieChart } from './pie-chart';
3
+
4
+ const labels1 = ['label 1', 'label 2', 'label 3'];
5
+ const dataset1 = { label: 'data1', data: [3, 8, 7] };
6
+ const dataset2 = { label: 'data2', data: [1, 2, 3] };
7
+ const dataset3 = { label: 'data3', data: [5, 0, 4] };
8
+
9
+ const offsetDataset = {
10
+ label: 'spacing data',
11
+ offset: 40,
12
+ data: [3, 8, 7],
13
+ };
14
+
15
+ const hoverOffsetDataset = {
16
+ label: 'hover data',
17
+ data: [3, 8, 7],
18
+ hoverOffset: 40,
19
+ };
20
+
21
+ const datasetDatalabels = {
22
+ label: 'data',
23
+ data: [
24
+ {
25
+ value: 210,
26
+ label: 'label',
27
+ },
28
+ {
29
+ value: 333,
30
+ label: 'labal',
31
+ },
32
+ {
33
+ value: 248,
34
+ label: 'lebel',
35
+ },
36
+ ],
37
+ };
38
+ const datasetNoDataLabels = {
39
+ label: 'data',
40
+ data: [210, 333, 248],
41
+ };
42
+
43
+ const singleChart = {
44
+ data: {
45
+ labels: labels1,
46
+ datasets: [dataset1],
47
+ },
48
+ };
49
+
50
+ const multiplePieData = {
51
+ data: {
52
+ labels: labels1,
53
+ datasets: [dataset1, dataset2, dataset3],
54
+ },
55
+ };
56
+ const stackedMultiplePieData = {
57
+ data: {
58
+ labels: labels1,
59
+ datasets: [dataset1, dataset2, dataset3],
60
+ },
61
+ options: {
62
+ title: 'Stacked',
63
+ graph: { stacked: true },
64
+ },
65
+ };
66
+
67
+ const dataLabelsChart = {
68
+ data: {
69
+ labels: labels1,
70
+ datasets: [datasetDatalabels],
71
+ },
72
+ options: {
73
+ title: 'With custom datalabels',
74
+ graph: {
75
+ showDataLabels: true,
76
+ },
77
+ },
78
+ };
79
+ const dataLabelsNumberChart = {
80
+ data: {
81
+ labels: labels1,
82
+ datasets: [datasetNoDataLabels],
83
+ },
84
+ options: {
85
+ title: 'With value as datalabels',
86
+ graph: {
87
+ showDataLabels: true,
88
+ },
89
+ },
90
+ };
91
+
92
+ const dataLabelssTooltips = {
93
+ data: {
94
+ labels: labels1,
95
+ datasets: [datasetDatalabels],
96
+ },
97
+ options: {
98
+ title: 'With datalabels in tooltips',
99
+ tooltip: {
100
+ showLabelsInTooltips: true,
101
+ },
102
+ },
103
+ };
104
+
105
+ const darkMode = {
106
+ data: {
107
+ labels: labels1,
108
+ datasets: [dataset1, dataset2, dataset3],
109
+ },
110
+ options: {
111
+ title: 'Dark mode',
112
+ axes: {
113
+ x: [
114
+ {
115
+ label: 'X',
116
+ },
117
+ ],
118
+ },
119
+ chartStyling: {
120
+ darkMode: true,
121
+ },
122
+ graph: {
123
+ showDataLabels: true,
124
+ showMinorGridlines: true,
125
+ },
126
+ annotations: {
127
+ annotationsData: [
128
+ {
129
+ label: 'Annotation 1',
130
+ value: 6,
131
+ },
132
+ ],
133
+ },
134
+ },
135
+ };
136
+
137
+ const hoverOffsetChart = {
138
+ data: {
139
+ labels: labels1,
140
+ datasets: [hoverOffsetDataset],
141
+ },
142
+ options: { title: 'Hover offset' },
143
+ };
144
+
145
+ const offsetChart = {
146
+ data: {
147
+ labels: labels1,
148
+ datasets: [offsetDataset],
149
+ },
150
+ options: { title: 'Segment offset' },
151
+ };
152
+
153
+ const animatedChart = {
154
+ data: {
155
+ labels: labels1,
156
+ datasets: [dataset1],
157
+ },
158
+ options: {
159
+ chartStyling: { performanceMode: false },
160
+ },
161
+ };
162
+
163
+ const doughnutPercentage = {
164
+ data: {
165
+ labels: labels1,
166
+ datasets: [dataset1],
167
+ },
168
+ options: {
169
+ title: 'Cutout 50%',
170
+ graph: { cutout: '50%' },
171
+ },
172
+ };
173
+
174
+ const doughnutNumber = {
175
+ data: {
176
+ labels: labels1,
177
+ datasets: [dataset1],
178
+ },
179
+ options: {
180
+ title: 'Cutout 20 pixels',
181
+ graph: { cutout: 20 },
182
+ },
183
+ };
184
+
185
+ export default {
186
+ title: 'PieChart',
187
+ component: PieChart,
188
+ args: {
189
+ chart: singleChart,
190
+ },
191
+ };
192
+
193
+ const Template = (args) => {
194
+ return (
195
+ <PieChart
196
+ // eslint-disable-next-line react/jsx-props-no-spreading
197
+ {...args}
198
+ />
199
+ );
200
+ };
201
+ export const Default = Template.bind({});
202
+
203
+ export const MultipleSets = Template.bind({});
204
+ MultipleSets.args = { chart: multiplePieData };
205
+
206
+ export const MultipleSetsStacked = Template.bind({});
207
+ MultipleSetsStacked.args = { chart: stackedMultiplePieData };
208
+
209
+ export const Datalabels = Template.bind({});
210
+ Datalabels.args = { chart: dataLabelsChart };
211
+
212
+ export const DatalabelsValues = Template.bind({});
213
+ DatalabelsValues.args = { chart: dataLabelsNumberChart };
214
+
215
+ export const DatalabelsInTooltips = Template.bind({});
216
+ DatalabelsInTooltips.args = { chart: dataLabelssTooltips };
217
+
218
+ export const Animated = Template.bind({});
219
+ Animated.args = { chart: animatedChart };
220
+
221
+ export const Doughnut = Template.bind({});
222
+ Doughnut.args = { chart: doughnutPercentage };
223
+
224
+ export const DoughnutFixedCutout = Template.bind({});
225
+ DoughnutFixedCutout.args = { chart: doughnutNumber };
226
+
227
+ export const HoverOffset = Template.bind({});
228
+ HoverOffset.args = { chart: hoverOffsetChart };
229
+
230
+ export const SegmentOffset = Template.bind({});
231
+ HoverOffset.args = { chart: offsetChart };
232
+
233
+ export const DarkMode = Template.bind({});
234
+ DarkMode.args = { chart: darkMode };
@@ -1,10 +1,7 @@
1
1
  import React from 'react';
2
- import { Container } from '../../helpers/container';
3
2
  import ScatterChart from './scatter-chart';
4
3
 
5
4
  const style = {
6
- height: '1000px',
7
- width: '1000px',
8
5
  colorOrange: '#ED9860',
9
6
  colorLightGray: '#C8C8C8',
10
7
  colorDarkGray: '#595959',
@@ -114,83 +111,32 @@ const chartLegendRight = {
114
111
  },
115
112
  };
116
113
 
117
- const chartResponsive = {
118
- data: {
119
- datasets: datasetMultiLines,
120
- },
121
- options: {
122
- responsive: true,
123
- maintainAspectRatio: false,
124
- },
125
- };
126
114
  export default {
127
- title: 'Scatter Chart',
115
+ title: 'ScatterChart',
128
116
  component: ScatterChart,
117
+ args: {
118
+ chart: chartDefault,
119
+ },
120
+ };
121
+
122
+ const Template = (args) => {
123
+ return (
124
+ <ScatterChart
125
+ // eslint-disable-next-line react/jsx-props-no-spreading
126
+ {...args}
127
+ />
128
+ );
129
129
  };
130
+ export const Default = Template.bind({});
131
+
132
+ export const Linear = Template.bind({});
133
+ Linear.args = { chart: chartWithLine };
134
+
135
+ export const MultiLines = Template.bind({});
136
+ MultiLines.args = { chart: multiLines };
137
+
138
+ export const Reverse = Template.bind({});
139
+ Reverse.args = { chart: chartReverse };
130
140
 
131
- export const Default = () => (
132
- <Container style={style}>
133
- <ScatterChart chart={chartDefault} />
134
- </Container>
135
- );
136
-
137
- export const Linear = () => (
138
- <Container style={style}>
139
- <ScatterChart chart={chartWithLine} />
140
- </Container>
141
- );
142
-
143
- export const MultiLines = () => (
144
- <Container style={style}>
145
- <ScatterChart chart={multiLines} />
146
- </Container>
147
- );
148
-
149
- export const Reverse = () => (
150
- <Container style={style}>
151
- <ScatterChart chart={chartReverse} />
152
- </Container>
153
- );
154
-
155
- export const LegendRight = () => (
156
- <Container style={style}>
157
- <ScatterChart chart={chartLegendRight} />
158
- </Container>
159
- );
160
-
161
- export const Responsive = () => (
162
- <Container>
163
- <div
164
- style={{
165
- width: '50%',
166
- height: '50%',
167
- }}
168
- >
169
- <ScatterChart
170
- chart={{
171
- data: multiLines.data,
172
- options: {
173
- responsive: true,
174
- maintainAspectRatio: false,
175
- },
176
- }}
177
- />
178
- </div>
179
-
180
- <div
181
- style={{
182
- width: '50%',
183
- height: '50%',
184
- }}
185
- >
186
- <ScatterChart
187
- chart={{
188
- data: multiLines.data,
189
- options: {
190
- responsive: true,
191
- },
192
- }}
193
- />
194
- </div>
195
- </Container>
196
- );
141
+ export const LegendRight = Template.bind({});
142
+ LegendRight.args = { chart: chartLegendRight };
@@ -80,3 +80,5 @@ export const ANIMATION_DURATION = {
80
80
  };
81
81
 
82
82
  export const DEFAULT_CHART_NAME = 'new_chart';
83
+
84
+ export const CUSTOM_LEGEND_PLUGIN_NAME = 'htmlLegend';
@@ -1,3 +1,5 @@
1
+ import { Plugin } from "chart.js";
2
+
1
3
  export interface IChartAnnotationsData {
2
4
  annotationAxis: 'x' | 'y';
3
5
  label: string;
@@ -16,15 +18,22 @@ export interface IChartStyling {
16
18
  width: number | string;
17
19
  height: number | string;
18
20
  maintainAspectRatio: boolean;
21
+ squareAspectRatio?: boolean;
19
22
  staticChartHeight: boolean;
20
23
  performanceMode: boolean;
21
24
  darkMode: boolean;
22
25
  }
23
26
 
27
+ export interface ICustomLegend {
28
+ customLegendPlugin: Plugin;
29
+ customLegendContainerID: string,
30
+ }
31
+
24
32
  export interface IChartLegend {
25
33
  display: boolean;
26
34
  position: 'top' | 'bottom' | 'right';
27
35
  align: 'start' | 'center' | 'end';
36
+ customLegend?: ICustomLegend;
28
37
  }
29
38
 
30
39
  export interface IChartInteractions {
@@ -1,6 +1,7 @@
1
1
  import cx from 'classnames';
2
2
  import { chartMinorGridlinesPlugin } from '../components/line-chart/line-chart.minor-gridlines-plugin';
3
3
  import {
4
+ CUSTOM_LEGEND_PLUGIN_NAME,
4
5
  DEFAULT_CHART_NAME,
5
6
  DEFAULT_DARK_MODE_COLOR,
6
7
  LEGEND_LABEL_BOX_SIZE,
@@ -11,13 +12,25 @@ import { AxisType, ChartDirection, Position } from './enums';
11
12
  /**
12
13
  * @param {import('../components/bar-chart/bar-chart.interface').IBarChartGraph |
13
14
  * import('../components/line-chart/line-chart.interface').ILineChartGraph } graph - graph data from chart options
15
+ * @param {import('../helpers/chart-interface').IChartLegend} legend
16
+ * @param {import('./chart-interface').IInitialState} state - chart state object controlled by useReducer or similar
14
17
  * @return {[]}
15
18
  */
16
- export const getPlugins = (graph) => {
19
+ export const getPlugins = (graph, legend, state = null) => {
17
20
  let plugins = [];
18
21
  if (graph.showMinorGridlines) {
19
22
  plugins.push(chartMinorGridlinesPlugin);
20
23
  }
24
+ const customLegend = legend?.customLegend;
25
+ if (
26
+ customLegend?.customLegendPlugin &&
27
+ customLegend?.customLegendContainerID
28
+ ) {
29
+ plugins.push({
30
+ id: CUSTOM_LEGEND_PLUGIN_NAME,
31
+ ...legend.customLegend.customLegendPlugin,
32
+ });
33
+ }
21
34
  return plugins;
22
35
  };
23
36
 
@@ -114,7 +127,10 @@ export const getAxisPosition = (axisType, i) => {
114
127
  * @return {string} - class name
115
128
  */
116
129
  export const getClassName = (chartStyling, styles) => {
117
- const { width, height, staticChartHeight } = chartStyling;
130
+ const { width, height, staticChartHeight, squareAspectRatio } = chartStyling;
131
+ const squareAspectRatioStyle = squareAspectRatio
132
+ ? styles.squareAspectRatio
133
+ : '';
118
134
  let heightStyles = '';
119
135
  if (width || height) {
120
136
  heightStyles = '';
@@ -123,7 +139,7 @@ export const getClassName = (chartStyling, styles) => {
123
139
  ? styles?.fixedHeight
124
140
  : styles?.stretchHeight;
125
141
  }
126
- return cx(styles.chart, heightStyles);
142
+ return cx(styles.chart, heightStyles, squareAspectRatioStyle);
127
143
  };
128
144
 
129
145
  /**
@@ -138,7 +154,11 @@ export const getLegend = (options, clickHandler, state = null) => {
138
154
  const { legend, chartStyling } = options;
139
155
  return {
140
156
  position: legend.position || Position.Top,
141
- display: state ? state.legendEnabled : legend.display,
157
+ display: !legend?.customLegend?.customLegendPlugin
158
+ ? state
159
+ ? state.legendEnabled
160
+ : legend.display
161
+ : false,
142
162
  align: legend.align,
143
163
  labels: {
144
164
  boxHeight: LEGEND_LABEL_BOX_SIZE,
@@ -0,0 +1,81 @@
1
+ const getOrCreateLegendList = (chart, id) => {
2
+ const legendContainer = document.getElementById(id);
3
+ let listContainer = legendContainer.querySelector('ul');
4
+
5
+ if (!listContainer) {
6
+ listContainer = document.createElement('ul');
7
+ listContainer.style.display = 'flex';
8
+ listContainer.style.flexDirection = 'row';
9
+ listContainer.style.margin = 0;
10
+ listContainer.style.padding = 0;
11
+
12
+ legendContainer.appendChild(listContainer);
13
+ }
14
+
15
+ return listContainer;
16
+ };
17
+
18
+ /**
19
+ * Gets an example custom legend plugin for use in storybook.
20
+ * @param {string} customLegendContainerID - the id of the div container to put the generated legend in
21
+ */
22
+ export const getCustomLegendPlugin = (customLegendContainerID) => ({
23
+ afterUpdate(chart, _args, _options) {
24
+ const ul = getOrCreateLegendList(chart, customLegendContainerID);
25
+
26
+ // Remove old legend items
27
+ while (ul.firstChild) {
28
+ ul.firstChild.remove();
29
+ }
30
+
31
+ // Reuse the built-in legendItems generator
32
+ const items = chart.options.plugins.legend.labels.generateLabels(chart);
33
+
34
+ items.forEach((item) => {
35
+ const li = document.createElement('li');
36
+ li.style.alignItems = 'center';
37
+ li.style.cursor = 'pointer';
38
+ li.style.display = 'flex';
39
+ li.style.flexDirection = 'row';
40
+ li.style.marginLeft = '10px';
41
+
42
+ li.onclick = () => {
43
+ const { type } = chart.config;
44
+ if (type === 'pie' || type === 'doughnut') {
45
+ // Pie and doughnut charts only have a single dataset and visibility is per item
46
+ chart.toggleDataVisibility(item.index);
47
+ } else {
48
+ chart.setDatasetVisibility(
49
+ item.datasetIndex,
50
+ !chart.isDatasetVisible(item.datasetIndex),
51
+ );
52
+ }
53
+ chart.update();
54
+ };
55
+
56
+ // Color box
57
+ const boxSpan = document.createElement('span');
58
+ boxSpan.style.background = item.fillStyle;
59
+ boxSpan.style.borderColor = item.strokeStyle;
60
+ boxSpan.style.borderWidth = item.lineWidth + 'px';
61
+ boxSpan.style.display = 'inline-block';
62
+ boxSpan.style.height = '20px';
63
+ boxSpan.style.marginRight = '10px';
64
+ boxSpan.style.width = '20px';
65
+
66
+ // Text
67
+ const textContainer = document.createElement('p');
68
+ textContainer.style.color = item.fontColor;
69
+ textContainer.style.margin = 0;
70
+ textContainer.style.padding = 0;
71
+ textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
72
+
73
+ const text = document.createTextNode(item.text);
74
+ textContainer.appendChild(text);
75
+
76
+ li.appendChild(boxSpan);
77
+ li.appendChild(textContainer);
78
+ ul.appendChild(li);
79
+ });
80
+ },
81
+ });
@@ -1,119 +0,0 @@
1
- import React from 'react';
2
- import { BarChart } from './bar-chart';
3
- import { Container } from '../../helpers/container';
4
-
5
- const style = {
6
- height: '1000px',
7
- width: '1000px',
8
- };
9
-
10
- const chart = {
11
- data: {
12
- labels: ['monday', 'tuesday', 'wednesday', 'friday'],
13
- datasets: [
14
- {
15
- label: 'some data',
16
- borderColor: ['red', 'purple', 'black', 'rgba(255,255,64,0.3)'],
17
- backgroundColor: [
18
- 'white',
19
- 'transparent',
20
- 'red',
21
- 'rgba(255,255,64,0.9)',
22
- ],
23
- borderWidth: 4,
24
- borderRadius: 50,
25
- borderSkipped: false,
26
- data: [[20, 40], 30, [-10, 11], 42],
27
- stack: 1,
28
- },
29
- {
30
- label: 'some other data',
31
- borderColor: ['purple', 'orange'],
32
- backgroundColor: ['orange', 'purple'],
33
- borderWidth: 10,
34
- borderSkipped: false,
35
- data: [1, 8, 22, 19],
36
- stack: 1,
37
- },
38
- {
39
- label: 'some otter data',
40
- yAxisID: 'y2',
41
- data: [10, 20, 22, 19],
42
- stack: 2,
43
- },
44
- ],
45
- },
46
- options: {
47
- title: 'Look at those bars!',
48
- axes: {
49
- x: [
50
- {
51
- label: 'x data',
52
- gridLines: {
53
- drawOnChartArea: false,
54
- },
55
- unit: 'day',
56
- },
57
- ],
58
- y: [
59
- {
60
- label: 'y data',
61
- position: 'right',
62
- color: '#a30125',
63
- unit: 'm',
64
- },
65
- {
66
- position: 'left',
67
- color: '#011fa3',
68
- gridLines: {
69
- drawOnChartArea: false,
70
- },
71
- unit: 'otters',
72
- stepSize: 1,
73
- },
74
- ],
75
- },
76
- additionalAxesOptions: {
77
- beginAtZero: true,
78
- stacked: true,
79
- reverse: true,
80
- suggestedMin: -15,
81
- },
82
- chartStyling: {
83
- width: 1200,
84
- height: 800,
85
- performanceMode: false,
86
- },
87
- graph: {
88
- showDataLabels: true,
89
- showMinorGridlines: true,
90
- },
91
- annotations: {
92
- showAnnotations: true,
93
- controlAnnotation: true,
94
- annotationsData: [
95
- {
96
- annotationAxis: 'y',
97
- label: '0',
98
- value: 0,
99
- color: 'rgba(128, 32, 196, 0.5)',
100
- },
101
- ],
102
- },
103
- legend: {
104
- position: 'top',
105
- align: 'start',
106
- },
107
- },
108
- };
109
-
110
- export const BarChartKitchenSink = () => (
111
- <Container style={style}>
112
- <BarChart chart={chart} />
113
- </Container>
114
- );
115
-
116
- export default {
117
- title: 'Bar Chart Kitchen Sink',
118
- component: BarChartKitchenSink,
119
- };