@ons/design-system 72.5.0 → 72.8.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.
- package/components/breadcrumbs/_breadcrumbs.scss +1 -0
- package/components/chart/_chart.scss +56 -0
- package/components/chart/_macro.njk +43 -5
- package/components/chart/_macro.spec.js +420 -0
- package/components/chart/annotations-options.js +78 -0
- package/components/chart/bar-chart.js +15 -2
- package/components/chart/chart.js +119 -27
- package/components/chart/column-chart.js +2 -2
- package/components/chart/common-chart-options.js +104 -51
- package/components/chart/example-bar-chart-with-annotations.njk +62 -0
- package/components/chart/example-bar-chart.njk +1 -0
- package/components/chart/example-bar-with-line-chart.njk +64 -0
- package/components/chart/example-clustered-column-chart.njk +5 -2
- package/components/chart/example-column-chart-with-annotations.njk +63 -0
- package/components/chart/example-column-chart.njk +5 -2
- package/components/chart/example-column-with-line-chart.njk +63 -0
- package/components/chart/example-line-chart-with-annotations.njk +235 -0
- package/components/chart/example-line-chart.njk +7 -3
- package/components/chart/example-stacked-column-chart.njk +3 -1
- package/components/chart/line-chart.js +2 -7
- package/components/details/details.js +5 -0
- package/components/details-panel/_details-panel.scss +107 -0
- package/components/details-panel/_macro.njk +41 -0
- package/components/details-panel/_macro.spec.js +54 -0
- package/components/details-panel/example-details-panel-open.njk +29 -0
- package/components/details-panel/example-details-panel.njk +28 -0
- package/components/hero/_hero.scss +101 -43
- package/components/hero/_macro.njk +21 -10
- package/components/hero/_macro.spec.js +94 -0
- package/components/hero/example-hero-dark.njk +1 -1
- package/components/hero/example-hero-grey.njk +8 -0
- package/components/icon/_macro.njk +1 -1
- package/components/pagination/_macro.njk +1 -1
- package/components/pagination/_pagination.scss +6 -0
- package/components/summary/_macro.njk +2 -2
- package/components/summary/_macro.spec.js +43 -21
- package/components/summary/_summary.scss +5 -1
- package/components/summary/example-summary-grouped.njk +0 -12
- package/components/table-of-contents/_macro.njk +40 -0
- package/components/table-of-contents/_macro.spec.js +72 -0
- package/components/table-of-contents/_table-of-contents.scss +11 -0
- package/components/table-of-contents/example-table-of-contents-related-links-with-button.njk +60 -0
- package/css/main.css +1 -1
- package/js/cookies-functions.js +11 -6
- package/js/cookies-functions.spec.js +44 -0
- package/package.json +1 -1
- package/scripts/main.es5.js +1 -1
- package/scripts/main.js +1 -1
- package/scss/main.scss +1 -0
- package/scss/vars/_colors.scss +1 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import ChartConstants from './chart-constants';
|
|
2
|
+
|
|
3
|
+
class AnnotationsOptions {
|
|
4
|
+
constructor(annotations) {
|
|
5
|
+
this.constants = ChartConstants.constants();
|
|
6
|
+
this.annotations = annotations;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
getAnnotationsOptionsDesktop = () => {
|
|
10
|
+
let annotations = [
|
|
11
|
+
{
|
|
12
|
+
labels: [],
|
|
13
|
+
labelOptions: {
|
|
14
|
+
shape: 'connector',
|
|
15
|
+
borderColor: this.constants.labelColor,
|
|
16
|
+
padding: 3,
|
|
17
|
+
style: {
|
|
18
|
+
color: this.constants.labelColor,
|
|
19
|
+
fontSize: this.constants.desktopFontSize,
|
|
20
|
+
width: 150,
|
|
21
|
+
textAlign: 'left',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
draggable: '',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
this.annotations.forEach((annotation) => {
|
|
28
|
+
annotations[0].labels.push({
|
|
29
|
+
point: {
|
|
30
|
+
x: annotation.point.x,
|
|
31
|
+
y: annotation.point.y,
|
|
32
|
+
xAxis: 0,
|
|
33
|
+
yAxis: 0,
|
|
34
|
+
},
|
|
35
|
+
text: annotation.text,
|
|
36
|
+
x: annotation.labelOffsetX,
|
|
37
|
+
y: annotation.labelOffsetY,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
return annotations;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
getAnnotationsOptionsMobile = () => {
|
|
44
|
+
let annotations = [
|
|
45
|
+
{
|
|
46
|
+
labels: [],
|
|
47
|
+
labelOptions: {
|
|
48
|
+
backgroundColor: 'transparent',
|
|
49
|
+
borderColor: 'transparent',
|
|
50
|
+
// We use css styling for the rounded number annotation at mobile
|
|
51
|
+
useHTML: true,
|
|
52
|
+
className: 'ons-chart__footnote-number',
|
|
53
|
+
},
|
|
54
|
+
draggable: '',
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
this.annotations.forEach((annotation, index) => {
|
|
58
|
+
annotations[0].labels.push({
|
|
59
|
+
point: {
|
|
60
|
+
x: annotation.point.x,
|
|
61
|
+
y: annotation.point.y,
|
|
62
|
+
xAxis: 0,
|
|
63
|
+
yAxis: 0,
|
|
64
|
+
},
|
|
65
|
+
text: index + 1,
|
|
66
|
+
x: 0,
|
|
67
|
+
y: 0,
|
|
68
|
+
// Ensures the full label is read out by screen readers
|
|
69
|
+
accessibility: {
|
|
70
|
+
description: annotation.text,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
return annotations;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default AnnotationsOptions;
|
|
@@ -40,6 +40,7 @@ class BarChart {
|
|
|
40
40
|
style: {
|
|
41
41
|
color: this.constants.categoryLabelColor,
|
|
42
42
|
},
|
|
43
|
+
useHTML: false,
|
|
43
44
|
},
|
|
44
45
|
// remove the tick marks for bar charts
|
|
45
46
|
tickWidth: 0,
|
|
@@ -48,6 +49,14 @@ class BarChart {
|
|
|
48
49
|
title: { align: 'high', textAlign: 'middle', reserveSpace: false, rotation: 0, y: -25, useHTML: true },
|
|
49
50
|
},
|
|
50
51
|
yAxis: {
|
|
52
|
+
labels: {
|
|
53
|
+
rotation: 0,
|
|
54
|
+
useHTML: true,
|
|
55
|
+
style: {
|
|
56
|
+
whiteSpace: 'nowrap',
|
|
57
|
+
color: this.constants.categoryLabelColor,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
51
60
|
title: {
|
|
52
61
|
// Override the y Axis title settings for bar charts where the y axis is horizontal
|
|
53
62
|
textAlign: 'right',
|
|
@@ -76,6 +85,10 @@ class BarChart {
|
|
|
76
85
|
// Get the actual width of the data label
|
|
77
86
|
const labelWidth = point.dataLabel && point.dataLabel.getBBox().width;
|
|
78
87
|
// Move the data labels inside the bar if the bar is wider than the label plus some padding
|
|
88
|
+
if (series.type == 'line') {
|
|
89
|
+
// If we have a bar chart with an extra line, exit early for the line series
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
79
92
|
if (point.shapeArgs.height > labelWidth + 20) {
|
|
80
93
|
point.update(insideOptions, false);
|
|
81
94
|
} else {
|
|
@@ -112,9 +125,9 @@ class BarChart {
|
|
|
112
125
|
|
|
113
126
|
// This updates the height of the vertical axis and overall chart to fit the number of categories
|
|
114
127
|
// Note that the vertical axis on a bar chart is the x axis
|
|
115
|
-
updateBarChartHeight = (config, currentChart, useStackedLayout) => {
|
|
128
|
+
updateBarChartHeight = (config, currentChart, useStackedLayout, numberOfExtraLines) => {
|
|
116
129
|
const numberOfCategories = config.xAxis.categories.length;
|
|
117
|
-
const numberOfSeries = currentChart.series.length; // Get number of bar series
|
|
130
|
+
const numberOfSeries = currentChart.series.length - numberOfExtraLines; // Get number of bar series
|
|
118
131
|
let barHeight = 30; // Height of each individual bar - set in bar-chart-plot-options
|
|
119
132
|
let groupSpacing = 0; // Space we want between category groups, or between series groups for cluster charts
|
|
120
133
|
let categoriesTotalHeight = 0;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import Highcharts from 'highcharts';
|
|
2
|
+
import 'highcharts/modules/accessibility';
|
|
3
|
+
import 'highcharts/modules/annotations';
|
|
4
|
+
|
|
1
5
|
import CommonChartOptions from './common-chart-options';
|
|
2
6
|
import SpecificChartOptions from './specific-chart-options';
|
|
3
7
|
import LineChart from './line-chart';
|
|
4
8
|
import BarChart from './bar-chart';
|
|
5
9
|
import ColumnChart from './column-chart';
|
|
6
|
-
import
|
|
7
|
-
import 'highcharts/modules/accessibility';
|
|
10
|
+
import AnnotationsOptions from './annotations-options';
|
|
8
11
|
|
|
9
12
|
class HighchartsBaseChart {
|
|
10
13
|
static selector() {
|
|
@@ -16,27 +19,42 @@ class HighchartsBaseChart {
|
|
|
16
19
|
this.chartType = this.node.dataset.highchartsType;
|
|
17
20
|
this.theme = this.node.dataset.highchartsTheme;
|
|
18
21
|
const chartNode = this.node.querySelector('[data-highcharts-chart]');
|
|
19
|
-
|
|
20
22
|
this.id = this.node.dataset.highchartsId;
|
|
21
23
|
this.useStackedLayout = this.node.hasAttribute('data-highcharts-use-stacked-layout');
|
|
22
24
|
this.config = JSON.parse(this.node.querySelector(`[data-highcharts-config--${this.id}]`).textContent);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
if (this.node.querySelector(`[data-highcharts-annotations--${this.id}]`)) {
|
|
26
|
+
const annotations = JSON.parse(this.node.querySelector(`[data-highcharts-annotations--${this.id}]`).textContent);
|
|
27
|
+
this.annotationsOptions = new AnnotationsOptions(annotations);
|
|
28
|
+
}
|
|
29
|
+
this.percentageHeightDesktop = this.node.dataset.highchartsPercentageHeightDesktop;
|
|
30
|
+
this.percentageHeightMobile = this.node.dataset.highchartsPercentageHeightMobile;
|
|
31
|
+
this.xAxisTickIntervalMobile = parseInt(this.node.dataset.highchartsXAxisTickIntervalMobile);
|
|
32
|
+
this.xAxisTickIntervalDesktop = parseInt(this.node.dataset.highchartsXAxisTickIntervalDesktop);
|
|
33
|
+
this.yAxisTickIntervalMobile = parseInt(this.node.dataset.highchartsYAxisTickIntervalMobile);
|
|
34
|
+
this.yAxisTickIntervalDesktop = parseInt(this.node.dataset.highchartsYAxisTickIntervalDesktop);
|
|
35
|
+
this.commonChartOptions = new CommonChartOptions(this.xAxisTickIntervalDesktop, this.yAxisTickIntervalDesktop);
|
|
25
36
|
this.specificChartOptions = new SpecificChartOptions(this.theme, this.chartType, this.config);
|
|
26
37
|
this.lineChart = new LineChart();
|
|
27
38
|
this.barChart = new BarChart();
|
|
28
39
|
this.columnChart = new ColumnChart();
|
|
40
|
+
this.extraLines = this.checkForExtraLines();
|
|
29
41
|
if (window.isCommonChartOptionsDefined === undefined) {
|
|
30
42
|
this.setCommonChartOptions();
|
|
31
43
|
window.isCommonChartOptionsDefined = true;
|
|
32
44
|
}
|
|
33
45
|
this.hideDataLabels = this.checkHideDataLabels();
|
|
34
46
|
this.setSpecificChartOptions();
|
|
47
|
+
this.setResponsiveOptions();
|
|
35
48
|
this.setLoadEvent();
|
|
36
49
|
this.setWindowResizeEvent();
|
|
37
50
|
this.chart = Highcharts.chart(chartNode, this.config);
|
|
38
51
|
}
|
|
39
52
|
|
|
53
|
+
// Check for the number of extra line series in the config
|
|
54
|
+
checkForExtraLines = () => {
|
|
55
|
+
return this.chartType === 'line' ? 0 : this.config.series.filter((series) => series.type === 'line').length;
|
|
56
|
+
};
|
|
57
|
+
|
|
40
58
|
// Set up the global Highcharts options which are used for all charts
|
|
41
59
|
setCommonChartOptions = () => {
|
|
42
60
|
const chartOptions = this.commonChartOptions.getOptions();
|
|
@@ -99,6 +117,19 @@ class HighchartsBaseChart {
|
|
|
99
117
|
// Merge the column chart options with the existing config
|
|
100
118
|
this.config = this.mergeConfigs(this.config, columnChartOptions);
|
|
101
119
|
}
|
|
120
|
+
|
|
121
|
+
if (this.extraLines > 0) {
|
|
122
|
+
this.config = this.mergeConfigs(this.config, this.lineChart.getLineChartOptions());
|
|
123
|
+
if (this.chartType === 'column') {
|
|
124
|
+
this.config = this.mergeConfigs(this.config, columnChartOptions);
|
|
125
|
+
}
|
|
126
|
+
if (this.chartType === 'bar') {
|
|
127
|
+
this.config = this.mergeConfigs(this.config, barChartOptions);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Disable the legend for single series charts
|
|
132
|
+
this.commonChartOptions.disableLegendForSingleSeries(this.config);
|
|
102
133
|
};
|
|
103
134
|
|
|
104
135
|
// Check if the data labels should be hidden
|
|
@@ -107,50 +138,111 @@ class HighchartsBaseChart {
|
|
|
107
138
|
return (this.chartType === 'bar' && this.config.series.length > 2) || this.useStackedLayout === true;
|
|
108
139
|
};
|
|
109
140
|
|
|
141
|
+
// Adjust font size and annotations for smaller width of chart
|
|
142
|
+
// Note this is not the same as the viewport width
|
|
143
|
+
// All responsive rules should be defined here to avoid overriding existing rules
|
|
144
|
+
setResponsiveOptions = () => {
|
|
145
|
+
const mobileCommonChartOptions = this.commonChartOptions.getMobileOptions(
|
|
146
|
+
this.xAxisTickIntervalMobile,
|
|
147
|
+
this.yAxisTickIntervalMobile,
|
|
148
|
+
);
|
|
149
|
+
if (!this.config.responsive) {
|
|
150
|
+
this.config.responsive = {};
|
|
151
|
+
}
|
|
152
|
+
// If these conditions change, the styling for the footnotes container query in _chart.scss needs to be updated
|
|
153
|
+
let rules = [
|
|
154
|
+
{
|
|
155
|
+
condition: {
|
|
156
|
+
maxWidth: 400,
|
|
157
|
+
},
|
|
158
|
+
chartOptions: {
|
|
159
|
+
...mobileCommonChartOptions,
|
|
160
|
+
},
|
|
161
|
+
},
|
|
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
|
+
{
|
|
166
|
+
condition: {
|
|
167
|
+
maxWidth: 600,
|
|
168
|
+
},
|
|
169
|
+
chartOptions: {
|
|
170
|
+
annotations: this.annotationsOptions ? this.annotationsOptions.getAnnotationsOptionsMobile() : undefined,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
condition: {
|
|
175
|
+
minWidth: 601,
|
|
176
|
+
},
|
|
177
|
+
chartOptions: {
|
|
178
|
+
annotations: this.annotationsOptions ? this.annotationsOptions.getAnnotationsOptionsDesktop() : undefined,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
this.config.responsive.rules = rules;
|
|
183
|
+
};
|
|
184
|
+
|
|
110
185
|
// Create the load event for various chart types
|
|
186
|
+
// All load events should be defined here to avoid overriding existing events
|
|
111
187
|
setLoadEvent = () => {
|
|
112
188
|
if (!this.config.chart.events) {
|
|
113
189
|
this.config.chart.events = {};
|
|
114
190
|
}
|
|
115
191
|
this.config.chart.events.load = (event) => {
|
|
116
192
|
const currentChart = event.target;
|
|
117
|
-
// Disable the legend for single series charts
|
|
118
|
-
this.commonChartOptions.disableLegendForSingleSeries(currentChart);
|
|
119
193
|
if (this.chartType === 'line') {
|
|
120
|
-
this.lineChart.updateLastPointMarker(currentChart);
|
|
121
|
-
this.commonChartOptions.hideDataLabels(currentChart);
|
|
194
|
+
this.lineChart.updateLastPointMarker(currentChart.series);
|
|
195
|
+
this.commonChartOptions.hideDataLabels(currentChart.series);
|
|
122
196
|
}
|
|
123
197
|
if (this.chartType === 'bar') {
|
|
124
|
-
this.barChart.updateBarChartHeight(this.config, currentChart, this.useStackedLayout);
|
|
198
|
+
this.barChart.updateBarChartHeight(this.config, currentChart, this.useStackedLayout, this.extraLines);
|
|
125
199
|
if (!this.hideDataLabels) {
|
|
126
200
|
this.barChart.postLoadDataLabels(currentChart);
|
|
127
201
|
} else {
|
|
128
|
-
this.commonChartOptions.hideDataLabels(currentChart);
|
|
202
|
+
this.commonChartOptions.hideDataLabels(currentChart.series);
|
|
129
203
|
}
|
|
130
204
|
}
|
|
131
205
|
if (this.chartType === 'column') {
|
|
132
|
-
this.columnChart.updatePointPadding(this.config, currentChart, this.useStackedLayout);
|
|
133
|
-
this.commonChartOptions.hideDataLabels(currentChart);
|
|
206
|
+
this.columnChart.updatePointPadding(this.config, currentChart, this.useStackedLayout, this.extraLines);
|
|
207
|
+
this.commonChartOptions.hideDataLabels(currentChart.series);
|
|
208
|
+
}
|
|
209
|
+
if (this.chartType != 'bar') {
|
|
210
|
+
this.commonChartOptions.adjustChartHeight(currentChart, this.percentageHeightDesktop, this.percentageHeightMobile);
|
|
134
211
|
}
|
|
212
|
+
|
|
213
|
+
// If the chart has an extra line or lines, hide the data labels for
|
|
214
|
+
// that series, update the last point marker
|
|
215
|
+
if (this.extraLines > 0) {
|
|
216
|
+
currentChart.series.forEach((series) => {
|
|
217
|
+
if (series.type === 'line') {
|
|
218
|
+
this.lineChart.updateLastPointMarker([series]);
|
|
219
|
+
this.commonChartOptions.hideDataLabels([series]);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
// Update the legend items for all charts
|
|
224
|
+
this.commonChartOptions.updateLegendSymbols(currentChart);
|
|
135
225
|
currentChart.redraw(false);
|
|
136
226
|
};
|
|
137
227
|
};
|
|
138
228
|
|
|
139
|
-
// Set resize events - throttled to
|
|
229
|
+
// Set resize events - throttled to 50ms
|
|
230
|
+
// All resize events should be defined here to avoid overriding existing events
|
|
140
231
|
setWindowResizeEvent = () => {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
232
|
+
window.addEventListener('resize', () => {
|
|
233
|
+
clearTimeout(this.resizeTimeout);
|
|
234
|
+
this.resizeTimeout = setTimeout(() => {
|
|
235
|
+
// Get the current rendered chart instance
|
|
236
|
+
const currentChart = Highcharts.charts.find((chart) => chart && chart.container === this.chart.container);
|
|
237
|
+
// Update the data labels when the window is resized
|
|
238
|
+
if (this.chartType === 'bar' && !this.hideDataLabels) {
|
|
239
|
+
this.barChart.postLoadDataLabels(currentChart);
|
|
240
|
+
}
|
|
241
|
+
if (this.chartType != 'bar') {
|
|
242
|
+
this.commonChartOptions.adjustChartHeight(currentChart, this.percentageHeightDesktop, this.percentageHeightMobile);
|
|
243
|
+
}
|
|
244
|
+
}, 50);
|
|
245
|
+
});
|
|
154
246
|
};
|
|
155
247
|
}
|
|
156
248
|
|
|
@@ -18,9 +18,9 @@ class ColumnChart {
|
|
|
18
18
|
// Set the point padding between each bar to be 3% (an overall gap of 6%)
|
|
19
19
|
// For charts with fewer than 5 categories, we use a wider point padding of 4% (8% gap between bars)
|
|
20
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) => {
|
|
21
|
+
updatePointPadding = (config, currentChart, stackedLayout, numberOfExtraLines) => {
|
|
22
22
|
const numberOfCategories = config.xAxis.categories.length;
|
|
23
|
-
const numberOfSeries = currentChart.series.length; // Get number of
|
|
23
|
+
const numberOfSeries = currentChart.series.length - numberOfExtraLines; // Get number of column series
|
|
24
24
|
let pointPadding = 0;
|
|
25
25
|
let groupPadding = 0;
|
|
26
26
|
// non-clustered charts or stacked charts
|
|
@@ -2,7 +2,7 @@ import ChartConstants from './chart-constants';
|
|
|
2
2
|
|
|
3
3
|
// Options that are common to all chart types - these are set once in the Highcharts.setOptions() method
|
|
4
4
|
class CommonChartOptions {
|
|
5
|
-
constructor() {
|
|
5
|
+
constructor(xAxisTickInterval, yAxisTickInterval) {
|
|
6
6
|
this.constants = ChartConstants.constants();
|
|
7
7
|
|
|
8
8
|
this.options = {
|
|
@@ -17,10 +17,15 @@ class CommonChartOptions {
|
|
|
17
17
|
align: 'left',
|
|
18
18
|
verticalAlign: 'top',
|
|
19
19
|
layout: 'horizontal',
|
|
20
|
-
// Symbol width and height in
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
// Symbol width and height are set in a postLoad event, depending on the series type
|
|
21
|
+
// Default to the line width to ensure there is enough space for the overall legend item for line symbols
|
|
22
|
+
symbolWidth: 20,
|
|
23
23
|
margin: 50,
|
|
24
|
+
navigation: {
|
|
25
|
+
// ensures that when the legend is long, there is no pagination or scrollbar
|
|
26
|
+
enabled: false,
|
|
27
|
+
},
|
|
28
|
+
itemDistance: 30,
|
|
24
29
|
itemHoverStyle: {
|
|
25
30
|
color: this.constants.labelColor, // Prevents the text from changing color on hover
|
|
26
31
|
},
|
|
@@ -91,9 +96,12 @@ class CommonChartOptions {
|
|
|
91
96
|
tickWidth: 1,
|
|
92
97
|
tickLength: 6,
|
|
93
98
|
tickColor: this.constants.gridLineColor,
|
|
99
|
+
tickInterval: yAxisTickInterval,
|
|
94
100
|
},
|
|
95
101
|
xAxis: {
|
|
96
102
|
labels: {
|
|
103
|
+
useHTML: true,
|
|
104
|
+
rotation: 0,
|
|
97
105
|
style: {
|
|
98
106
|
color: this.constants.axisLabelColor,
|
|
99
107
|
fontSize: this.constants.desktopFontSize,
|
|
@@ -112,6 +120,7 @@ class CommonChartOptions {
|
|
|
112
120
|
tickWidth: 1,
|
|
113
121
|
tickLength: 6,
|
|
114
122
|
tickColor: this.constants.gridLineColor,
|
|
123
|
+
tickInterval: xAxisTickInterval,
|
|
115
124
|
},
|
|
116
125
|
plotOptions: {
|
|
117
126
|
series: {
|
|
@@ -131,55 +140,49 @@ class CommonChartOptions {
|
|
|
131
140
|
},
|
|
132
141
|
},
|
|
133
142
|
},
|
|
134
|
-
// Adjust font size for smaller width of chart
|
|
135
|
-
// Note this is not the same as the viewport width
|
|
136
|
-
responsive: {
|
|
137
|
-
rules: [
|
|
138
|
-
{
|
|
139
|
-
condition: {
|
|
140
|
-
maxWidth: 400,
|
|
141
|
-
},
|
|
142
|
-
chartOptions: {
|
|
143
|
-
legend: {
|
|
144
|
-
itemStyle: {
|
|
145
|
-
fontSize: this.constants.mobileFontSize,
|
|
146
|
-
},
|
|
147
|
-
},
|
|
148
|
-
xAxis: {
|
|
149
|
-
labels: {
|
|
150
|
-
style: {
|
|
151
|
-
fontSize: this.constants.mobileFontSize,
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
title: {
|
|
155
|
-
style: {
|
|
156
|
-
fontSize: this.constants.mobileFontSize,
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
yAxis: {
|
|
161
|
-
labels: {
|
|
162
|
-
style: {
|
|
163
|
-
fontSize: this.constants.mobileFontSize,
|
|
164
|
-
},
|
|
165
|
-
},
|
|
166
|
-
title: {
|
|
167
|
-
style: {
|
|
168
|
-
fontSize: this.constants.mobileFontSize,
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
],
|
|
175
|
-
},
|
|
176
143
|
};
|
|
177
144
|
}
|
|
178
145
|
|
|
179
146
|
getOptions = () => this.options;
|
|
180
147
|
|
|
181
|
-
|
|
182
|
-
|
|
148
|
+
getMobileOptions = (xAxisTickInterval, yAxisTickInterval) => {
|
|
149
|
+
return {
|
|
150
|
+
legend: {
|
|
151
|
+
itemStyle: {
|
|
152
|
+
fontSize: this.constants.mobileFontSize,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
xAxis: {
|
|
156
|
+
labels: {
|
|
157
|
+
style: {
|
|
158
|
+
fontSize: this.constants.mobileFontSize,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
title: {
|
|
162
|
+
style: {
|
|
163
|
+
fontSize: this.constants.mobileFontSize,
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
tickInterval: xAxisTickInterval,
|
|
167
|
+
},
|
|
168
|
+
yAxis: {
|
|
169
|
+
labels: {
|
|
170
|
+
style: {
|
|
171
|
+
fontSize: this.constants.mobileFontSize,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
title: {
|
|
175
|
+
style: {
|
|
176
|
+
fontSize: this.constants.mobileFontSize,
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
tickInterval: yAxisTickInterval,
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
hideDataLabels = (series) => {
|
|
185
|
+
series.forEach((series) => {
|
|
183
186
|
series.update({
|
|
184
187
|
dataLabels: {
|
|
185
188
|
enabled: false,
|
|
@@ -188,13 +191,63 @@ class CommonChartOptions {
|
|
|
188
191
|
});
|
|
189
192
|
};
|
|
190
193
|
|
|
191
|
-
disableLegendForSingleSeries = (
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
+
disableLegendForSingleSeries = (config) => {
|
|
195
|
+
if (config.series.length === 1) {
|
|
196
|
+
config.legend = {
|
|
194
197
|
enabled: false,
|
|
198
|
+
};
|
|
199
|
+
config.chart.marginTop = 50;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
updateLegendSymbols = (chart) => {
|
|
204
|
+
if (chart.legend.options.enabled) {
|
|
205
|
+
chart.legend.allItems.forEach((item) => {
|
|
206
|
+
const { legendItem, userOptions } = item;
|
|
207
|
+
const seriesType = userOptions?.type;
|
|
208
|
+
// symbol is defined for bar / column series, and line is defined for line series
|
|
209
|
+
// if symbol is defined for a line series, it is the marker symbol
|
|
210
|
+
const { label, symbol } = legendItem || {};
|
|
211
|
+
|
|
212
|
+
if (seriesType === 'line') {
|
|
213
|
+
symbol?.attr({
|
|
214
|
+
x: 16, // Position the marker to the right of the line
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
label?.attr({
|
|
218
|
+
x: 30, // Adjust label position to account for longer line
|
|
219
|
+
});
|
|
220
|
+
} else {
|
|
221
|
+
// Set the symbol size for bar / column series
|
|
222
|
+
symbol.attr({
|
|
223
|
+
width: 12,
|
|
224
|
+
height: 12,
|
|
225
|
+
y: 8,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
195
228
|
});
|
|
196
229
|
}
|
|
197
230
|
};
|
|
231
|
+
|
|
232
|
+
adjustChartHeight = (currentChart, percentageHeightDesktop, percentageHeightMobile) => {
|
|
233
|
+
// get height and width of the plot area
|
|
234
|
+
const plotHeight = currentChart.plotHeight;
|
|
235
|
+
const plotWidth = currentChart.plotWidth;
|
|
236
|
+
// calculate the new plot height based on the percentage height
|
|
237
|
+
// default to the current height
|
|
238
|
+
let newPlotHeight = plotHeight;
|
|
239
|
+
if (plotWidth > 400) {
|
|
240
|
+
newPlotHeight = plotWidth * (percentageHeightDesktop / 100);
|
|
241
|
+
} else {
|
|
242
|
+
newPlotHeight = plotWidth * (percentageHeightMobile / 100);
|
|
243
|
+
}
|
|
244
|
+
const totalHeight = currentChart.plotTop + newPlotHeight + currentChart.marginBottom;
|
|
245
|
+
|
|
246
|
+
// set the new size of the chart
|
|
247
|
+
if (totalHeight !== currentChart.chartHeight) {
|
|
248
|
+
currentChart.setSize(null, totalHeight, false);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
198
251
|
}
|
|
199
252
|
|
|
200
253
|
export default CommonChartOptions;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{% from "components/chart/_macro.njk" import onsChart %}
|
|
2
|
+
|
|
3
|
+
{{
|
|
4
|
+
onsChart({
|
|
5
|
+
"chartType": "bar",
|
|
6
|
+
"description": "Volume sales, seasonally adjusted, Great Britain, January 2022 to January 2025",
|
|
7
|
+
"theme": "primary",
|
|
8
|
+
"title": "Food stores showed a strong rise on the month, while non-food stores fell",
|
|
9
|
+
"subtitle": "Figure 6: Upward contribution from housing and household services (including energy) saw the annual CPIH inflation rate rise",
|
|
10
|
+
"id": "uuid",
|
|
11
|
+
"caption": "Source: Monthly Business Survey, Retail Sales Inquiry from the Office for National Statistics",
|
|
12
|
+
"download": {
|
|
13
|
+
'title': 'Download Figure 1 data',
|
|
14
|
+
'itemsList': [
|
|
15
|
+
{
|
|
16
|
+
"text": "Excel spreadsheet (XLSX format, 18KB)",
|
|
17
|
+
"url": "#"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"text": "Simple text file (CSV format, 25KB)",
|
|
21
|
+
"url": "#"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"text": "Image (PNG format, 25KB)",
|
|
25
|
+
"url": "#"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"xAxis": {
|
|
30
|
+
"categories": [
|
|
31
|
+
"All retailing",
|
|
32
|
+
"All retailing excluding Automotive fuel",
|
|
33
|
+
"Food stores",
|
|
34
|
+
"Department stores",
|
|
35
|
+
"Other non-food stores",
|
|
36
|
+
"Textile clothing \u0026 footwear stores",
|
|
37
|
+
"Household goods stores",
|
|
38
|
+
"Non-store retailing",
|
|
39
|
+
"Automotive fuel"
|
|
40
|
+
],
|
|
41
|
+
"type": "linear"
|
|
42
|
+
},
|
|
43
|
+
"yAxis": {
|
|
44
|
+
"title": "Percent (%)"
|
|
45
|
+
},
|
|
46
|
+
"series": [
|
|
47
|
+
{
|
|
48
|
+
"data": [1.7, 2.1, 5.6, 0, -0.6, -2.7, -1.7, 2.4, -1.2],
|
|
49
|
+
"dataLabels": true,
|
|
50
|
+
"name": "Jan-25"
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"annotations": [
|
|
54
|
+
{
|
|
55
|
+
"text": "A test annotation",
|
|
56
|
+
"point": {"x": 2, "y": 3},
|
|
57
|
+
"labelOffsetX": 40,
|
|
58
|
+
"labelOffsetY": -30
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
})
|
|
62
|
+
}}
|