@ons/design-system 72.8.0 → 72.9.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.
@@ -83,3 +83,8 @@
83
83
  display: block !important;
84
84
  white-space: nowrap !important;
85
85
  }
86
+
87
+ // Enforce cursor as auto in the legend
88
+ .highcharts-a11y-proxy-group-legend button {
89
+ cursor: auto !important;
90
+ }
@@ -1,7 +1,7 @@
1
1
  {% from "components/list/_macro.njk" import onsList %}
2
2
 
3
3
  {% macro onsChart(params) %}
4
- {% set supportedChartTypes = ['line', 'bar', 'column'] %}
4
+ {% set supportedChartTypes = ['line', 'bar', 'column', 'scatter', 'area'] %}
5
5
 
6
6
  {% if params.headingLevel and params.headingLevel >= 1 and params.headingLevel <= 4 %}
7
7
  {% set headingLevel = params.headingLevel %}
@@ -22,7 +22,6 @@
22
22
  data-highcharts-title="{{ params.title }}"
23
23
  data-highcharts-id="{{ params.id }}"
24
24
  {% if params.useStackedLayout %}data-highcharts-use-stacked-layout="{{ params.useStackedLayout }}"{% endif %}
25
- id="{{ params.id }}"
26
25
  {% if params.percentageHeightDesktop and params.chartType != 'bar' %}
27
26
  data-highcharts-percentage-height-desktop="{{ params.percentageHeightDesktop }}"
28
27
  {% endif %}
@@ -42,32 +41,34 @@
42
41
  data-highcharts-y-axis-tick-interval-desktop="{{ params.yAxis.tickIntervalDesktop }}"
43
42
  {% endif %}
44
43
  >
45
- {% if params.chartType in supportedChartTypes %}
46
- <figure class="ons-chart">
47
- {{ openingTitleTag | safe }} class="ons-chart__title">{{ params.title }}{{ closingTitleTag | safe }}
48
- {{ openingSubtitleTag | safe }}
49
- class="ons-chart__subtitle">{{ params.subtitle }}{{ closingSubtitleTag | safe }}
50
- {% if params.description %}
51
- <p class="ons-u-vh">{{ params.description }}</p>
52
- {% endif %}
44
+ <figure class="ons-chart">
45
+ {{ openingTitleTag | safe }} class="ons-chart__title">{{ params.title }}{{ closingTitleTag | safe }}
46
+ {{ openingSubtitleTag | safe }}
47
+ class="ons-chart__subtitle">{{ params.subtitle }}{{ closingSubtitleTag | safe }}
48
+ {% if params.description %}
49
+ <p class="ons-u-vh">{{ params.description }}</p>
50
+ {% endif %}
51
+ {% if params.chartType in supportedChartTypes %}
53
52
  <div data-highcharts-chart></div>
54
- {#
55
- Footnotes for the annotations at mobile
56
- Hidden from screen readers because the full text will be read out where they appear in the chart
57
- #}
58
- <ul class="ons-chart__footnotes" aria-hidden="true">
59
- {% for annotation in params.annotations %}
60
- <li class="ons-chart__footnote_item">
61
- <span class="ons-chart__footnote-number">{{ loop.index }}</span>
62
- {{ annotation.text }}
63
- </li>
64
- {% endfor %}
65
- </ul>
66
- {% if params.caption %}
67
- <figcaption class="ons-chart__caption">{{ params.caption }}</figcaption>
68
- {% endif %}
69
- </figure>
70
- {% endif %}
53
+ {% else %}
54
+ <p data-invalid-chart-type><b>Chart type "{{ params.chartType }}" is not supported</b></p>
55
+ {% endif %}
56
+ {#
57
+ Footnotes for the annotations at mobile
58
+ Hidden from screen readers because the full text will be read out where they appear in the chart
59
+ #}
60
+ <ul class="ons-chart__footnotes" aria-hidden="true">
61
+ {% for annotation in params.annotations %}
62
+ <li class="ons-chart__footnote_item">
63
+ <span class="ons-chart__footnote-number">{{ loop.index }}</span>
64
+ {{ annotation.text }}
65
+ </li>
66
+ {% endfor %}
67
+ </ul>
68
+ {% if params.caption %}
69
+ <figcaption class="ons-chart__caption">{{ params.caption }}</figcaption>
70
+ {% endif %}
71
+ </figure>
71
72
 
72
73
  {% if params.download.title and params.download.itemsList | length > 0 %}
73
74
  {{ openingDownloadTitleTag | safe }}
@@ -79,6 +80,7 @@
79
80
  })
80
81
  }}
81
82
  {% endif %}
83
+ {% if params.chartType in supportedChartTypes %}
82
84
  {% set series = [] %}
83
85
  {% for item in params.series %}
84
86
  {%
@@ -91,6 +93,7 @@
91
93
  "dataLabels": {
92
94
  "enabled": item.dataLabels if item.dataLabels else false
93
95
  },
96
+ "connectNulls": item.connectNulls if item.connectNulls else false,
94
97
  "type": item.type if item.type and item.type == 'line' else params.chartType
95
98
  }
96
99
  %}
@@ -126,9 +129,10 @@
126
129
 
127
130
  <!-- Set scripts to pass the config values as json to the javascript -->
128
131
  <!-- prettier-ignore-start -->
129
- <script type="application/json" data-highcharts-config--{{ params.id }}>
130
- {{ config | tojson }}
131
- </script>
132
+ <script type="application/json" data-highcharts-config--{{ params.id }}>
133
+ {{ config | tojson }}
134
+ </script>
135
+ {% endif %}
132
136
  {% if params.annotations %}
133
137
  <script type="application/json" data-highcharts-annotations--{{ params.id }}>
134
138
  {{ params.annotations | tojson }}
@@ -15,6 +15,9 @@ import {
15
15
  EXAMPLE_COLUMN_CHART_WITH_ANNOTATIONS_PARAMS,
16
16
  EXAMPLE_BAR_WITH_LINE_CHART_PARAMS,
17
17
  EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS,
18
+ EXAMPLE_SCATTER_CHART_PARAMS,
19
+ EXAMPLE_AREA_CHART_PARAMS,
20
+ EXAMPLE_INVALID_CHART_PARAMS,
18
21
  } from './_test-examples';
19
22
 
20
23
  describe('Macro: Chart', () => {
@@ -28,6 +31,10 @@ describe('Macro: Chart', () => {
28
31
  expect(results).toHaveNoViolations();
29
32
  });
30
33
 
34
+ test('THEN: it renders the title', () => {
35
+ expect($('.ons-chart__title').text()).toBe('Example Line Chart');
36
+ });
37
+
31
38
  test('THEN: it renders the subtitle', () => {
32
39
  expect($('.ons-chart__subtitle').text()).toBe('A sample subtitle');
33
40
  });
@@ -153,6 +160,9 @@ describe('Macro: Chart', () => {
153
160
  expect(configScript).toContain('"data":[5,15,25]');
154
161
  expect(configScript).toContain('"name":"Category 2"');
155
162
  expect(configScript).toContain('"data":[10,20,30]');
163
+ expect(configScript).toContain('"connectNulls":true');
164
+ expect(configScript).toContain('"marker":{"enabled":true}');
165
+ expect(configScript).toContain('"dataLabels":{"enabled":true}');
156
166
  });
157
167
  });
158
168
  });
@@ -854,4 +864,163 @@ describe('Macro: Chart', () => {
854
864
  });
855
865
  });
856
866
  });
867
+
868
+ describe('FOR: Scatter chart', () => {
869
+ describe('GIVEN: Params: required', () => {
870
+ describe('WHEN: required params are provided', () => {
871
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_SCATTER_CHART_PARAMS));
872
+
873
+ test('THEN: it passes jest-axe checks', async () => {
874
+ const results = await axe($.html());
875
+ expect(results).toHaveNoViolations();
876
+ });
877
+
878
+ test('THEN: it renders the chart container with correct data attributes', () => {
879
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('scatter');
880
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('primary');
881
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Scatter Chart');
882
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('scatter-chart-123');
883
+ });
884
+ });
885
+ });
886
+ });
887
+
888
+ describe('FOR: Area Chart', () => {
889
+ describe('GIVEN: Params: required', () => {
890
+ describe('WHEN: required params are provided', () => {
891
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_AREA_CHART_PARAMS));
892
+ const configScript = $(`script[data-highcharts-config--area-chart-123]`).html();
893
+
894
+ test('THEN: it passes jest-axe checks', async () => {
895
+ const results = await axe($.html());
896
+ expect(results).toHaveNoViolations();
897
+ });
898
+
899
+ test('THEN: it includes at least one area series', () => {
900
+ expect(configScript).toContain('"type":"area"');
901
+ });
902
+
903
+ test('THEN: it renders the chart container with correct data attributes', () => {
904
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('area');
905
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('primary');
906
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Area Chart');
907
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('area-chart-123');
908
+ });
909
+
910
+ test('THEN: it includes the Highcharts JSON config', () => {
911
+ expect(configScript).toContain('"text":"X Axis Title"');
912
+ expect(configScript).toContain('"text":"Y Axis Title"');
913
+ });
914
+ });
915
+ });
916
+
917
+ describe('GIVEN: Params: Legend', () => {
918
+ describe('WHEN: legend is enabled', () => {
919
+ const $ = cheerio.load(renderComponent('chart', { ...EXAMPLE_AREA_CHART_PARAMS, legend: false }));
920
+
921
+ test('THEN: it renders the legend', () => {
922
+ const configScript = $(`script[data-highcharts-config--area-chart-123]`).html();
923
+ expect(configScript).toContain('"enabled":false');
924
+ });
925
+ });
926
+ });
927
+
928
+ describe('GIVEN: Params: Caption', () => {
929
+ describe('WHEN: caption is provided', () => {
930
+ const $ = cheerio.load(
931
+ renderComponent('chart', {
932
+ ...EXAMPLE_AREA_CHART_PARAMS,
933
+ caption: 'This is an example caption for the chart.',
934
+ }),
935
+ );
936
+
937
+ test('THEN: it renders the caption when provided', () => {
938
+ expect($('figcaption').text()).toBe('This is an example caption for the chart.');
939
+ });
940
+ });
941
+ });
942
+
943
+ describe('GIVEN: Params: Description', () => {
944
+ describe('WHEN: description is provided', () => {
945
+ const $ = cheerio.load(
946
+ renderComponent('chart', {
947
+ ...EXAMPLE_AREA_CHART_PARAMS,
948
+ description: 'An accessible description for screen readers.',
949
+ }),
950
+ );
951
+
952
+ test('THEN: it renders the description for accessibility', () => {
953
+ expect($('.ons-u-vh').text()).toBe('An accessible description for screen readers.');
954
+ });
955
+ });
956
+ });
957
+
958
+ describe('GIVEN: Params: Download', () => {
959
+ describe('WHEN: download object are provided', () => {
960
+ const $ = cheerio.load(
961
+ renderComponent('chart', {
962
+ ...EXAMPLE_AREA_CHART_PARAMS,
963
+ download: {
964
+ title: 'Download Chart Data',
965
+ itemsList: [
966
+ { text: 'Download as PNG', url: 'https://example.com/chart.png' },
967
+ { text: 'Download as CSV', url: 'https://example.com/chart.csv' },
968
+ ],
969
+ },
970
+ }),
971
+ );
972
+
973
+ test('THEN: it renders the download section correctly', () => {
974
+ expect($('.ons-chart__download-title').text()).toBe('Download Chart Data');
975
+
976
+ const downloadLinks = $('.ons-chart__download-title').next().find('li a');
977
+ expect(downloadLinks.eq(0).text()).toBe('Download as PNG');
978
+ expect(downloadLinks.eq(0).attr('href')).toBe('https://example.com/chart.png');
979
+ expect(downloadLinks.eq(1).text()).toBe('Download as CSV');
980
+ expect(downloadLinks.eq(1).attr('href')).toBe('https://example.com/chart.csv');
981
+ });
982
+ });
983
+ });
984
+ });
985
+
986
+ describe('FOR: Invalid Chart', () => {
987
+ describe('GIVEN: Invalid chart type', () => {
988
+ describe('WHEN: an invalid chart type is provided', () => {
989
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_INVALID_CHART_PARAMS));
990
+
991
+ test('THEN: it does not render the chart', () => {
992
+ expect($('[data-highcharts-chart]').length).toBe(0);
993
+ });
994
+
995
+ test('THEN: it renders the error message', () => {
996
+ expect($('[data-invalid-chart-type]').text()).toBe('Chart type "invalid" is not supported');
997
+ });
998
+
999
+ test('THEN: it does not include the Highcharts JSON config', () => {
1000
+ const configScript = $(`script[data-highcharts-config--invalid-chart-123]`).html();
1001
+ expect(configScript).toBeNull();
1002
+ });
1003
+
1004
+ test('THEN: it still renders the title', () => {
1005
+ expect($('.ons-chart__title').text()).toBe('Example Invalid Chart');
1006
+ });
1007
+
1008
+ test('THEN: it still renders the subtitle', () => {
1009
+ expect($('.ons-chart__subtitle').text()).toBe('A sample subtitle');
1010
+ });
1011
+
1012
+ test('THEN: it still renders the description', () => {
1013
+ expect($('.ons-u-vh').text()).toBe('A detailed description');
1014
+ });
1015
+
1016
+ test('THEN: it still renders the caption', () => {
1017
+ expect($('figcaption').text()).toBe('A detailed caption');
1018
+ });
1019
+
1020
+ test('THEN: it still renders the download', () => {
1021
+ expect($('.ons-chart__download-title').text()).toBe('Download this chart');
1022
+ });
1023
+ });
1024
+ });
1025
+ });
857
1026
  });
@@ -16,7 +16,7 @@ class AnnotationsOptions {
16
16
  padding: 3,
17
17
  style: {
18
18
  color: this.constants.labelColor,
19
- fontSize: this.constants.desktopFontSize,
19
+ fontSize: this.constants.defaultFontSize,
20
20
  width: 150,
21
21
  textAlign: 'left',
22
22
  },
@@ -0,0 +1,26 @@
1
+ class AreaChart {
2
+ getAreaChartOptions = () => {
3
+ return {
4
+ legend: {
5
+ symbolWidth: 12,
6
+ symbolHeight: 12,
7
+ },
8
+ plotOptions: {
9
+ area: {
10
+ fillOpacity: 1,
11
+ // Use a circle instead of the default blob plus line icon
12
+ // 'rectangle' counterintuitively gives a circle, because the legend icon has a border radius of half it's height by default
13
+ legendSymbol: 'rectangle',
14
+ stacking: 'normal',
15
+ },
16
+ series: {
17
+ marker: {
18
+ enabled: false,
19
+ },
20
+ },
21
+ },
22
+ };
23
+ };
24
+ }
25
+
26
+ export default AreaChart;
@@ -26,7 +26,7 @@ class BarChart {
26
26
  // The design system does not include a semibold font weight, so we use 700 (bold) as an alternative.
27
27
  fontWeight: '700',
28
28
  color: this.constants.labelColor,
29
- fontSize: this.constants.mobileFontSize,
29
+ fontSize: this.constants.defaultFontSize,
30
30
  },
31
31
  },
32
32
  },
@@ -119,7 +119,7 @@ class BarChart {
119
119
  // The design system does not include a semibold font weight, so we use 700 (bold) as an alternative.
120
120
  fontWeight: '700',
121
121
  color: this.constants.labelColor,
122
- fontSize: this.constants.mobileFontSize,
122
+ fontSize: this.constants.defaultFontSize,
123
123
  },
124
124
  });
125
125
 
@@ -9,9 +9,90 @@ class ChartConstants {
9
9
  categoryLabelColor: '#414042',
10
10
  gridLineColor: '#d9d9d9',
11
11
  zeroLineColor: '#b3b3b3',
12
- // Responsive font sizes
13
- mobileFontSize: '0.75rem', // 12px
14
- desktopFontSize: '0.875rem', // 14px
12
+ defaultFontSize: '0.875rem', // 14px
13
+ lineMarkerStyles: [
14
+ {
15
+ radius: 4,
16
+ symbol: 'circle',
17
+ },
18
+ {
19
+ radius: 4,
20
+ symbol: 'square',
21
+ },
22
+ {
23
+ radius: 5,
24
+ symbol: 'diamond',
25
+ },
26
+ {
27
+ radius: 5,
28
+ symbol: 'triangle',
29
+ },
30
+ {
31
+ radius: 5,
32
+ symbol: 'triangle-down',
33
+ },
34
+ {
35
+ radius: 4,
36
+ symbol: 'circle',
37
+ fillColor: 'white',
38
+ lineWidth: 2.5,
39
+ lineColor: null,
40
+ },
41
+ {
42
+ radius: 4,
43
+ symbol: 'square',
44
+ fillColor: 'white',
45
+ lineWidth: 2.5,
46
+ lineColor: null,
47
+ },
48
+ {
49
+ radius: 5,
50
+ symbol: 'diamond',
51
+ fillColor: 'white',
52
+ lineWidth: 2.5,
53
+ lineColor: null,
54
+ },
55
+ {
56
+ radius: 5,
57
+ symbol: 'triangle',
58
+ fillColor: 'white',
59
+ lineWidth: 2.5,
60
+ lineColor: null,
61
+ },
62
+ {
63
+ radius: 5,
64
+ symbol: 'triangle-down',
65
+ fillColor: 'white',
66
+ lineWidth: 2.5,
67
+ lineColor: null,
68
+ },
69
+ ],
70
+ scatterMarkerStyles: [
71
+ {
72
+ radius: 5,
73
+ symbol: 'circle',
74
+ lineWidth: 1,
75
+ lineColor: '#ffffff',
76
+ },
77
+ {
78
+ radius: 5,
79
+ symbol: 'square',
80
+ lineWidth: 1,
81
+ lineColor: '#ffffff',
82
+ },
83
+ {
84
+ radius: 6,
85
+ symbol: 'diamond',
86
+ lineWidth: 1,
87
+ lineColor: '#ffffff',
88
+ },
89
+ {
90
+ radius: 6,
91
+ symbol: 'triangle',
92
+ lineWidth: 1,
93
+ lineColor: '#ffffff',
94
+ },
95
+ ],
15
96
  };
16
97
 
17
98
  return constants;
@@ -7,7 +7,9 @@ import SpecificChartOptions from './specific-chart-options';
7
7
  import LineChart from './line-chart';
8
8
  import BarChart from './bar-chart';
9
9
  import ColumnChart from './column-chart';
10
+ import ScatterChart from './scatter-chart';
10
11
  import AnnotationsOptions from './annotations-options';
12
+ import AreaChart from './area-chart';
11
13
 
12
14
  class HighchartsBaseChart {
13
15
  static selector() {
@@ -19,6 +21,10 @@ class HighchartsBaseChart {
19
21
  this.chartType = this.node.dataset.highchartsType;
20
22
  this.theme = this.node.dataset.highchartsTheme;
21
23
  const chartNode = this.node.querySelector('[data-highcharts-chart]');
24
+ if (!chartNode) {
25
+ console.error('No chart node found');
26
+ return;
27
+ }
22
28
  this.id = this.node.dataset.highchartsId;
23
29
  this.useStackedLayout = this.node.hasAttribute('data-highcharts-use-stacked-layout');
24
30
  this.config = JSON.parse(this.node.querySelector(`[data-highcharts-config--${this.id}]`).textContent);
@@ -37,6 +43,8 @@ class HighchartsBaseChart {
37
43
  this.lineChart = new LineChart();
38
44
  this.barChart = new BarChart();
39
45
  this.columnChart = new ColumnChart();
46
+ this.areaChart = new AreaChart();
47
+ this.scatterChart = new ScatterChart();
40
48
  this.extraLines = this.checkForExtraLines();
41
49
  if (window.isCommonChartOptionsDefined === undefined) {
42
50
  this.setCommonChartOptions();
@@ -100,7 +108,9 @@ class HighchartsBaseChart {
100
108
  const specificChartOptions = this.specificChartOptions.getOptions();
101
109
  const lineChartOptions = this.lineChart.getLineChartOptions();
102
110
  const barChartOptions = this.barChart.getBarChartOptions(this.useStackedLayout);
103
- const columnChartOptions = this.columnChart.getColumnChartOptions(this.useStackedLayout);
111
+ const columnChartOptions = this.columnChart.getColumnChartOptions(this.config, this.useStackedLayout, this.extraLines);
112
+ const areaChartOptions = this.areaChart.getAreaChartOptions();
113
+ const scatterChartOptions = this.scatterChart.getScatterChartOptions();
104
114
  // Merge specificChartOptions with the existing config
105
115
  this.config = this.mergeConfigs(this.config, specificChartOptions);
106
116
 
@@ -117,6 +127,14 @@ class HighchartsBaseChart {
117
127
  // Merge the column chart options with the existing config
118
128
  this.config = this.mergeConfigs(this.config, columnChartOptions);
119
129
  }
130
+ if (this.chartType === 'area') {
131
+ // Merge the area chart options with the existing config
132
+ this.config = this.mergeConfigs(this.config, areaChartOptions);
133
+ }
134
+ if (this.chartType === 'scatter') {
135
+ // Merge the scatter chart options with the existing config
136
+ this.config = this.mergeConfigs(this.config, scatterChartOptions);
137
+ }
120
138
 
121
139
  if (this.extraLines > 0) {
122
140
  this.config = this.mergeConfigs(this.config, this.lineChart.getLineChartOptions());
@@ -142,27 +160,34 @@ class HighchartsBaseChart {
142
160
  // Note this is not the same as the viewport width
143
161
  // All responsive rules should be defined here to avoid overriding existing rules
144
162
  setResponsiveOptions = () => {
145
- const mobileCommonChartOptions = this.commonChartOptions.getMobileOptions(
146
- this.xAxisTickIntervalMobile,
147
- this.yAxisTickIntervalMobile,
148
- );
163
+ let mobileChartOptions = this.commonChartOptions.getMobileOptions(this.xAxisTickIntervalMobile, this.yAxisTickIntervalMobile);
164
+ if (this.chartType === 'column') {
165
+ const mobileColumnChartOptions = this.columnChart.getColumnChartMobileOptions(
166
+ this.config,
167
+ this.useStackedLayout,
168
+ this.extraLines,
169
+ );
170
+ mobileChartOptions = this.mergeConfigs(mobileChartOptions, mobileColumnChartOptions);
171
+ }
172
+
149
173
  if (!this.config.responsive) {
150
174
  this.config.responsive = {};
151
175
  }
152
- // If these conditions change, the styling for the footnotes container query in _chart.scss needs to be updated
176
+
153
177
  let rules = [
154
178
  {
155
179
  condition: {
156
180
  maxWidth: 400,
157
181
  },
158
182
  chartOptions: {
159
- ...mobileCommonChartOptions,
183
+ ...mobileChartOptions,
160
184
  },
161
185
  },
162
- // We are using a slightly wider breakpoint for annotations
163
- // to try and reduce the likelihood of them being automatically
164
- // hidden by Highcharts
165
186
  {
187
+ // If these conditions change, the styling for the footnotes container query in _chart.scss needs to be updated
188
+ // We are using a slightly wider breakpoint for annotations
189
+ // to try and reduce the likelihood of them being automatically
190
+ // hidden by Highcharts
166
191
  condition: {
167
192
  maxWidth: 600,
168
193
  },
@@ -203,9 +228,11 @@ class HighchartsBaseChart {
203
228
  }
204
229
  }
205
230
  if (this.chartType === 'column') {
206
- this.columnChart.updatePointPadding(this.config, currentChart, this.useStackedLayout, this.extraLines);
207
231
  this.commonChartOptions.hideDataLabels(currentChart.series);
208
232
  }
233
+ if (this.chartType === 'scatter') {
234
+ this.scatterChart.updateMarkers(currentChart);
235
+ }
209
236
  if (this.chartType != 'bar') {
210
237
  this.commonChartOptions.adjustChartHeight(currentChart, this.percentageHeightDesktop, this.percentageHeightMobile);
211
238
  }
@@ -215,7 +242,6 @@ class HighchartsBaseChart {
215
242
  if (this.extraLines > 0) {
216
243
  currentChart.series.forEach((series) => {
217
244
  if (series.type === 'line') {
218
- this.lineChart.updateLastPointMarker([series]);
219
245
  this.commonChartOptions.hideDataLabels([series]);
220
246
  }
221
247
  });
@@ -1,10 +1,9 @@
1
1
  class ColumnChart {
2
- getColumnChartOptions = (useStackedLayout) => {
2
+ getColumnChartOptions = (config, useStackedLayout, extraLines) => {
3
3
  return {
4
4
  plotOptions: {
5
5
  column: {
6
- pointPadding: 0,
7
- groupPadding: 0,
6
+ ...this.getPointPadding(config, useStackedLayout, extraLines, false),
8
7
  borderRadius: 0,
9
8
  borderWidth: 0,
10
9
  },
@@ -15,33 +14,43 @@ class ColumnChart {
15
14
  };
16
15
  };
17
16
 
18
- // Set the point padding between each bar to be 3% (an overall gap of 6%)
19
- // For charts with fewer than 5 categories, we use a wider point padding of 4% (8% gap between bars)
20
- // For cluster charts we use 0 for the point padding and a group padding of 4% (8% gap between bars)
21
- updatePointPadding = (config, currentChart, stackedLayout, numberOfExtraLines) => {
22
- const numberOfCategories = config.xAxis.categories.length;
23
- const numberOfSeries = currentChart.series.length - numberOfExtraLines; // Get number of column series
17
+ getColumnChartMobileOptions = (config, useStackedLayout, extraLines) => {
18
+ return {
19
+ plotOptions: {
20
+ column: {
21
+ ...this.getPointPadding(config, useStackedLayout, extraLines, true),
22
+ },
23
+ },
24
+ };
25
+ };
26
+
27
+ // Set spacing between bars based on screen size and number of categories:
28
+ // - For charts with enough categories (≥ 10 on mobile, ≥ 20 on desktop), use 10% spacing (0.1), i.e. 20% total gap between bars
29
+ // - For charts with fewer categories, use 20% spacing (0.2), i.e. 40% total gap
30
+ // - For non-clustered or stacked charts, spacing is applied as pointPadding
31
+ // - For clustered charts, spacing is applied as groupPadding
32
+ // - Max bar width: 75px (desktop), 55px (mobile)
33
+ getPointPadding = (config, stackedLayout, numberOfExtraLines, isMobile) => {
34
+ const numberOfCategories = config.xAxis.categories ? config.xAxis.categories.length : 0;
35
+ const numberOfSeries = config.series.length - numberOfExtraLines; // Get number of column series
36
+
37
+ const categoryThreshold = isMobile ? 10 : 20;
38
+ const maxPointWidth = isMobile ? 55 : 75;
39
+
24
40
  let pointPadding = 0;
25
41
  let groupPadding = 0;
42
+ let spacing = numberOfCategories >= categoryThreshold ? 0.1 : 0.2;
43
+
26
44
  // non-clustered charts or stacked charts
27
45
  if (numberOfSeries === 1 || stackedLayout === true) {
28
- if (numberOfCategories > 5) {
29
- pointPadding = 0.03;
30
- } else {
31
- pointPadding = 0.04;
32
- }
33
- } else {
34
- // clustered charts
35
- groupPadding = 0.04;
46
+ pointPadding = spacing;
47
+ }
48
+ // clustered charts
49
+ else {
50
+ groupPadding = spacing;
36
51
  }
37
52
 
38
- // update the point width and padding
39
- currentChart.series.forEach((series) => {
40
- series.update({
41
- pointPadding: pointPadding,
42
- groupPadding: groupPadding,
43
- });
44
- });
53
+ return { pointPadding: pointPadding, groupPadding: groupPadding, maxPointWidth: maxPointWidth };
45
54
  };
46
55
  }
47
56