@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.
Files changed (48) hide show
  1. package/components/char-check-limit/_macro.njk +2 -2
  2. package/components/char-check-limit/character-check.js +30 -9
  3. package/components/char-check-limit/character-check.spec.js +1 -1
  4. package/components/chart/_chart.scss +73 -1
  5. package/components/chart/_macro.njk +90 -20
  6. package/components/chart/_macro.spec.js +424 -0
  7. package/components/chart/bar-chart.js +46 -20
  8. package/components/chart/boxplot.js +37 -0
  9. package/components/chart/chart-constants.js +14 -0
  10. package/components/chart/chart.js +102 -46
  11. package/components/chart/columnrange-chart.js +94 -0
  12. package/components/chart/common-chart-options.js +65 -23
  13. package/components/chart/example-bar-chart-with-annotations.njk +1 -1
  14. package/components/chart/example-bar-chart-with-point-range-and-reference-line-annotations.njk +95 -0
  15. package/components/chart/example-bar-with-confidence-levels.njk +71 -0
  16. package/components/chart/example-clustered-column-chart.njk +1 -3
  17. package/components/chart/example-column-chart-with-annotations.njk +1 -1
  18. package/components/chart/example-column-chart-with-custom-reference-line-value.njk +56 -0
  19. package/components/chart/example-column-chart-with-range-annotations.njk +64 -0
  20. package/components/chart/example-column-chart-with-reference-line-annotations.njk +64 -0
  21. package/components/chart/example-column-with-confidence-levels.njk +61 -0
  22. package/components/chart/example-line-chart-with-annotations.njk +3 -3
  23. package/components/chart/example-line-chart-with-custom-reference-line-value.njk +224 -0
  24. package/components/chart/example-line-chart-with-markers.njk +21 -21
  25. package/components/chart/example-line-chart-with-range-annotations-inside.njk +238 -0
  26. package/components/chart/example-line-chart-with-range-annotations-outside-left-right.njk +240 -0
  27. package/components/chart/example-line-chart-with-range-annotations-outside-top-bottom.njk +239 -0
  28. package/components/chart/example-line-chart-with-reference-line-annotations.njk +236 -0
  29. package/components/chart/example-scatter-chart.njk +5 -5
  30. package/components/chart/line-chart.js +29 -11
  31. package/components/chart/range-annotations-options.js +221 -0
  32. package/components/chart/reference-line-annotations-options.js +93 -0
  33. package/components/chart/scatter-chart.js +15 -6
  34. package/components/chart/specific-chart-options.js +22 -1
  35. package/components/chart/utilities.js +97 -0
  36. package/components/checkboxes/_macro.spec.js +1 -1
  37. package/components/mutually-exclusive/mutually-exclusive.textarea.spec.js +1 -1
  38. package/components/textarea/_macro.njk +8 -6
  39. package/components/textarea/_macro.spec.js +12 -8
  40. package/components/textarea/{example-textarea-with-character-limit.njk → example-textarea-with-character-check.njk} +3 -1
  41. package/css/main.css +1 -1
  42. package/js/main.js +0 -1
  43. package/package.json +14 -14
  44. package/scripts/main.es5.js +1 -1
  45. package/scripts/main.js +1 -1
  46. package/components/char-check-limit/character-limit.js +0 -55
  47. package/components/textarea/textarea.dom.js +0 -12
  48. package/components/textarea/textarea.spec.js +0 -98
@@ -7,6 +7,7 @@ import { renderComponent } from '../../tests/helpers/rendering';
7
7
  import {
8
8
  EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
9
9
  EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS,
10
+ EXAMPLE_LINE_CHART_WITH_LEGEND_UNSET_PARAMS,
10
11
  EXAMPLE_BAR_CHART_PARAMS,
11
12
  EXAMPLE_BAR_CHART_WITH_PERCENTAGE_HEIGHT_PARAMS,
12
13
  EXAMPLE_COLUMN_CHART_PARAMS,
@@ -16,7 +17,14 @@ import {
16
17
  EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS,
17
18
  EXAMPLE_SCATTER_CHART_PARAMS,
18
19
  EXAMPLE_AREA_CHART_PARAMS,
20
+ EXAMPLE_BOXPLOT_CHART_PARAMS,
21
+ EXAMPLE_COLUMN_RANGE_CHART_PARAMS,
19
22
  EXAMPLE_INVALID_CHART_PARAMS,
23
+ EXAMPLE_LINE_CHART_WITH_RANGE_ANNOTATION_ON_X_AXIS_PARAMS,
24
+ EXAMPLE_LINE_CHART_WITH_RANGE_ANNOTATION_ON_Y_AXIS_WITH_LABEL_WIDTH_PARAMS,
25
+ EXAMPLE_LINE_CHART_WITH_RANGE_ANNOTATION_WITH_LABEL_INSIDE_PARAMS,
26
+ EXAMPLE_LINE_CHART_WITH_REFERENCE_LINE_ANNOTATIONS_PARAMS,
27
+ EXAMPLE_LINE_CHART_WITH_MIXED_ANNOTATION_TYPES_PARAMS,
20
28
  } from './_test-examples';
21
29
 
22
30
  describe('Macro: Chart', () => {
@@ -240,6 +248,55 @@ describe('Macro: Chart', () => {
240
248
  });
241
249
  });
242
250
  });
251
+
252
+ describe('GIVEN: Params: Legend', () => {
253
+ describe('WHEN: legend parameter is provided and set to true', () => {
254
+ const $ = cheerio.load(
255
+ renderComponent('chart', {
256
+ ...EXAMPLE_LINE_CHART_WITH_LEGEND_UNSET_PARAMS,
257
+ legend: true,
258
+ }),
259
+ );
260
+ test('THEN: it renders the legend', () => {
261
+ const configScript = $(`script[data-highcharts-config--line-chart-legend-tests-123]`).html();
262
+ expect(configScript).toContain('"legend":{"enabled":true}');
263
+ });
264
+ });
265
+
266
+ describe('WHEN: legend parameter is provided and set to false', () => {
267
+ const $ = cheerio.load(
268
+ renderComponent('chart', {
269
+ ...EXAMPLE_LINE_CHART_WITH_LEGEND_UNSET_PARAMS,
270
+ legend: false,
271
+ }),
272
+ );
273
+ test('THEN: it does not render the legend', () => {
274
+ const configScript = $(`script[data-highcharts-config--line-chart-legend-tests-123]`).html();
275
+ expect(configScript).toContain('"legend":{"enabled":false}');
276
+ });
277
+ });
278
+
279
+ describe('WHEN: legend parameter is not provided', () => {
280
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_LEGEND_UNSET_PARAMS));
281
+ test('THEN: it renders the legend', () => {
282
+ const configScript = $(`script[data-highcharts-config--line-chart-legend-tests-123]`).html();
283
+ expect(configScript).toContain('"legend":{"enabled":true}');
284
+ });
285
+ });
286
+
287
+ describe('WHEN: legend parameter is provided but is not a boolean', () => {
288
+ const $ = cheerio.load(
289
+ renderComponent('chart', {
290
+ ...EXAMPLE_LINE_CHART_WITH_LEGEND_UNSET_PARAMS,
291
+ legend: 'false',
292
+ }),
293
+ );
294
+ test('THEN: it renders the legend', () => {
295
+ const configScript = $(`script[data-highcharts-config--line-chart-legend-tests-123]`).html();
296
+ expect(configScript).toContain('"legend":{"enabled":true}');
297
+ });
298
+ });
299
+ });
243
300
  });
244
301
 
245
302
  describe('FOR: Bar Chart', () => {
@@ -645,6 +702,7 @@ describe('Macro: Chart', () => {
645
702
  expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('alternate');
646
703
  expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Column Chart');
647
704
  expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('column-chart-123');
705
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-custom-reference-line-value')).toBe('10');
648
706
  });
649
707
 
650
708
  test('THEN: it includes the Highcharts JSON config', () => {
@@ -652,6 +710,24 @@ describe('Macro: Chart', () => {
652
710
  expect(configScript).toContain('"text":"Y Axis Title"');
653
711
  });
654
712
  });
713
+ describe('WHEN: more than one line is provided', () => {
714
+ const $ = cheerio.load(
715
+ renderComponent('chart', {
716
+ ...EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS,
717
+ series: [
718
+ ...EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS.series,
719
+ { name: 'Additional Line', data: [15, 25, 35], type: 'line' },
720
+ { name: 'Another additional Line', data: [14, 27, 31], type: 'line' },
721
+ ],
722
+ }),
723
+ );
724
+ const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
725
+
726
+ test('THEN: it does not include the additional line series', () => {
727
+ const lineTypeMatches = (configScript.match(/"type":"line"/g) || []).length;
728
+ expect(lineTypeMatches).toBe(1);
729
+ });
730
+ });
655
731
  });
656
732
 
657
733
  describe('GIVEN: Params: Legend', () => {
@@ -879,6 +955,236 @@ describe('Macro: Chart', () => {
879
955
  });
880
956
  });
881
957
 
958
+ describe('FOR: Boxplot Chart', () => {
959
+ describe('GIVEN: Params: required', () => {
960
+ describe('WHEN: required params are provided', () => {
961
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_BOXPLOT_CHART_PARAMS));
962
+ const configScript = $(`script[data-highcharts-config--uuid]`).html();
963
+
964
+ test('THEN: it passes jest-axe checks', async () => {
965
+ const results = await axe($.html());
966
+ expect(results).toHaveNoViolations();
967
+ });
968
+
969
+ test('THEN: it includes at least one boxplot series', () => {
970
+ expect(configScript).toContain('"type":"boxplot"');
971
+ });
972
+
973
+ test('THEN: it renders the chart container with correct data attributes', () => {
974
+ const chartContainer = $('[data-highcharts-base-chart]');
975
+ expect(chartContainer.attr('data-highcharts-type')).toBe('boxplot');
976
+ expect(chartContainer.attr('data-highcharts-theme')).toBe('primary');
977
+ expect(chartContainer.attr('data-highcharts-title')).toBe('Example Boxplot Chart');
978
+ expect(chartContainer.attr('data-highcharts-id')).toBe('uuid');
979
+ });
980
+
981
+ test('THEN: it includes the Highcharts JSON config', () => {
982
+ expect(configScript).toContain('"text":"Years"');
983
+ expect(configScript).toContain('"text":"Percentage of GDP"');
984
+ });
985
+ });
986
+ });
987
+
988
+ describe('GIVEN: Params: Legend', () => {
989
+ describe('WHEN: both labels are provided and legend is enabled', () => {
990
+ const $ = cheerio.load(
991
+ renderComponent('chart', {
992
+ ...EXAMPLE_BOXPLOT_CHART_PARAMS,
993
+ estimateLineLabel: 'Estimated value',
994
+ uncertaintyRangeLabel: '95% Confidence Interval',
995
+ legend: true,
996
+ }),
997
+ );
998
+
999
+ test('THEN: it renders the boxplot legend container', () => {
1000
+ const legendContainer = $('.ons-chart__boxplot-legend');
1001
+ expect(legendContainer.length).toBe(1);
1002
+ });
1003
+
1004
+ test('THEN: it includes the estimate line legend item', () => {
1005
+ const estimateLabel = $('.ons-chart__boxplot-legend-label').filter((_, el) => $(el).text().includes('Estimated value'));
1006
+ expect(estimateLabel.length).toBe(1);
1007
+
1008
+ const estimateSwatch = $('.ons-chart__boxplot-legend-item--estimate');
1009
+ expect(estimateSwatch.length).toBe(1);
1010
+ });
1011
+
1012
+ test('THEN: it includes the uncertainty range legend item', () => {
1013
+ const uncertaintyLabel = $('.ons-chart__boxplot-legend-label').filter((_, el) =>
1014
+ $(el).text().includes('95% Confidence Interval'),
1015
+ );
1016
+ expect(uncertaintyLabel.length).toBe(1);
1017
+
1018
+ const uncertaintySwatch = $('.ons-chart__boxplot-legend-item--uncertainty');
1019
+ expect(uncertaintySwatch.length).toBe(1);
1020
+ });
1021
+ });
1022
+
1023
+ describe('WHEN: only estimateLineLabel is provided', () => {
1024
+ const $ = cheerio.load(
1025
+ renderComponent('chart', {
1026
+ ...EXAMPLE_BOXPLOT_CHART_PARAMS,
1027
+ estimateLineLabel: 'Estimated only',
1028
+ legend: true,
1029
+ }),
1030
+ );
1031
+
1032
+ test('THEN: it renders only the estimate legend item', () => {
1033
+ expect($('.ons-chart__boxplot-legend-item--estimate').length).toBe(1);
1034
+ expect($('.ons-chart__boxplot-legend-item--uncertainty').length).toBe(0);
1035
+ });
1036
+ });
1037
+ describe('WHEN: only uncertaintyRangeLabel is provided', () => {
1038
+ const $ = cheerio.load(
1039
+ renderComponent('chart', {
1040
+ ...EXAMPLE_BOXPLOT_CHART_PARAMS,
1041
+ uncertaintyRangeLabel: 'Uncertainty only',
1042
+ legend: true,
1043
+ }),
1044
+ );
1045
+ test('THEN: it renders only the uncertainty legend item', () => {
1046
+ expect($('.ons-chart__boxplot-legend-item--estimate').length).toBe(0);
1047
+ expect($('.ons-chart__boxplot-legend-item--uncertainty').length).toBe(1);
1048
+ });
1049
+ });
1050
+ describe('WHEN: estimate and uncertainty labels are provided but legend is false', () => {
1051
+ const $ = cheerio.load(
1052
+ renderComponent('chart', {
1053
+ ...EXAMPLE_BOXPLOT_CHART_PARAMS,
1054
+ estimateLineLabel: 'Estimated value',
1055
+ uncertaintyRangeLabel: '95% Confidence Interval',
1056
+ legend: false,
1057
+ }),
1058
+ );
1059
+
1060
+ test('THEN: it does NOT render the boxplot legend container', () => {
1061
+ expect($('.ons-chart__boxplot-legend').length).toBe(0);
1062
+ });
1063
+
1064
+ test('THEN: it does NOT render any custom legend items', () => {
1065
+ expect($('.ons-chart__boxplot-legend-item--estimate').length).toBe(0);
1066
+ expect($('.ons-chart__boxplot-legend-item--uncertainty').length).toBe(0);
1067
+ });
1068
+ });
1069
+ });
1070
+
1071
+ describe('GIVEN: Params: Description', () => {
1072
+ describe('WHEN: description is provided', () => {
1073
+ const accessibleDescription = 'An accessible description for screen readers.';
1074
+ const $ = cheerio.load(
1075
+ renderComponent('chart', {
1076
+ ...EXAMPLE_BOXPLOT_CHART_PARAMS,
1077
+ description: accessibleDescription,
1078
+ }),
1079
+ );
1080
+
1081
+ test('THEN: it renders the description for accessibility', () => {
1082
+ expect($('.ons-u-vh').text()).toBe(accessibleDescription);
1083
+ });
1084
+ });
1085
+ });
1086
+
1087
+ describe('GIVEN: Params: Estimate Line and Uncertainty Range Labels', () => {
1088
+ describe('WHEN: both labels are provided', () => {
1089
+ const $ = cheerio.load(
1090
+ renderComponent('chart', {
1091
+ ...EXAMPLE_BOXPLOT_CHART_PARAMS,
1092
+ estimateLineLabel: 'Estimated value',
1093
+ uncertaintyRangeLabel: '95% Confidence Interval',
1094
+ }),
1095
+ );
1096
+
1097
+ test('THEN: it sets the estimate line label as a data attribute', () => {
1098
+ const baseChart = $('[data-highcharts-base-chart]');
1099
+ expect(baseChart.attr('data-highcharts-estimate-line-label')).toBe('Estimated value');
1100
+ });
1101
+
1102
+ test('THEN: it sets the uncertainty range label as a data attribute', () => {
1103
+ const baseChart = $('[data-highcharts-base-chart]');
1104
+ expect(baseChart.attr('data-highcharts-uncertainty-range-label')).toBe('95% Confidence Interval');
1105
+ });
1106
+ });
1107
+ });
1108
+ });
1109
+
1110
+ describe('FOR: Column Range Chart', () => {
1111
+ describe('GIVEN: Params: Required', () => {
1112
+ describe('WHEN: required params are provided', () => {
1113
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_COLUMN_RANGE_CHART_PARAMS));
1114
+ const configScript = $(`script[data-highcharts-config--uuid]`).html();
1115
+
1116
+ test('THEN: it passes jest-axe accessibility checks', async () => {
1117
+ const results = await axe($.html());
1118
+ expect(results).toHaveNoViolations();
1119
+ });
1120
+
1121
+ test('THEN: it renders with correct chart metadata attributes', () => {
1122
+ const baseChart = $('[data-highcharts-base-chart]');
1123
+ expect(baseChart.attr('data-highcharts-type')).toBe('columnrange');
1124
+ expect(baseChart.attr('data-highcharts-theme')).toBe('primary');
1125
+ expect(baseChart.attr('data-highcharts-title')).toBe(
1126
+ 'Food stores showed a strong rise on the month, while non-food stores fell',
1127
+ );
1128
+ expect(baseChart.attr('data-highcharts-id')).toBe('uuid');
1129
+ });
1130
+
1131
+ test('THEN: it includes columnrange and scatter series types', () => {
1132
+ expect(configScript).toContain('"type":"columnrange"');
1133
+ expect(configScript).toContain('"type":"scatter"');
1134
+ });
1135
+
1136
+ describe('GIVEN: Params: isChartInverted', () => {
1137
+ describe('WHEN: isChartInverted parameter is provided and set to true', () => {
1138
+ const $ = cheerio.load(
1139
+ renderComponent('chart', {
1140
+ ...EXAMPLE_COLUMN_RANGE_CHART_PARAMS,
1141
+ isChartInverted: true,
1142
+ }),
1143
+ );
1144
+ test('THEN: it sets isChartInverted to true', () => {
1145
+ const configScript = $(`script[data-highcharts-config--uuid]`).html();
1146
+ expect(configScript).toContain('"chart":{"type":"columnrange","inverted":true}');
1147
+ });
1148
+ });
1149
+
1150
+ describe('WHEN: isChartInverted parameter is provided and set to false', () => {
1151
+ const $ = cheerio.load(
1152
+ renderComponent('chart', {
1153
+ ...EXAMPLE_COLUMN_RANGE_CHART_PARAMS,
1154
+ isChartInverted: false,
1155
+ }),
1156
+ );
1157
+ test('THEN: it sets isChartInverted to false', () => {
1158
+ const configScript = $(`script[data-highcharts-config--uuid]`).html();
1159
+ expect(configScript).toContain('"chart":{"type":"columnrange","inverted":false}');
1160
+ });
1161
+ });
1162
+
1163
+ describe('WHEN: isChartInverted parameter is not provided', () => {
1164
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_COLUMN_RANGE_CHART_PARAMS));
1165
+ test('THEN: it sets isChartInverted to false', () => {
1166
+ const configScript = $(`script[data-highcharts-config--uuid]`).html();
1167
+ expect(configScript).toContain('"chart":{"type":"columnrange","inverted":false}');
1168
+ });
1169
+ });
1170
+
1171
+ describe('WHEN: isChartInverted parameter is provided but is not a boolean', () => {
1172
+ const $ = cheerio.load(
1173
+ renderComponent('chart', {
1174
+ ...EXAMPLE_COLUMN_RANGE_CHART_PARAMS,
1175
+ isChartInverted: 'false',
1176
+ }),
1177
+ );
1178
+ test('THEN: it sets isChartInverted to false', () => {
1179
+ const configScript = $(`script[data-highcharts-config--uuid]`).html();
1180
+ expect(configScript).toContain('"chart":{"type":"columnrange","inverted":false}');
1181
+ });
1182
+ });
1183
+ });
1184
+ });
1185
+ });
1186
+ });
1187
+
882
1188
  describe('FOR: Invalid Chart', () => {
883
1189
  describe('GIVEN: Invalid chart type', () => {
884
1190
  describe('WHEN: an invalid chart type is provided', () => {
@@ -919,4 +1225,122 @@ describe('Macro: Chart', () => {
919
1225
  });
920
1226
  });
921
1227
  });
1228
+
1229
+ describe('FOR: Line chart with range annotation on the x axis', () => {
1230
+ describe('GIVEN: Params: Range annotations', () => {
1231
+ describe('WHEN: range annotations params are provided', () => {
1232
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_RANGE_ANNOTATION_ON_X_AXIS_PARAMS));
1233
+
1234
+ test('THEN: it passes jest-axe checks', async () => {
1235
+ const results = await axe($.html());
1236
+ expect(results).toHaveNoViolations();
1237
+ });
1238
+
1239
+ test('THEN: it includes the range annotations JSON config', () => {
1240
+ const configScript = $(`script[data-highcharts-range-annotations--line-chart-range-annotations-x-axis-123]`).html();
1241
+ expect(configScript).toContain('"text":"A test x axis range annotation"');
1242
+ expect(configScript).toContain('"range":{"axisValue1":10,"axisValue2":15}');
1243
+ expect(configScript).toContain('"axis":"x"');
1244
+ expect(configScript).toContain('"labelOffsetX":150');
1245
+ expect(configScript).toContain('"labelOffsetY":0');
1246
+ });
1247
+ });
1248
+ });
1249
+ });
1250
+
1251
+ describe('FOR: Line chart with range annotation on the y axis with label width', () => {
1252
+ describe('GIVEN: Params: Range annotations', () => {
1253
+ describe('WHEN: range annotations params are provided', () => {
1254
+ const $ = cheerio.load(
1255
+ renderComponent('chart', EXAMPLE_LINE_CHART_WITH_RANGE_ANNOTATION_ON_Y_AXIS_WITH_LABEL_WIDTH_PARAMS),
1256
+ );
1257
+
1258
+ test('THEN: it passes jest-axe checks', async () => {
1259
+ const results = await axe($.html());
1260
+ expect(results).toHaveNoViolations();
1261
+ });
1262
+
1263
+ test('THEN: it includes the range annotations JSON config', () => {
1264
+ const configScript = $(`script[data-highcharts-range-annotations--line-chart-range-annotations-y-axis-123]`).html();
1265
+ expect(configScript).toContain('"text":"A test y axis range annotation with a label width of 250px"');
1266
+ expect(configScript).toContain('"axis":"y"');
1267
+ expect(configScript).toContain('"labelWidth":250');
1268
+ });
1269
+ });
1270
+ });
1271
+ });
1272
+
1273
+ describe('FOR: Line chart with range annotation with the label inside', () => {
1274
+ describe('GIVEN: Params: Range annotations', () => {
1275
+ describe('WHEN: range annotations params are provided', () => {
1276
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_RANGE_ANNOTATION_WITH_LABEL_INSIDE_PARAMS));
1277
+
1278
+ test('THEN: it passes jest-axe checks', async () => {
1279
+ const results = await axe($.html());
1280
+ expect(results).toHaveNoViolations();
1281
+ });
1282
+
1283
+ test('THEN: it includes the range annotations JSON config', () => {
1284
+ const configScript = $(
1285
+ `script[data-highcharts-range-annotations--line-chart-range-annotations-label-inside-123]`,
1286
+ ).html();
1287
+ expect(configScript).toContain('"text":"A test y axis range annotation with the label inside"');
1288
+ expect(configScript).toContain('"axis":"y"');
1289
+ expect(configScript).toContain('"labelInside":true');
1290
+ });
1291
+ });
1292
+ });
1293
+ });
1294
+
1295
+ describe('FOR: Line chart with reference line annotations', () => {
1296
+ describe('GIVEN: Params: Reference line annotations', () => {
1297
+ describe('WHEN: reference line annotations params are provided', () => {
1298
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_REFERENCE_LINE_ANNOTATIONS_PARAMS));
1299
+
1300
+ test('THEN: it passes jest-axe checks', async () => {
1301
+ const results = await axe($.html());
1302
+ expect(results).toHaveNoViolations();
1303
+ });
1304
+
1305
+ test('THEN: it includes the reference line annotations JSON config', () => {
1306
+ const configScript = $(
1307
+ `script[data-highcharts-reference-line-annotations--line-chart-reference-line-annotations-123]`,
1308
+ ).html();
1309
+ expect(configScript).toContain('"text":"A test x axis reference line annotation"');
1310
+ expect(configScript).toContain('"value":34');
1311
+ expect(configScript).toContain('"axis":"x"');
1312
+ expect(configScript).toContain('"text":"A test y axis reference line annotation"');
1313
+ expect(configScript).toContain('"value":12');
1314
+ expect(configScript).toContain('"axis":"y"');
1315
+ expect(configScript).toContain('"labelWidth":100');
1316
+ });
1317
+ });
1318
+ });
1319
+ });
1320
+
1321
+ describe('FOR: Line chart with mixed annotation types', () => {
1322
+ describe('GIVEN: Params: Mixed annotations', () => {
1323
+ describe('WHEN: mixed annotations params are provided', () => {
1324
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_MIXED_ANNOTATION_TYPES_PARAMS));
1325
+
1326
+ test('THEN: it passes jest-axe checks', async () => {
1327
+ const results = await axe($.html());
1328
+ expect(results).toHaveNoViolations();
1329
+ });
1330
+
1331
+ test('THEN: it renders the footnotes sequentially', () => {
1332
+ expect($('.ons-chart__footnotes').text()).toContain('1');
1333
+ expect($('.ons-chart__footnotes').text()).toContain('A test point annotation');
1334
+ expect($('.ons-chart__footnotes').text()).toContain('2');
1335
+ expect($('.ons-chart__footnotes').text()).toContain('A test x axis range annotation');
1336
+ expect($('.ons-chart__footnotes').text()).toContain('3');
1337
+ expect($('.ons-chart__footnotes').text()).toContain('A test y axis range annotation with the label inside');
1338
+ expect($('.ons-chart__footnotes').text()).toContain('4');
1339
+ expect($('.ons-chart__footnotes').text()).toContain('A test x axis reference line annotation');
1340
+ expect($('.ons-chart__footnotes').text()).toContain('5');
1341
+ expect($('.ons-chart__footnotes').text()).toContain('A test y axis reference line annotation');
1342
+ });
1343
+ });
1344
+ });
1345
+ });
922
1346
  });
@@ -70,29 +70,55 @@ class BarChart {
70
70
  };
71
71
 
72
72
  // Updates the config to move the data labels inside the bars, but only if the bar is wide enough
73
- // This may also need to run when the chart is resized
73
+ // This also runs when the chart is resized
74
74
  postLoadDataLabels = (currentChart) => {
75
- const insideOptions = {
76
- dataLabels: this.getBarChartLabelsInsideOptions(),
77
- };
78
- const outsideOptions = {
79
- dataLabels: this.getBarChartLabelsOutsideOptions(),
80
- };
81
-
75
+ const insideOptions = this.getBarChartLabelsInsideOptions();
76
+ const outsideOptions = this.getBarChartLabelsOutsideOptions();
82
77
  currentChart.series.forEach((series) => {
78
+ // If we have a bar chart with an extra line, exit early for the line series
79
+ if (series.type == 'line') {
80
+ return;
81
+ }
82
+
83
+ if (series.type == 'scatter') {
84
+ // If we have a bar chart with confidence levels, exit early for the scatter series
85
+ return;
86
+ }
87
+
83
88
  const points = series.data;
89
+
84
90
  points.forEach((point) => {
85
- // Get the actual width of the data label
86
- const labelWidth = point.dataLabel && point.dataLabel.getBBox().width;
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
- }
92
- if (point.shapeArgs.height > labelWidth + 20) {
93
- point.update(insideOptions, false);
94
- } else {
95
- point.update(outsideOptions, false);
91
+ if (point.dataLabel) {
92
+ // Get the actual width of the data label
93
+ const labelWidth = point.dataLabel.getBBox().width;
94
+
95
+ // Move the data labels inside the bar if the bar is wider than the label plus some padding
96
+ if (point.shapeArgs.height > labelWidth + 5) {
97
+ // Negative values are aligned on the left, positive values on the right
98
+ if (point.y < 0) {
99
+ point.update(
100
+ {
101
+ dataLabels: {
102
+ ...insideOptions,
103
+ align: 'left',
104
+ },
105
+ },
106
+ false,
107
+ );
108
+ } else {
109
+ point.update(
110
+ {
111
+ dataLabels: {
112
+ ...insideOptions,
113
+ align: 'right',
114
+ },
115
+ },
116
+ false,
117
+ );
118
+ }
119
+ } else {
120
+ point.update({ dataLabels: outsideOptions }, false);
121
+ }
96
122
  }
97
123
  });
98
124
  });
@@ -102,7 +128,6 @@ class BarChart {
102
128
 
103
129
  getBarChartLabelsInsideOptions = () => ({
104
130
  inside: true,
105
- align: 'right',
106
131
  verticalAlign: 'middle',
107
132
  style: {
108
133
  color: 'white',
@@ -114,6 +139,7 @@ class BarChart {
114
139
  inside: false,
115
140
  align: undefined,
116
141
  verticalAlign: undefined,
142
+ overflow: 'allow',
117
143
  style: {
118
144
  textOutline: 'none',
119
145
  // The design system does not include a semibold font weight, so we use 700 (bold) as an alternative.
@@ -0,0 +1,37 @@
1
+ import ChartConstants from './chart-constants';
2
+ import ColumnChart from './column-chart';
3
+
4
+ class Boxplot {
5
+ constructor() {
6
+ this.constants = ChartConstants.constants();
7
+ this.columnChart = new ColumnChart();
8
+ }
9
+
10
+ getBoxplotOptions = (config, useStackedLayout, extraLines) => {
11
+ return {
12
+ plotOptions: {
13
+ boxplot: {
14
+ ...this.columnChart.getPointPadding(config, useStackedLayout, extraLines, false),
15
+ boxDashStyle: 'Solid',
16
+ fillColor: this.constants.uncertaintyRangeColor,
17
+ lineWidth: 0,
18
+ medianColor: this.constants.estimateLineColor,
19
+ medianDashStyle: 'Solid',
20
+ medianWidth: 3,
21
+ stemWidth: 0,
22
+ whiskerWidth: 0,
23
+ },
24
+ },
25
+ legend: {
26
+ enabled: false, // Legend is handled via custom HTML for box plots, so we disable the default Highcharts legend
27
+ symbolRadius: 0,
28
+ itemStyle: {
29
+ fontSize: this.constants.defaultFontSize,
30
+ color: this.constants.legendLabelColor,
31
+ },
32
+ },
33
+ };
34
+ };
35
+ }
36
+
37
+ export default Boxplot;
@@ -9,7 +9,11 @@ class ChartConstants {
9
9
  categoryLabelColor: '#414042',
10
10
  gridLineColor: '#d9d9d9',
11
11
  zeroLineColor: '#b3b3b3',
12
+ estimateLineColor: '#003c57',
13
+ uncertaintyRangeColor: 'rgba(32, 96, 149, 0.65)',
12
14
  defaultFontSize: '0.875rem', // 14px
15
+ extraLineColor: '#222222',
16
+ shadingColor: '#ececec',
13
17
  lineMarkerStyles: [
14
18
  {
15
19
  radius: 4,
@@ -93,6 +97,16 @@ class ChartConstants {
93
97
  lineColor: '#ffffff',
94
98
  },
95
99
  ],
100
+ confidenceLevelMarkerStyles: [
101
+ {
102
+ radius: 6,
103
+ symbol: 'diamond',
104
+ lineWidth: 2,
105
+ lineColor: '#222',
106
+ outline: true,
107
+ fillColor: 'white',
108
+ },
109
+ ],
96
110
  };
97
111
 
98
112
  return constants;