@ons/design-system 72.9.2 → 72.10.1
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/char-check-limit/_macro.njk +2 -2
- package/components/char-check-limit/character-check.js +30 -9
- package/components/char-check-limit/character-check.spec.js +1 -1
- package/components/chart/_chart.scss +73 -1
- package/components/chart/_macro.njk +90 -20
- package/components/chart/_macro.spec.js +424 -0
- package/components/chart/bar-chart.js +46 -20
- package/components/chart/boxplot.js +37 -0
- package/components/chart/chart-constants.js +14 -0
- package/components/chart/chart.js +102 -46
- package/components/chart/columnrange-chart.js +94 -0
- package/components/chart/common-chart-options.js +65 -23
- package/components/chart/example-bar-chart-with-annotations.njk +1 -1
- package/components/chart/example-bar-chart-with-point-range-and-reference-line-annotations.njk +95 -0
- package/components/chart/example-bar-with-confidence-levels.njk +71 -0
- package/components/chart/example-clustered-column-chart.njk +1 -3
- package/components/chart/example-column-chart-with-annotations.njk +1 -1
- package/components/chart/example-column-chart-with-custom-reference-line-value.njk +56 -0
- package/components/chart/example-column-chart-with-range-annotations.njk +64 -0
- package/components/chart/example-column-chart-with-reference-line-annotations.njk +64 -0
- package/components/chart/example-column-with-confidence-levels.njk +61 -0
- package/components/chart/example-line-chart-with-annotations.njk +3 -3
- package/components/chart/example-line-chart-with-custom-reference-line-value.njk +224 -0
- package/components/chart/example-line-chart-with-markers.njk +21 -21
- package/components/chart/example-line-chart-with-range-annotations-inside.njk +238 -0
- package/components/chart/example-line-chart-with-range-annotations-outside-left-right.njk +240 -0
- package/components/chart/example-line-chart-with-range-annotations-outside-top-bottom.njk +239 -0
- package/components/chart/example-line-chart-with-reference-line-annotations.njk +236 -0
- package/components/chart/example-scatter-chart.njk +5 -5
- package/components/chart/line-chart.js +29 -11
- package/components/chart/range-annotations-options.js +221 -0
- package/components/chart/reference-line-annotations-options.js +93 -0
- package/components/chart/scatter-chart.js +15 -6
- package/components/chart/specific-chart-options.js +22 -1
- package/components/chart/utilities.js +97 -0
- package/components/checkboxes/_macro.spec.js +1 -1
- package/components/mutually-exclusive/mutually-exclusive.textarea.spec.js +1 -1
- package/components/textarea/_macro.njk +8 -6
- package/components/textarea/_macro.spec.js +12 -8
- package/components/textarea/{example-textarea-with-character-limit.njk → example-textarea-with-character-check.njk} +3 -1
- package/css/main.css +1 -1
- package/js/main.js +0 -1
- package/package.json +14 -14
- package/scripts/main.es5.js +1 -1
- package/scripts/main.js +1 -1
- package/components/char-check-limit/character-limit.js +0 -55
- package/components/textarea/textarea.dom.js +0 -12
- package/components/textarea/textarea.spec.js +0 -98
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import ChartConstants from './chart-constants';
|
|
2
|
+
|
|
3
|
+
class RangeAnnotationsOptions {
|
|
4
|
+
constructor(rangeAnnotations) {
|
|
5
|
+
this.constants = ChartConstants.constants();
|
|
6
|
+
this.rangeAnnotations = rangeAnnotations;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
getRangeAnnotationsOptionsDesktop = (chartType) => {
|
|
10
|
+
let xAxisPlotBands = [];
|
|
11
|
+
let yAxisPlotBands = [];
|
|
12
|
+
this.rangeAnnotations.forEach((rangeAnnotation) => {
|
|
13
|
+
let adjustedRangeValues = this.adjustRangeForCategoryAxis(rangeAnnotation, chartType);
|
|
14
|
+
let rangeAnnotationLabelWidth = undefined;
|
|
15
|
+
if (!rangeAnnotation.labelInside) {
|
|
16
|
+
rangeAnnotationLabelWidth = rangeAnnotation.labelWidth ? rangeAnnotation.labelWidth : 150;
|
|
17
|
+
}
|
|
18
|
+
let rangeConfig = {
|
|
19
|
+
from: adjustedRangeValues.axisValue1,
|
|
20
|
+
to: adjustedRangeValues.axisValue2,
|
|
21
|
+
label: {
|
|
22
|
+
text: rangeAnnotation.text,
|
|
23
|
+
useHTML: true,
|
|
24
|
+
className: rangeAnnotation.labelInside
|
|
25
|
+
? `ons-chart__range-annotation-label ons-chart__range-annotation-label--${rangeAnnotation.axis}`
|
|
26
|
+
: 'ons-chart__range-annotation-label--outside',
|
|
27
|
+
allowOverlap: true,
|
|
28
|
+
style: {
|
|
29
|
+
color: this.constants.labelColor,
|
|
30
|
+
fontSize: this.constants.defaultFontSize,
|
|
31
|
+
// This property is not set as an inline style if rangeAnnotation.labelInside is undefined
|
|
32
|
+
width: rangeAnnotationLabelWidth,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
color: this.constants.shadingColor,
|
|
36
|
+
};
|
|
37
|
+
if (!rangeAnnotation.labelInside && rangeAnnotation.labelOffsetX) {
|
|
38
|
+
rangeConfig.label.x = rangeAnnotation.labelOffsetX;
|
|
39
|
+
}
|
|
40
|
+
if (!rangeAnnotation.labelInside && rangeAnnotation.labelOffsetY) {
|
|
41
|
+
rangeConfig.label.y = rangeAnnotation.labelOffsetY;
|
|
42
|
+
}
|
|
43
|
+
if (rangeAnnotation.axis === 'x') {
|
|
44
|
+
xAxisPlotBands.push(rangeConfig);
|
|
45
|
+
} else if (rangeAnnotation.axis === 'y') {
|
|
46
|
+
yAxisPlotBands.push(rangeConfig);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return {
|
|
50
|
+
xAxis: xAxisPlotBands.length > 0 ? { plotBands: xAxisPlotBands } : undefined,
|
|
51
|
+
yAxis: yAxisPlotBands.length > 0 ? { plotBands: yAxisPlotBands } : undefined,
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
getRangeAnnotationsOptionsMobile = (annotationsLength, chartType) => {
|
|
56
|
+
let xAxisPlotBands = [];
|
|
57
|
+
let yAxisPlotBands = [];
|
|
58
|
+
this.rangeAnnotations.forEach((rangeAnnotation, index) => {
|
|
59
|
+
let adjustedRangeValues = this.adjustRangeForCategoryAxis(rangeAnnotation, chartType);
|
|
60
|
+
let rangeConfig = {
|
|
61
|
+
from: adjustedRangeValues.axisValue1,
|
|
62
|
+
to: adjustedRangeValues.axisValue2,
|
|
63
|
+
label: {
|
|
64
|
+
// Add the number of point annotations to the current range annotation loop
|
|
65
|
+
// So that if we have both types of annotations, the numbers will be sequential
|
|
66
|
+
text: `${annotationsLength + index + 1}`,
|
|
67
|
+
useHTML: true,
|
|
68
|
+
className: 'ons-chart__footnote-number',
|
|
69
|
+
allowOverlap: true,
|
|
70
|
+
style: {
|
|
71
|
+
color: this.constants.labelColor,
|
|
72
|
+
fontSize: this.constants.defaultFontSize,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
color: this.constants.shadingColor,
|
|
76
|
+
};
|
|
77
|
+
if (rangeAnnotation.axis === 'x') {
|
|
78
|
+
xAxisPlotBands.push(rangeConfig);
|
|
79
|
+
} else if (rangeAnnotation.axis === 'y') {
|
|
80
|
+
yAxisPlotBands.push(rangeConfig);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
xAxis: xAxisPlotBands.length > 0 ? { plotBands: xAxisPlotBands } : undefined,
|
|
85
|
+
yAxis: yAxisPlotBands.length > 0 ? { plotBands: yAxisPlotBands } : undefined,
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Returns the position of the label relative to the plotBand
|
|
90
|
+
getLabelPosition = (chartType, labelRect, plotBandRect, isXaxis) => {
|
|
91
|
+
let isToLeft = false;
|
|
92
|
+
let isToRight = false;
|
|
93
|
+
let isToTop = false;
|
|
94
|
+
let isToBottom = false;
|
|
95
|
+
let isVertical = chartType === 'bar' ? !isXaxis : isXaxis;
|
|
96
|
+
if (isVertical) {
|
|
97
|
+
// vertical plotBand
|
|
98
|
+
if (labelRect.right < plotBandRect.left) {
|
|
99
|
+
isToLeft = true;
|
|
100
|
+
}
|
|
101
|
+
if (labelRect.left > plotBandRect.right) {
|
|
102
|
+
isToRight = true;
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
// horizontal plotBand
|
|
106
|
+
if (labelRect.bottom < plotBandRect.top) {
|
|
107
|
+
isToTop = true;
|
|
108
|
+
}
|
|
109
|
+
if (labelRect.top > plotBandRect.bottom) {
|
|
110
|
+
isToBottom = true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return { isToLeft, isToRight, isToTop, isToBottom };
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Draws the line connecting the label to the plotBand
|
|
117
|
+
// Also adds a custom class to the label based on its position
|
|
118
|
+
addLine = (currentChart) => {
|
|
119
|
+
// If there is already a line in the chart, remove it before redrawing it
|
|
120
|
+
const lines = currentChart.container.querySelectorAll('[data-range-annotation-line]');
|
|
121
|
+
if (lines.length > 0) {
|
|
122
|
+
lines.forEach((line) => {
|
|
123
|
+
line.remove();
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Get all plotBands from both axes
|
|
128
|
+
// The filter ensures that plotLines are not included as they will have a `value` property rather than `from` and `to` properties
|
|
129
|
+
const xAxisPlotBands = currentChart.xAxis[0].plotLinesAndBands.filter((band) => band.options.from !== undefined);
|
|
130
|
+
const yAxisPlotBands = currentChart.yAxis[0].plotLinesAndBands.filter((band) => band.options.from !== undefined);
|
|
131
|
+
|
|
132
|
+
// Combine all plotBands
|
|
133
|
+
const allPlotBands = [...xAxisPlotBands, ...yAxisPlotBands];
|
|
134
|
+
|
|
135
|
+
allPlotBands.forEach((plotBand) => {
|
|
136
|
+
// If the label is set to be inside the plotBand, exit early and don't draw a line
|
|
137
|
+
if (plotBand.options.label.x === undefined && plotBand.options.label.y === undefined) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Get the plotBand element
|
|
142
|
+
const plotBandElement = plotBand.svgElem.element;
|
|
143
|
+
const plotBandRect = plotBandElement.getBoundingClientRect();
|
|
144
|
+
|
|
145
|
+
// Get the label element
|
|
146
|
+
const labelElement = plotBand.label.element;
|
|
147
|
+
const labelRect = labelElement.getBoundingClientRect();
|
|
148
|
+
|
|
149
|
+
// Get the label position relative to the plotBand (left, right, top or bottom)
|
|
150
|
+
const labelPosition = this.getLabelPosition(
|
|
151
|
+
currentChart.userOptions.chart.type,
|
|
152
|
+
labelRect,
|
|
153
|
+
plotBandRect,
|
|
154
|
+
plotBand.axis.isXAxis,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// Only draw a line in the label doesn't overlap the plot band
|
|
158
|
+
if (!(labelPosition.isToLeft || labelPosition.isToRight || labelPosition.isToTop || labelPosition.isToBottom)) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Create and add the connecting line
|
|
163
|
+
const line = document.createElement('div');
|
|
164
|
+
line.classList.add('ons-chart__connector-line');
|
|
165
|
+
line.setAttribute('data-range-annotation-line', true);
|
|
166
|
+
labelElement.appendChild(line);
|
|
167
|
+
|
|
168
|
+
// Draw the line based on the label position
|
|
169
|
+
if (labelPosition.isToRight) {
|
|
170
|
+
let divWidth = labelRect.left - plotBandRect.right;
|
|
171
|
+
line.style.width = `${divWidth}px`;
|
|
172
|
+
line.style.height = '1px';
|
|
173
|
+
line.style.top = `${labelRect.height / 2}px`;
|
|
174
|
+
line.style.left = `-${divWidth}px`;
|
|
175
|
+
labelElement.classList.add('ons-chart__range-annotation-label--right');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (labelPosition.isToLeft) {
|
|
179
|
+
let divWidth = plotBandRect.left - labelRect.right;
|
|
180
|
+
line.style.width = `${divWidth}px`;
|
|
181
|
+
line.style.height = '1px';
|
|
182
|
+
line.style.top = `${labelRect.height / 2}px`;
|
|
183
|
+
line.style.right = `-${divWidth}px`;
|
|
184
|
+
labelElement.classList.add('ons-chart__range-annotation-label--left');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (labelPosition.isToTop) {
|
|
188
|
+
let divHeight = plotBandRect.top - labelRect.bottom;
|
|
189
|
+
line.style.height = `${divHeight}px`;
|
|
190
|
+
line.style.width = '1px';
|
|
191
|
+
line.style.top = `${labelRect.height}px`;
|
|
192
|
+
line.style.left = `${labelRect.width / 2}px`;
|
|
193
|
+
labelElement.classList.add('ons-chart__range-annotation-label--top');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (labelPosition.isToBottom) {
|
|
197
|
+
let divHeight = labelRect.top - plotBandRect.bottom;
|
|
198
|
+
line.style.height = `${divHeight}px`;
|
|
199
|
+
line.style.width = '1px';
|
|
200
|
+
line.style.top = `-${divHeight}px`;
|
|
201
|
+
line.style.left = `${labelRect.width / 2}px`;
|
|
202
|
+
labelElement.classList.add('ons-chart__range-annotation-label--bottom');
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// For bar and column charts, we want the range to
|
|
208
|
+
// start and end flush with the edges of the columns / bars,
|
|
209
|
+
// not halfway through as is the Highcharts default.
|
|
210
|
+
adjustRangeForCategoryAxis = (rangeAnnotation, chartType) => {
|
|
211
|
+
let axisValue1 = rangeAnnotation.range.axisValue1;
|
|
212
|
+
let axisValue2 = rangeAnnotation.range.axisValue2;
|
|
213
|
+
if ((chartType === 'bar' && rangeAnnotation.axis === 'x') || (chartType === 'column' && rangeAnnotation.axis === 'x')) {
|
|
214
|
+
axisValue1 = rangeAnnotation.range.axisValue1 - 0.5;
|
|
215
|
+
axisValue2 = rangeAnnotation.range.axisValue2 - 0.5;
|
|
216
|
+
}
|
|
217
|
+
return { axisValue1, axisValue2 };
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export default RangeAnnotationsOptions;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import ChartConstants from './chart-constants';
|
|
2
|
+
|
|
3
|
+
class ReferenceLineAnnotationsOptions {
|
|
4
|
+
constructor(referenceLineAnnotations) {
|
|
5
|
+
this.constants = ChartConstants.constants();
|
|
6
|
+
this.referenceLineAnnotations = referenceLineAnnotations;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
getReferenceLineAnnotationsOptionsDesktop = () => {
|
|
10
|
+
let xAxisPlotLines = [];
|
|
11
|
+
let yAxisPlotLines = [];
|
|
12
|
+
this.referenceLineAnnotations.forEach((referenceLineAnnotation) => {
|
|
13
|
+
let referenceLineConfig = {
|
|
14
|
+
label: {
|
|
15
|
+
text: referenceLineAnnotation.text,
|
|
16
|
+
style: {
|
|
17
|
+
color: this.constants.labelColor,
|
|
18
|
+
fontSize: this.constants.defaultFontSize,
|
|
19
|
+
width: referenceLineAnnotation.labelWidth ? referenceLineAnnotation.labelWidth : 150,
|
|
20
|
+
},
|
|
21
|
+
x: referenceLineAnnotation.labelOffsetX ? referenceLineAnnotation.labelOffsetX : 10,
|
|
22
|
+
y: referenceLineAnnotation.labelOffsetY ? referenceLineAnnotation.labelOffsetY : 20,
|
|
23
|
+
rotation: 0,
|
|
24
|
+
align: 'left',
|
|
25
|
+
textAlign: 'left',
|
|
26
|
+
},
|
|
27
|
+
color: this.constants.zeroLineColor,
|
|
28
|
+
// note this works to give a dashed line with 4px and a 4px gap, but
|
|
29
|
+
// this way of styling it is undocumented
|
|
30
|
+
dashStyle: '2 2',
|
|
31
|
+
width: 2,
|
|
32
|
+
value: referenceLineAnnotation.value,
|
|
33
|
+
zIndex: 3,
|
|
34
|
+
};
|
|
35
|
+
if (referenceLineAnnotation.axis === 'x') {
|
|
36
|
+
xAxisPlotLines.push(referenceLineConfig);
|
|
37
|
+
} else if (referenceLineAnnotation.axis === 'y') {
|
|
38
|
+
yAxisPlotLines.push(referenceLineConfig);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return {
|
|
42
|
+
xAxis: xAxisPlotLines.length > 0 ? { plotLines: xAxisPlotLines } : undefined,
|
|
43
|
+
yAxis: yAxisPlotLines.length > 0 ? { plotLines: yAxisPlotLines } : undefined,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
getReferenceLineAnnotationsOptionsMobile = (annotationsLength, chartType) => {
|
|
48
|
+
let xAxisPlotLines = [];
|
|
49
|
+
let yAxisPlotLines = [];
|
|
50
|
+
this.referenceLineAnnotations.forEach((referenceLineAnnotation, index) => {
|
|
51
|
+
let isVertical = referenceLineAnnotation.axis === 'x' ? true : false;
|
|
52
|
+
if (chartType === 'bar') {
|
|
53
|
+
isVertical = !isVertical;
|
|
54
|
+
}
|
|
55
|
+
let referenceLineConfig = {
|
|
56
|
+
label: {
|
|
57
|
+
// Add the number of point and range annotations to the current reference line annotation loop
|
|
58
|
+
// So that if we have more than one type of annotations, the numbers will be sequential
|
|
59
|
+
text: `${annotationsLength + index + 1}`,
|
|
60
|
+
useHTML: true,
|
|
61
|
+
className: 'ons-chart__footnote-number',
|
|
62
|
+
allowOverlap: true,
|
|
63
|
+
style: {
|
|
64
|
+
color: this.constants.labelColor,
|
|
65
|
+
fontSize: this.constants.defaultFontSize,
|
|
66
|
+
},
|
|
67
|
+
x: isVertical ? 7 : 0,
|
|
68
|
+
y: isVertical ? 13 : -10, // 13 works with the height of the styled number
|
|
69
|
+
rotation: 0,
|
|
70
|
+
zIndex: 3,
|
|
71
|
+
},
|
|
72
|
+
color: this.constants.zeroLineColor,
|
|
73
|
+
// note this works to give a dashed line with 4px and a 4px gap, but
|
|
74
|
+
// this way of styling it is undocumented
|
|
75
|
+
dashStyle: '2 2',
|
|
76
|
+
width: 2,
|
|
77
|
+
value: referenceLineAnnotation.value,
|
|
78
|
+
zIndex: 3,
|
|
79
|
+
};
|
|
80
|
+
if (referenceLineAnnotation.axis === 'x') {
|
|
81
|
+
xAxisPlotLines.push(referenceLineConfig);
|
|
82
|
+
} else if (referenceLineAnnotation.axis === 'y') {
|
|
83
|
+
yAxisPlotLines.push(referenceLineConfig);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
xAxis: xAxisPlotLines.length > 0 ? { plotLines: xAxisPlotLines } : undefined,
|
|
88
|
+
yAxis: yAxisPlotLines.length > 0 ? { plotLines: yAxisPlotLines } : undefined,
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export default ReferenceLineAnnotationsOptions;
|
|
@@ -4,16 +4,11 @@ class ScatterChart {
|
|
|
4
4
|
constructor() {
|
|
5
5
|
this.chartConstants = ChartConstants.constants();
|
|
6
6
|
this.markerStyles = this.chartConstants.scatterMarkerStyles;
|
|
7
|
+
this.confidenceLevelMarkerStyles = this.chartConstants.confidenceLevelMarkerStyles;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
getScatterChartOptions = () => {
|
|
10
11
|
return {
|
|
11
|
-
plotOptions: {
|
|
12
|
-
scatter: {
|
|
13
|
-
// enable tooltips for scatter charts
|
|
14
|
-
enableMouseTracking: true,
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
12
|
xAxis: {
|
|
18
13
|
gridLineWidth: 1,
|
|
19
14
|
},
|
|
@@ -29,6 +24,20 @@ class ScatterChart {
|
|
|
29
24
|
series.update({ marker: this.markerStyles[i] != undefined ? this.markerStyles[i] : this.markerStyles[0] }, false);
|
|
30
25
|
});
|
|
31
26
|
};
|
|
27
|
+
|
|
28
|
+
updateMarkersForConfidenceLevels = (series) => {
|
|
29
|
+
series.forEach((series, i) => {
|
|
30
|
+
series.update(
|
|
31
|
+
{
|
|
32
|
+
marker:
|
|
33
|
+
this.confidenceLevelMarkerStyles[i] != undefined
|
|
34
|
+
? this.confidenceLevelMarkerStyles[i]
|
|
35
|
+
: this.confidenceLevelMarkerStyles[0],
|
|
36
|
+
},
|
|
37
|
+
false,
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
};
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
export default ScatterChart;
|
|
@@ -11,12 +11,33 @@ class SpecificChartOptions {
|
|
|
11
11
|
colors: this.theme === 'alternate' ? this.constants.alternateTheme : this.constants.primaryTheme,
|
|
12
12
|
chart: {
|
|
13
13
|
type: type,
|
|
14
|
-
marginTop: this.config.legend.enabled ? undefined : 50,
|
|
14
|
+
marginTop: this.config.legend.enabled ? (type === 'boxplot' ? 50 : undefined) : 50,
|
|
15
15
|
},
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
getOptions = () => this.options;
|
|
20
|
+
|
|
21
|
+
// Add zero line or custom reference line (but only for column or line charts)
|
|
22
|
+
getReferenceLine = (customReferenceLineValue, chartType) => {
|
|
23
|
+
let lineValue = 0;
|
|
24
|
+
if (customReferenceLineValue && (chartType === 'line' || chartType === 'column')) {
|
|
25
|
+
lineValue = customReferenceLineValue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
yAxis: {
|
|
30
|
+
plotLines: [
|
|
31
|
+
{
|
|
32
|
+
color: this.constants.zeroLineColor,
|
|
33
|
+
width: 1.5,
|
|
34
|
+
value: lineValue,
|
|
35
|
+
zIndex: 2,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
};
|
|
20
41
|
}
|
|
21
42
|
|
|
22
43
|
export default SpecificChartOptions;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Utility function to merge two configs together
|
|
2
|
+
export const mergeConfigs = (baseConfig, newConfig) => {
|
|
3
|
+
// If newConfig is null/undefined, return baseConfig
|
|
4
|
+
if (!newConfig) return baseConfig;
|
|
5
|
+
|
|
6
|
+
// Create a new object to store the merged result
|
|
7
|
+
const merged = { ...baseConfig };
|
|
8
|
+
|
|
9
|
+
// Iterate through all keys in newConfig
|
|
10
|
+
Object.keys(newConfig).forEach((key) => {
|
|
11
|
+
// Get values from both configs for this key
|
|
12
|
+
const baseValue = merged[key];
|
|
13
|
+
const newValue = newConfig[key];
|
|
14
|
+
|
|
15
|
+
// If both values are objects (and not null), recursively merge them
|
|
16
|
+
if (
|
|
17
|
+
baseValue &&
|
|
18
|
+
newValue &&
|
|
19
|
+
typeof baseValue === 'object' &&
|
|
20
|
+
typeof newValue === 'object' &&
|
|
21
|
+
!Array.isArray(baseValue) &&
|
|
22
|
+
!Array.isArray(newValue)
|
|
23
|
+
) {
|
|
24
|
+
merged[key] = mergeConfigs(baseValue, newValue);
|
|
25
|
+
} else {
|
|
26
|
+
// For non-objects and arrays use the new value
|
|
27
|
+
// If the new value is null/undefined, use the base value
|
|
28
|
+
merged[key] = newValue ?? baseValue;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return merged;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Helper function to process the range and reference line annotations plus any plot lines config
|
|
36
|
+
// ready to pass to the responsive rules
|
|
37
|
+
export const preparePlotLinesAndBands = (
|
|
38
|
+
annotations = undefined,
|
|
39
|
+
rangeAnnotations = undefined,
|
|
40
|
+
rangeAnnotationsOptions = undefined,
|
|
41
|
+
referenceLineAnnotationsOptions = undefined,
|
|
42
|
+
specificChartOptions,
|
|
43
|
+
chartType,
|
|
44
|
+
customReferenceLineValue,
|
|
45
|
+
) => {
|
|
46
|
+
const totalPointAndRangeAnnotations = (annotations ? annotations.length : 0) + (rangeAnnotations ? rangeAnnotations.length : 0);
|
|
47
|
+
|
|
48
|
+
// Both range and reference line annotations are added to the axis objects, so we need to correctly combine them before we can pass them to the config.
|
|
49
|
+
let desktopRangeAnnotations = {};
|
|
50
|
+
let mobileRangeAnnotations = {};
|
|
51
|
+
let desktopReferenceLineAnnotations = {};
|
|
52
|
+
let mobileReferenceLineAnnotations = {};
|
|
53
|
+
let desktopAllPlotLines = {};
|
|
54
|
+
let mobileAllPlotLines = {};
|
|
55
|
+
let desktopAllPlotLinesAndBands = {};
|
|
56
|
+
let mobileAllPlotLinesAndBands = {};
|
|
57
|
+
|
|
58
|
+
// get the desktop and mobile range annotations
|
|
59
|
+
if (rangeAnnotationsOptions) {
|
|
60
|
+
desktopRangeAnnotations = rangeAnnotationsOptions.getRangeAnnotationsOptionsDesktop(chartType);
|
|
61
|
+
mobileRangeAnnotations = rangeAnnotationsOptions.getRangeAnnotationsOptionsMobile(annotations ? annotations.length : 0, chartType);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// get the desktop and mobile reference line annotations
|
|
65
|
+
if (referenceLineAnnotationsOptions) {
|
|
66
|
+
desktopReferenceLineAnnotations = referenceLineAnnotationsOptions.getReferenceLineAnnotationsOptionsDesktop();
|
|
67
|
+
mobileReferenceLineAnnotations = referenceLineAnnotationsOptions.getReferenceLineAnnotationsOptionsMobile(
|
|
68
|
+
totalPointAndRangeAnnotations,
|
|
69
|
+
chartType,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// We also need to combine the zero line / custom reference line with the reference line annotations here, as otherwise
|
|
74
|
+
// it gets overridden by the reference line annotations config
|
|
75
|
+
let plotLineOptions = specificChartOptions.getReferenceLine(customReferenceLineValue, chartType);
|
|
76
|
+
desktopAllPlotLines = { ...desktopReferenceLineAnnotations };
|
|
77
|
+
mobileAllPlotLines = { ...mobileReferenceLineAnnotations };
|
|
78
|
+
|
|
79
|
+
if (desktopReferenceLineAnnotations.yAxis !== undefined) {
|
|
80
|
+
let desktopMergedPlotLines = desktopReferenceLineAnnotations.yAxis.plotLines.concat(plotLineOptions.yAxis.plotLines);
|
|
81
|
+
let mobileMergedPlotLines = mobileReferenceLineAnnotations.yAxis.plotLines.concat(plotLineOptions.yAxis.plotLines);
|
|
82
|
+
desktopAllPlotLines.yAxis.plotLines = desktopMergedPlotLines;
|
|
83
|
+
mobileAllPlotLines.yAxis.plotLines = mobileMergedPlotLines;
|
|
84
|
+
} else {
|
|
85
|
+
desktopAllPlotLines.yAxis = { ...plotLineOptions.yAxis };
|
|
86
|
+
mobileAllPlotLines.yAxis = { ...plotLineOptions.yAxis };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// combine the desktop and mobile range and reference line annotations, along with other plot lines, ready to pass to the config
|
|
90
|
+
desktopAllPlotLinesAndBands = mergeConfigs(desktopRangeAnnotations, desktopAllPlotLines);
|
|
91
|
+
mobileAllPlotLinesAndBands = mergeConfigs(mobileRangeAnnotations, mobileAllPlotLines);
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
desktopAllPlotLinesAndBands,
|
|
95
|
+
mobileAllPlotLinesAndBands,
|
|
96
|
+
};
|
|
97
|
+
};
|
|
@@ -164,7 +164,7 @@ describe('macro: checkboxes', () => {
|
|
|
164
164
|
it('renders the text area with expected parameters', () => {
|
|
165
165
|
const $ = cheerio.load(renderComponent('checkboxes', EXAMPLE_CHECKBOX_ITEM_CHECKBOXES_WITH_TEXTAREA));
|
|
166
166
|
expect($('.ons-input--textarea').attr('name')).toBe('other answer');
|
|
167
|
-
expect($('.ons-input--textarea').attr('
|
|
167
|
+
expect($('.ons-input--textarea').attr('data-char-check-num')).toBe('300');
|
|
168
168
|
expect($('.ons-input__limit').attr('data-charcount-singular')).toBe('You have {x} character remaining');
|
|
169
169
|
expect($('.ons-input__limit').attr('data-charcount-plural')).toBe('You have {x} characters remaining');
|
|
170
170
|
});
|
|
@@ -65,7 +65,7 @@ describe('script: mutually-exclusive', () => {
|
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
it('then the characters remaining readout should be reset', async () => {
|
|
68
|
-
const limitText = await page.$eval('#feedback-
|
|
68
|
+
const limitText = await page.$eval('#feedback-check', (node) => node.textContent);
|
|
69
69
|
expect(limitText).toBe('You have 200 characters remaining');
|
|
70
70
|
});
|
|
71
71
|
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
|
|
20
20
|
<textarea
|
|
21
21
|
id="{{ params.id }}"
|
|
22
|
-
class="ons-input ons-input--textarea{{ ' ons-input--error' if params.error }}{{ ' ons-js-char-
|
|
22
|
+
class="ons-input ons-input--textarea{{ ' ons-input--error' if params.error }}{{ ' ons-js-char-check-input' if params.charCheckLimit and params.charCheckLimit.limit }}{{ textareaExclusiveClass }}{{ ' ' + params.classes if params.classes else '' }}{% if params.width %}{{ ' ' }}ons-input--w-{{ params.width }}{% endif %}"
|
|
23
23
|
{% if params.name %}
|
|
24
24
|
name="{{ params.name }}"
|
|
25
25
|
{% endif %}
|
|
26
26
|
rows="{{ params.rows | default(8) }}"
|
|
27
|
-
{% if params.charCheckLimit
|
|
28
|
-
|
|
29
|
-
aria-describedby="{{ params.id }}-
|
|
27
|
+
{% if params.charCheckLimit %}
|
|
28
|
+
data-char-check-ref="{{ params.id }}-check" data-char-check-num="{{ params.charCheckLimit.limit }}"
|
|
29
|
+
aria-describedby="{{ params.id }}-check" data-char-check-countdown=true
|
|
30
30
|
{% endif %}
|
|
31
31
|
{% if params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{ ' ' }}{{ attribute }}{% if value %}="{{ value }}"{% endif %}{% endfor %}{% endif %}
|
|
32
32
|
>
|
|
@@ -36,10 +36,12 @@
|
|
|
36
36
|
{% if params.charCheckLimit and params.charCheckLimit.limit %}
|
|
37
37
|
{%
|
|
38
38
|
call onsCharLimit({
|
|
39
|
-
"id": params.id ~ "-
|
|
39
|
+
"id": params.id ~ "-check",
|
|
40
40
|
"limit": params.charCheckLimit.limit,
|
|
41
41
|
"charCountSingular": params.charCheckLimit.charCountSingular,
|
|
42
|
-
"charCountPlural": params.charCheckLimit.charCountPlural
|
|
42
|
+
"charCountPlural": params.charCheckLimit.charCountPlural,
|
|
43
|
+
"charCountOverLimitSingular": params.charCheckLimit.charCountOverLimitSingular,
|
|
44
|
+
"charCountOverLimitPlural": params.charCheckLimit.charCountOverLimitPlural
|
|
43
45
|
})
|
|
44
46
|
%}
|
|
45
47
|
{% endcall %}
|
|
@@ -22,6 +22,8 @@ const EXAMPLE_TEXTAREA_WITH_CHARACTER_LIMIT = {
|
|
|
22
22
|
limit: 200,
|
|
23
23
|
charCountSingular: 'You have {x} character remaining',
|
|
24
24
|
charCountPlural: 'You have {x} characters remaining',
|
|
25
|
+
charCountOverLimitSingular: '{x} character too many',
|
|
26
|
+
charCountOverLimitPlural: '{x} characters too many',
|
|
25
27
|
},
|
|
26
28
|
};
|
|
27
29
|
|
|
@@ -200,29 +202,29 @@ describe('macro: textarea', () => {
|
|
|
200
202
|
});
|
|
201
203
|
});
|
|
202
204
|
|
|
203
|
-
describe('with character
|
|
204
|
-
it('has the `ons-js-char-
|
|
205
|
+
describe('with character check', () => {
|
|
206
|
+
it('has the `ons-js-char-check-input` class', () => {
|
|
205
207
|
const $ = cheerio.load(renderComponent('textarea', EXAMPLE_TEXTAREA_WITH_CHARACTER_LIMIT));
|
|
206
208
|
|
|
207
|
-
expect($('.ons-input--textarea').hasClass('ons-js-char-
|
|
209
|
+
expect($('.ons-input--textarea').hasClass('ons-js-char-check-input')).toBe(true);
|
|
208
210
|
});
|
|
209
211
|
|
|
210
212
|
it('has the provided maximum length', () => {
|
|
211
213
|
const $ = cheerio.load(renderComponent('textarea', EXAMPLE_TEXTAREA_WITH_CHARACTER_LIMIT));
|
|
212
214
|
|
|
213
|
-
expect($('.ons-input--textarea').attr('
|
|
215
|
+
expect($('.ons-input--textarea').attr('data-char-check-num')).toBe('200');
|
|
214
216
|
});
|
|
215
217
|
|
|
216
|
-
it('has data attribute which references the character
|
|
218
|
+
it('has data attribute which references the character check component', () => {
|
|
217
219
|
const $ = cheerio.load(renderComponent('textarea', EXAMPLE_TEXTAREA_WITH_CHARACTER_LIMIT));
|
|
218
220
|
|
|
219
|
-
expect($('.ons-input--textarea').attr('data-char-
|
|
221
|
+
expect($('.ons-input--textarea').attr('data-char-check-ref')).toBe('example-id-check');
|
|
220
222
|
});
|
|
221
223
|
|
|
222
224
|
it('has `aria-describedby` attribute which references the character limit component', () => {
|
|
223
225
|
const $ = cheerio.load(renderComponent('textarea', EXAMPLE_TEXTAREA_WITH_CHARACTER_LIMIT));
|
|
224
226
|
|
|
225
|
-
expect($('.ons-input--textarea').attr('aria-describedby')).toBe('example-id-
|
|
227
|
+
expect($('.ons-input--textarea').attr('aria-describedby')).toBe('example-id-check');
|
|
226
228
|
});
|
|
227
229
|
|
|
228
230
|
it('renders character limit component', () => {
|
|
@@ -232,10 +234,12 @@ describe('macro: textarea', () => {
|
|
|
232
234
|
faker.renderComponent('textarea', EXAMPLE_TEXTAREA_WITH_CHARACTER_LIMIT);
|
|
233
235
|
|
|
234
236
|
expect(charCheckLimitSpy.occurrences).toContainEqual({
|
|
235
|
-
id: 'example-id-
|
|
237
|
+
id: 'example-id-check',
|
|
236
238
|
limit: 200,
|
|
237
239
|
charCountSingular: 'You have {x} character remaining',
|
|
238
240
|
charCountPlural: 'You have {x} characters remaining',
|
|
241
|
+
charCountOverLimitSingular: '{x} character too many',
|
|
242
|
+
charCountOverLimitPlural: '{x} characters too many',
|
|
239
243
|
});
|
|
240
244
|
});
|
|
241
245
|
});
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"charCheckLimit": {
|
|
13
13
|
"limit": 200,
|
|
14
14
|
"charCountSingular": "You have {x} character remaining",
|
|
15
|
-
"charCountPlural": "You have {x} characters remaining"
|
|
15
|
+
"charCountPlural": "You have {x} characters remaining",
|
|
16
|
+
"charCountOverLimitSingular": "{x} character too many",
|
|
17
|
+
"charCountOverLimitPlural": "{x} characters too many"
|
|
16
18
|
}
|
|
17
19
|
})
|
|
18
20
|
}}
|