@ons/design-system 72.4.0 → 72.6.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/button/_button.scss +171 -3
- package/components/button/_macro.njk +4 -0
- package/components/card/_card.scss +5 -0
- package/components/card/_macro.njk +10 -2
- package/components/card/_macro.spec.js +58 -0
- package/components/card/example-card-set-with-headline-figures.njk +62 -0
- package/components/chart/_chart.scss +80 -0
- package/components/chart/_macro.njk +125 -0
- package/components/chart/_macro.spec.js +825 -0
- package/components/chart/annotations-options.js +78 -0
- package/components/chart/bar-chart.js +164 -0
- package/components/chart/chart-constants.js +21 -0
- package/components/chart/chart.dom.js +8 -0
- package/components/chart/chart.js +242 -0
- package/components/chart/column-chart.js +48 -0
- package/components/chart/common-chart-options.js +247 -0
- package/components/chart/example-bar-chart-with-annotations.njk +62 -0
- package/components/chart/example-bar-chart.njk +55 -0
- package/components/chart/example-bar-with-line-chart.njk +64 -0
- package/components/chart/example-clustered-bar-chart.njk +60 -0
- package/components/chart/example-clustered-column-chart.njk +74 -0
- package/components/chart/example-column-chart-with-annotations.njk +62 -0
- package/components/chart/example-column-chart.njk +54 -0
- package/components/chart/example-column-with-line-chart.njk +62 -0
- package/components/chart/example-line-chart-with-annotations.njk +235 -0
- package/components/chart/example-line-chart.njk +221 -0
- package/components/chart/example-stacked-bar-chart.njk +60 -0
- package/components/chart/example-stacked-column-chart.njk +67 -0
- package/components/chart/line-chart.js +42 -0
- package/components/chart/specific-chart-options.js +22 -0
- package/components/footer/_footer.scss +12 -0
- package/components/footer/_macro.njk +38 -17
- package/components/header/_header.scss +18 -2
- package/components/header/_macro.njk +81 -8
- package/components/header/_macro.spec.js +97 -0
- package/components/header/example-header-with-search-button.njk +190 -0
- package/components/header/header.spec.js +128 -0
- package/components/hero/_hero.scss +39 -7
- package/components/hero/_macro.njk +47 -17
- package/components/hero/_macro.spec.js +94 -0
- package/components/hero/example-hero-grey.njk +9 -0
- package/components/icon/_macro.njk +8 -8
- package/components/input/_input.scss +15 -0
- package/components/input/_macro.njk +3 -3
- package/components/navigation/navigation.dom.js +17 -0
- package/components/navigation/navigation.js +72 -2
- package/components/pagination/_pagination.scss +7 -1
- package/components/summary/_macro.njk +1 -1
- package/components/summary/_macro.spec.js +6 -0
- 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/js/main.js +2 -0
- package/package.json +4 -1
- package/scripts/main.es5.js +1 -1
- package/scripts/main.js +1 -1
- package/scss/main.scss +1 -0
- package/scss/utilities/_grid.scss +13 -4
|
@@ -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;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import ChartConstants from './chart-constants';
|
|
2
|
+
|
|
3
|
+
class BarChart {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.constants = ChartConstants.constants();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
getBarChartOptions = (useStackedLayout) => {
|
|
9
|
+
return {
|
|
10
|
+
plotOptions: {
|
|
11
|
+
bar: {
|
|
12
|
+
// Set the width of the bars to be 30px
|
|
13
|
+
// The spacing is worked out in the specific-chart-options.js file
|
|
14
|
+
pointWidth: 30, // Fixed bar height
|
|
15
|
+
pointPadding: 0,
|
|
16
|
+
groupPadding: 0,
|
|
17
|
+
borderWidth: 0,
|
|
18
|
+
borderRadius: 0,
|
|
19
|
+
// Set the data labels to be enabled and positioned outside the bars
|
|
20
|
+
// We can add custom formatting on each chart to move the labels inside the bars if the bar is wide enough
|
|
21
|
+
dataLabels: {
|
|
22
|
+
enabled: true,
|
|
23
|
+
inside: false,
|
|
24
|
+
style: {
|
|
25
|
+
textOutline: 'none',
|
|
26
|
+
// The design system does not include a semibold font weight, so we use 700 (bold) as an alternative.
|
|
27
|
+
fontWeight: '700',
|
|
28
|
+
color: this.constants.labelColor,
|
|
29
|
+
fontSize: this.constants.mobileFontSize,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
series: {
|
|
34
|
+
stacking: useStackedLayout ? 'normal' : null,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
xAxis: {
|
|
38
|
+
// Update the category label colours for bar charts
|
|
39
|
+
labels: {
|
|
40
|
+
style: {
|
|
41
|
+
color: this.constants.categoryLabelColor,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
// remove the tick marks for bar charts
|
|
45
|
+
tickWidth: 0,
|
|
46
|
+
tickLength: 0,
|
|
47
|
+
tickColor: 'transparent',
|
|
48
|
+
title: { align: 'high', textAlign: 'middle', reserveSpace: false, rotation: 0, y: -25, useHTML: true },
|
|
49
|
+
},
|
|
50
|
+
yAxis: {
|
|
51
|
+
title: {
|
|
52
|
+
// Override the y Axis title settings for bar charts where the y axis is horizontal
|
|
53
|
+
textAlign: 'right',
|
|
54
|
+
offset: undefined,
|
|
55
|
+
y: 0,
|
|
56
|
+
reserveSpace: true,
|
|
57
|
+
useHTML: false,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Updates the config to move the data labels inside the bars, but only if the bar is wide enough
|
|
64
|
+
// This may also need to run when the chart is resized
|
|
65
|
+
postLoadDataLabels = (currentChart) => {
|
|
66
|
+
const insideOptions = {
|
|
67
|
+
dataLabels: this.getBarChartLabelsInsideOptions(),
|
|
68
|
+
};
|
|
69
|
+
const outsideOptions = {
|
|
70
|
+
dataLabels: this.getBarChartLabelsOutsideOptions(),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
currentChart.series.forEach((series) => {
|
|
74
|
+
const points = series.data;
|
|
75
|
+
points.forEach((point) => {
|
|
76
|
+
// Get the actual width of the data label
|
|
77
|
+
const labelWidth = point.dataLabel && point.dataLabel.getBBox().width;
|
|
78
|
+
// Move the data labels inside the bar if the bar is wider than the label plus some padding
|
|
79
|
+
if (series.type == 'line') {
|
|
80
|
+
// If we have a bar chart with an extra line, exit early for the line series
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (point.shapeArgs.height > labelWidth + 20) {
|
|
84
|
+
point.update(insideOptions, false);
|
|
85
|
+
} else {
|
|
86
|
+
point.update(outsideOptions, false);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
currentChart.redraw();
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
getBarChartLabelsInsideOptions = () => ({
|
|
95
|
+
inside: true,
|
|
96
|
+
align: 'right',
|
|
97
|
+
verticalAlign: 'middle',
|
|
98
|
+
style: {
|
|
99
|
+
color: 'white',
|
|
100
|
+
fontWeight: 'bold',
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
getBarChartLabelsOutsideOptions = () => ({
|
|
105
|
+
inside: false,
|
|
106
|
+
align: undefined,
|
|
107
|
+
verticalAlign: undefined,
|
|
108
|
+
style: {
|
|
109
|
+
textOutline: 'none',
|
|
110
|
+
// The design system does not include a semibold font weight, so we use 700 (bold) as an alternative.
|
|
111
|
+
fontWeight: '700',
|
|
112
|
+
color: this.constants.labelColor,
|
|
113
|
+
fontSize: this.constants.mobileFontSize,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// This updates the height of the vertical axis and overall chart to fit the number of categories
|
|
118
|
+
// Note that the vertical axis on a bar chart is the x axis
|
|
119
|
+
updateBarChartHeight = (config, currentChart, useStackedLayout, numberOfExtraLines) => {
|
|
120
|
+
const numberOfCategories = config.xAxis.categories.length;
|
|
121
|
+
const numberOfSeries = currentChart.series.length - numberOfExtraLines; // Get number of bar series
|
|
122
|
+
let barHeight = 30; // Height of each individual bar - set in bar-chart-plot-options
|
|
123
|
+
let groupSpacing = 0; // Space we want between category groups, or between series groups for cluster charts
|
|
124
|
+
let categoriesTotalHeight = 0;
|
|
125
|
+
let totalSpaceHeight = 0;
|
|
126
|
+
if (useStackedLayout == false && numberOfSeries > 1) {
|
|
127
|
+
// slightly lower bar height for cluster charts
|
|
128
|
+
barHeight = 28;
|
|
129
|
+
// for cluster charts there is no space between the bars within a series, and 14px between each series
|
|
130
|
+
groupSpacing = 14;
|
|
131
|
+
// lower barHeight for series with 3 categories or more
|
|
132
|
+
if (numberOfSeries >= 3) {
|
|
133
|
+
barHeight = 20;
|
|
134
|
+
}
|
|
135
|
+
categoriesTotalHeight = numberOfCategories * barHeight * numberOfSeries;
|
|
136
|
+
|
|
137
|
+
totalSpaceHeight = numberOfCategories * groupSpacing;
|
|
138
|
+
// work out the group padding for cluster charts which is measured in xAxis units.
|
|
139
|
+
const plotHeight = categoriesTotalHeight + totalSpaceHeight;
|
|
140
|
+
const xUnitHeight = plotHeight / numberOfCategories;
|
|
141
|
+
const groupPadding = groupSpacing / 2 / xUnitHeight;
|
|
142
|
+
currentChart.series.forEach((series) => {
|
|
143
|
+
series.update({
|
|
144
|
+
groupPadding: groupPadding,
|
|
145
|
+
pointWidth: barHeight,
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
} else {
|
|
149
|
+
groupSpacing = 10;
|
|
150
|
+
categoriesTotalHeight = numberOfCategories * barHeight;
|
|
151
|
+
totalSpaceHeight = (numberOfCategories - 1) * groupSpacing;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
config.xAxis.height = categoriesTotalHeight + totalSpaceHeight;
|
|
155
|
+
const totalHeight = currentChart.plotTop + config.xAxis.height + currentChart.marginBottom;
|
|
156
|
+
if (totalHeight !== currentChart.chartHeight) {
|
|
157
|
+
currentChart.setSize(null, totalHeight, false);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
currentChart.redraw();
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export default BarChart;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class ChartConstants {
|
|
2
|
+
static constants() {
|
|
3
|
+
const constants = {
|
|
4
|
+
primaryTheme: ['#206095', '#27a0cc', '#003c57', '#118c7b', '#a8bd3a', '#871a5b', '#f66068', '#746cb1', '#22d0b6'],
|
|
5
|
+
// Alternate theme colours from https://service-manual.ons.gov.uk/data-visualisation/colours/using-colours-in-charts
|
|
6
|
+
alternateTheme: ['#206095', '#27A0CC', '#871A5B', '#A8BD3A', '#F66068'],
|
|
7
|
+
labelColor: '#414042',
|
|
8
|
+
axisLabelColor: '#707071',
|
|
9
|
+
categoryLabelColor: '#414042',
|
|
10
|
+
gridLineColor: '#d9d9d9',
|
|
11
|
+
zeroLineColor: '#b3b3b3',
|
|
12
|
+
// Responsive font sizes
|
|
13
|
+
mobileFontSize: '0.75rem', // 12px
|
|
14
|
+
desktopFontSize: '0.875rem', // 14px
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return constants;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default ChartConstants;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import Highcharts from 'highcharts';
|
|
2
|
+
import 'highcharts/modules/accessibility';
|
|
3
|
+
import 'highcharts/modules/annotations';
|
|
4
|
+
|
|
5
|
+
import CommonChartOptions from './common-chart-options';
|
|
6
|
+
import SpecificChartOptions from './specific-chart-options';
|
|
7
|
+
import LineChart from './line-chart';
|
|
8
|
+
import BarChart from './bar-chart';
|
|
9
|
+
import ColumnChart from './column-chart';
|
|
10
|
+
import AnnotationsOptions from './annotations-options';
|
|
11
|
+
|
|
12
|
+
class HighchartsBaseChart {
|
|
13
|
+
static selector() {
|
|
14
|
+
return '[data-highcharts-base-chart]';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
constructor(node) {
|
|
18
|
+
this.node = node;
|
|
19
|
+
this.chartType = this.node.dataset.highchartsType;
|
|
20
|
+
this.theme = this.node.dataset.highchartsTheme;
|
|
21
|
+
const chartNode = this.node.querySelector('[data-highcharts-chart]');
|
|
22
|
+
this.id = this.node.dataset.highchartsId;
|
|
23
|
+
this.useStackedLayout = this.node.hasAttribute('data-highcharts-use-stacked-layout');
|
|
24
|
+
this.config = JSON.parse(this.node.querySelector(`[data-highcharts-config--${this.id}]`).textContent);
|
|
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.commonChartOptions = new CommonChartOptions();
|
|
32
|
+
this.specificChartOptions = new SpecificChartOptions(this.theme, this.chartType, this.config);
|
|
33
|
+
this.lineChart = new LineChart();
|
|
34
|
+
this.barChart = new BarChart();
|
|
35
|
+
this.columnChart = new ColumnChart();
|
|
36
|
+
this.extraLines = this.checkForExtraLines();
|
|
37
|
+
if (window.isCommonChartOptionsDefined === undefined) {
|
|
38
|
+
this.setCommonChartOptions();
|
|
39
|
+
window.isCommonChartOptionsDefined = true;
|
|
40
|
+
}
|
|
41
|
+
this.hideDataLabels = this.checkHideDataLabels();
|
|
42
|
+
this.setSpecificChartOptions();
|
|
43
|
+
this.setResponsiveOptions();
|
|
44
|
+
this.setLoadEvent();
|
|
45
|
+
this.setWindowResizeEvent();
|
|
46
|
+
this.chart = Highcharts.chart(chartNode, this.config);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check for the number of extra line series in the config
|
|
50
|
+
checkForExtraLines = () => {
|
|
51
|
+
return this.chartType === 'line' ? 0 : this.config.series.filter((series) => series.type === 'line').length;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Set up the global Highcharts options which are used for all charts
|
|
55
|
+
setCommonChartOptions = () => {
|
|
56
|
+
const chartOptions = this.commonChartOptions.getOptions();
|
|
57
|
+
Highcharts.setOptions(chartOptions);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Utility function to merge two configs together
|
|
61
|
+
mergeConfigs = (baseConfig, newConfig) => {
|
|
62
|
+
// If newConfig is null/undefined, return baseConfig
|
|
63
|
+
if (!newConfig) return baseConfig;
|
|
64
|
+
|
|
65
|
+
// Create a new object to store the merged result
|
|
66
|
+
const merged = { ...baseConfig };
|
|
67
|
+
|
|
68
|
+
// Iterate through all keys in newConfig
|
|
69
|
+
Object.keys(newConfig).forEach((key) => {
|
|
70
|
+
// Get values from both configs for this key
|
|
71
|
+
const baseValue = merged[key];
|
|
72
|
+
const newValue = newConfig[key];
|
|
73
|
+
|
|
74
|
+
// If both values are objects (and not null), recursively merge them
|
|
75
|
+
if (
|
|
76
|
+
baseValue &&
|
|
77
|
+
newValue &&
|
|
78
|
+
typeof baseValue === 'object' &&
|
|
79
|
+
typeof newValue === 'object' &&
|
|
80
|
+
!Array.isArray(baseValue) &&
|
|
81
|
+
!Array.isArray(newValue)
|
|
82
|
+
) {
|
|
83
|
+
merged[key] = this.mergeConfigs(baseValue, newValue);
|
|
84
|
+
} else {
|
|
85
|
+
// For non-objects and arrays use the new value
|
|
86
|
+
// If the new value is null/undefined, use the base value
|
|
87
|
+
merged[key] = newValue ?? baseValue;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return merged;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Set up options for specific charts and chart types
|
|
95
|
+
setSpecificChartOptions = () => {
|
|
96
|
+
const specificChartOptions = this.specificChartOptions.getOptions();
|
|
97
|
+
const lineChartOptions = this.lineChart.getLineChartOptions();
|
|
98
|
+
const barChartOptions = this.barChart.getBarChartOptions(this.useStackedLayout);
|
|
99
|
+
const columnChartOptions = this.columnChart.getColumnChartOptions(this.useStackedLayout);
|
|
100
|
+
// Merge specificChartOptions with the existing config
|
|
101
|
+
this.config = this.mergeConfigs(this.config, specificChartOptions);
|
|
102
|
+
|
|
103
|
+
if (this.chartType === 'line') {
|
|
104
|
+
// Merge the line chart options with the existing config
|
|
105
|
+
this.config = this.mergeConfigs(this.config, lineChartOptions);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (this.chartType === 'bar') {
|
|
109
|
+
// Merge the bar chart options with the existing config
|
|
110
|
+
this.config = this.mergeConfigs(this.config, barChartOptions);
|
|
111
|
+
}
|
|
112
|
+
if (this.chartType === 'column') {
|
|
113
|
+
// Merge the column chart options with the existing config
|
|
114
|
+
this.config = this.mergeConfigs(this.config, columnChartOptions);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (this.extraLines > 0) {
|
|
118
|
+
this.config = this.mergeConfigs(this.config, this.lineChart.getLineChartOptions());
|
|
119
|
+
if (this.chartType === 'column') {
|
|
120
|
+
this.config = this.mergeConfigs(this.config, columnChartOptions);
|
|
121
|
+
}
|
|
122
|
+
if (this.chartType === 'bar') {
|
|
123
|
+
this.config = this.mergeConfigs(this.config, barChartOptions);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Disable the legend for single series charts
|
|
128
|
+
this.commonChartOptions.disableLegendForSingleSeries(this.config);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Check if the data labels should be hidden
|
|
132
|
+
// They should be hidden for clustered bar charts with more than 2 series, and also for stacked bar charts
|
|
133
|
+
checkHideDataLabels = () => {
|
|
134
|
+
return (this.chartType === 'bar' && this.config.series.length > 2) || this.useStackedLayout === true;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Adjust font size and annotations for smaller width of chart
|
|
138
|
+
// Note this is not the same as the viewport width
|
|
139
|
+
// All responsive rules should be defined here to avoid overriding existing rules
|
|
140
|
+
setResponsiveOptions = () => {
|
|
141
|
+
const mobileCommonChartOptions = this.commonChartOptions.getMobileOptions();
|
|
142
|
+
if (!this.config.responsive) {
|
|
143
|
+
this.config.responsive = {};
|
|
144
|
+
}
|
|
145
|
+
// If these conditions change, the styling for the footnotes container query in _chart.scss needs to be updated
|
|
146
|
+
let rules = [
|
|
147
|
+
{
|
|
148
|
+
condition: {
|
|
149
|
+
maxWidth: 400,
|
|
150
|
+
},
|
|
151
|
+
chartOptions: {
|
|
152
|
+
...mobileCommonChartOptions,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
// We are using a slightly wider breakpoint for annotations
|
|
156
|
+
// to try and reduce the likelihood of them being automatically
|
|
157
|
+
// hidden by Highcharts
|
|
158
|
+
{
|
|
159
|
+
condition: {
|
|
160
|
+
maxWidth: 600,
|
|
161
|
+
},
|
|
162
|
+
chartOptions: {
|
|
163
|
+
annotations: this.annotationsOptions ? this.annotationsOptions.getAnnotationsOptionsMobile() : undefined,
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
condition: {
|
|
168
|
+
minWidth: 601,
|
|
169
|
+
},
|
|
170
|
+
chartOptions: {
|
|
171
|
+
annotations: this.annotationsOptions ? this.annotationsOptions.getAnnotationsOptionsDesktop() : undefined,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
this.config.responsive.rules = rules;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// Create the load event for various chart types
|
|
179
|
+
// All load events should be defined here to avoid overriding existing events
|
|
180
|
+
setLoadEvent = () => {
|
|
181
|
+
if (!this.config.chart.events) {
|
|
182
|
+
this.config.chart.events = {};
|
|
183
|
+
}
|
|
184
|
+
this.config.chart.events.load = (event) => {
|
|
185
|
+
const currentChart = event.target;
|
|
186
|
+
if (this.chartType === 'line') {
|
|
187
|
+
this.lineChart.updateLastPointMarker(currentChart.series);
|
|
188
|
+
this.commonChartOptions.hideDataLabels(currentChart.series);
|
|
189
|
+
}
|
|
190
|
+
if (this.chartType === 'bar') {
|
|
191
|
+
this.barChart.updateBarChartHeight(this.config, currentChart, this.useStackedLayout, this.extraLines);
|
|
192
|
+
if (!this.hideDataLabels) {
|
|
193
|
+
this.barChart.postLoadDataLabels(currentChart);
|
|
194
|
+
} else {
|
|
195
|
+
this.commonChartOptions.hideDataLabels(currentChart.series);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (this.chartType === 'column') {
|
|
199
|
+
this.columnChart.updatePointPadding(this.config, currentChart, this.useStackedLayout, this.extraLines);
|
|
200
|
+
this.commonChartOptions.hideDataLabels(currentChart.series);
|
|
201
|
+
}
|
|
202
|
+
if (this.chartType != 'bar') {
|
|
203
|
+
this.commonChartOptions.adjustChartHeight(currentChart, this.percentageHeightDesktop, this.percentageHeightMobile);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// If the chart has an extra line or lines, hide the data labels for
|
|
207
|
+
// that series, update the last point marker
|
|
208
|
+
if (this.extraLines > 0) {
|
|
209
|
+
currentChart.series.forEach((series) => {
|
|
210
|
+
if (series.type === 'line') {
|
|
211
|
+
this.lineChart.updateLastPointMarker([series]);
|
|
212
|
+
this.commonChartOptions.hideDataLabels([series]);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
// Update the legend items for all charts
|
|
217
|
+
this.commonChartOptions.updateLegendSymbols(currentChart);
|
|
218
|
+
currentChart.redraw(false);
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Set resize events - throttled to 50ms
|
|
223
|
+
// All resize events should be defined here to avoid overriding existing events
|
|
224
|
+
setWindowResizeEvent = () => {
|
|
225
|
+
window.addEventListener('resize', () => {
|
|
226
|
+
clearTimeout(this.resizeTimeout);
|
|
227
|
+
this.resizeTimeout = setTimeout(() => {
|
|
228
|
+
// Get the current rendered chart instance
|
|
229
|
+
const currentChart = Highcharts.charts.find((chart) => chart && chart.container === this.chart.container);
|
|
230
|
+
// Update the data labels when the window is resized
|
|
231
|
+
if (this.chartType === 'bar' && !this.hideDataLabels) {
|
|
232
|
+
this.barChart.postLoadDataLabels(currentChart);
|
|
233
|
+
}
|
|
234
|
+
if (this.chartType != 'bar') {
|
|
235
|
+
this.commonChartOptions.adjustChartHeight(currentChart, this.percentageHeightDesktop, this.percentageHeightMobile);
|
|
236
|
+
}
|
|
237
|
+
}, 50);
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export default HighchartsBaseChart;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
class ColumnChart {
|
|
2
|
+
getColumnChartOptions = (useStackedLayout) => {
|
|
3
|
+
return {
|
|
4
|
+
plotOptions: {
|
|
5
|
+
column: {
|
|
6
|
+
pointPadding: 0,
|
|
7
|
+
groupPadding: 0,
|
|
8
|
+
borderRadius: 0,
|
|
9
|
+
borderWidth: 0,
|
|
10
|
+
},
|
|
11
|
+
series: {
|
|
12
|
+
stacking: useStackedLayout ? 'normal' : null,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
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
|
|
24
|
+
let pointPadding = 0;
|
|
25
|
+
let groupPadding = 0;
|
|
26
|
+
// non-clustered charts or stacked charts
|
|
27
|
+
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;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// update the point width and padding
|
|
39
|
+
currentChart.series.forEach((series) => {
|
|
40
|
+
series.update({
|
|
41
|
+
pointPadding: pointPadding,
|
|
42
|
+
groupPadding: groupPadding,
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default ColumnChart;
|