@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.
Files changed (62) hide show
  1. package/components/button/_button.scss +171 -3
  2. package/components/button/_macro.njk +4 -0
  3. package/components/card/_card.scss +5 -0
  4. package/components/card/_macro.njk +10 -2
  5. package/components/card/_macro.spec.js +58 -0
  6. package/components/card/example-card-set-with-headline-figures.njk +62 -0
  7. package/components/chart/_chart.scss +80 -0
  8. package/components/chart/_macro.njk +125 -0
  9. package/components/chart/_macro.spec.js +825 -0
  10. package/components/chart/annotations-options.js +78 -0
  11. package/components/chart/bar-chart.js +164 -0
  12. package/components/chart/chart-constants.js +21 -0
  13. package/components/chart/chart.dom.js +8 -0
  14. package/components/chart/chart.js +242 -0
  15. package/components/chart/column-chart.js +48 -0
  16. package/components/chart/common-chart-options.js +247 -0
  17. package/components/chart/example-bar-chart-with-annotations.njk +62 -0
  18. package/components/chart/example-bar-chart.njk +55 -0
  19. package/components/chart/example-bar-with-line-chart.njk +64 -0
  20. package/components/chart/example-clustered-bar-chart.njk +60 -0
  21. package/components/chart/example-clustered-column-chart.njk +74 -0
  22. package/components/chart/example-column-chart-with-annotations.njk +62 -0
  23. package/components/chart/example-column-chart.njk +54 -0
  24. package/components/chart/example-column-with-line-chart.njk +62 -0
  25. package/components/chart/example-line-chart-with-annotations.njk +235 -0
  26. package/components/chart/example-line-chart.njk +221 -0
  27. package/components/chart/example-stacked-bar-chart.njk +60 -0
  28. package/components/chart/example-stacked-column-chart.njk +67 -0
  29. package/components/chart/line-chart.js +42 -0
  30. package/components/chart/specific-chart-options.js +22 -0
  31. package/components/footer/_footer.scss +12 -0
  32. package/components/footer/_macro.njk +38 -17
  33. package/components/header/_header.scss +18 -2
  34. package/components/header/_macro.njk +81 -8
  35. package/components/header/_macro.spec.js +97 -0
  36. package/components/header/example-header-with-search-button.njk +190 -0
  37. package/components/header/header.spec.js +128 -0
  38. package/components/hero/_hero.scss +39 -7
  39. package/components/hero/_macro.njk +47 -17
  40. package/components/hero/_macro.spec.js +94 -0
  41. package/components/hero/example-hero-grey.njk +9 -0
  42. package/components/icon/_macro.njk +8 -8
  43. package/components/input/_input.scss +15 -0
  44. package/components/input/_macro.njk +3 -3
  45. package/components/navigation/navigation.dom.js +17 -0
  46. package/components/navigation/navigation.js +72 -2
  47. package/components/pagination/_pagination.scss +7 -1
  48. package/components/summary/_macro.njk +1 -1
  49. package/components/summary/_macro.spec.js +6 -0
  50. package/components/table-of-contents/_macro.njk +40 -0
  51. package/components/table-of-contents/_macro.spec.js +72 -0
  52. package/components/table-of-contents/_table-of-contents.scss +11 -0
  53. package/components/table-of-contents/example-table-of-contents-related-links-with-button.njk +60 -0
  54. package/css/main.css +1 -1
  55. package/js/cookies-functions.js +11 -6
  56. package/js/cookies-functions.spec.js +44 -0
  57. package/js/main.js +2 -0
  58. package/package.json +4 -1
  59. package/scripts/main.es5.js +1 -1
  60. package/scripts/main.js +1 -1
  61. package/scss/main.scss +1 -0
  62. package/scss/utilities/_grid.scss +13 -4
@@ -0,0 +1,825 @@
1
+ /** @jest-environment jsdom */
2
+
3
+ import * as cheerio from 'cheerio';
4
+
5
+ import axe from '../../tests/helpers/axe';
6
+ import { renderComponent } from '../../tests/helpers/rendering';
7
+ import {
8
+ EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
9
+ EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS,
10
+ EXAMPLE_BAR_CHART_PARAMS,
11
+ EXAMPLE_BAR_CHART_WITH_PERCENTAGE_HEIGHT_PARAMS,
12
+ EXAMPLE_COLUMN_CHART_PARAMS,
13
+ EXAMPLE_LINE_CHART_WITH_ANNOTATIONS_PARAMS,
14
+ EXAMPLE_BAR_CHART_WITH_ANNOTATIONS_PARAMS,
15
+ EXAMPLE_COLUMN_CHART_WITH_ANNOTATIONS_PARAMS,
16
+ EXAMPLE_BAR_WITH_LINE_CHART_PARAMS,
17
+ EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS,
18
+ } from './_test-examples';
19
+
20
+ describe('Macro: Chart', () => {
21
+ describe('FOR: Line Chart', () => {
22
+ describe('GIVEN: Params: required', () => {
23
+ describe('WHEN: required params are provided', () => {
24
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_REQUIRED_PARAMS));
25
+
26
+ test('THEN: it passes jest-axe checks', async () => {
27
+ const results = await axe($.html());
28
+ expect(results).toHaveNoViolations();
29
+ });
30
+
31
+ test('THEN: it renders the subtitle', () => {
32
+ expect($('.ons-chart__subtitle').text()).toBe('A sample subtitle');
33
+ });
34
+
35
+ test('THEN: it renders the chart container with the correct data attributes', () => {
36
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('line');
37
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Line Chart');
38
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('chart-123');
39
+ });
40
+
41
+ test('THEN: it includes the Highcharts JSON config', () => {
42
+ const configScript = $(`script[data-highcharts-config--chart-123]`).html();
43
+ expect(configScript).toContain('"text":"X Axis Title"');
44
+ expect(configScript).toContain('"text":"Y Axis Title"');
45
+ });
46
+
47
+ test('THEN: it does NOT render optional fields', () => {
48
+ expect($('figcaption').length).toBe(0);
49
+ expect($('.ons-chart__download-title').length).toBe(0);
50
+ });
51
+ });
52
+ });
53
+
54
+ describe('GIVEN: Params: Theme', () => {
55
+ describe('WHEN: theme is provided', () => {
56
+ const $ = cheerio.load(
57
+ renderComponent('chart', {
58
+ ...EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
59
+ theme: 'primary',
60
+ }),
61
+ );
62
+
63
+ test('THEN: it renders the chart container with the correct theme', () => {
64
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('primary');
65
+ });
66
+ });
67
+ });
68
+
69
+ describe('GIVEN: Params: Heading Level', () => {
70
+ describe('WHEN: heading level is provided', () => {
71
+ const $ = cheerio.load(
72
+ renderComponent('chart', {
73
+ ...EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
74
+ headingLevel: 3,
75
+ download: {
76
+ title: 'Download Chart Data',
77
+ itemsList: [
78
+ { text: 'Download as PNG', url: 'https://example.com/chart.png' },
79
+ { text: 'Download as CSV', url: 'https://example.com/chart.csv' },
80
+ ],
81
+ },
82
+ }),
83
+ );
84
+
85
+ test('THEN: renders title with correct tag', () => {
86
+ expect($('.ons-chart__title')[0].tagName).toBe('h3');
87
+ });
88
+
89
+ test('THEN: renders subtitle with correct tag', () => {
90
+ expect($('.ons-chart__subtitle')[0].tagName).toBe('h4');
91
+ });
92
+
93
+ test('THEN: renders download title with correct tag', () => {
94
+ expect($('.ons-chart__download-title')[0].tagName).toBe('h5');
95
+ });
96
+ });
97
+ });
98
+
99
+ describe('GIVEN: Params: Config', () => {
100
+ describe('WHEN: config params are provided', () => {
101
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS));
102
+ const configScript = $(`script[data-highcharts-config--chart-456]`).html();
103
+
104
+ test('THEN: it renders the legend when enabled', () => {
105
+ expect(configScript).toContain('"enabled":true');
106
+ });
107
+
108
+ test('THEN: it includes correct xAxis properties', () => {
109
+ expect(configScript).toContain('"text":"X Axis Label"');
110
+ expect(configScript).toContain('"categories":["A","B","C"]');
111
+ expect(configScript).toContain('"type":"linear"');
112
+ });
113
+
114
+ test('THEN: it includes correct yAxis properties', () => {
115
+ expect(configScript).toContain('"text":"Y Axis Label"');
116
+ expect(configScript).toContain('"labels":{"format":"{value:,.f}"}');
117
+ });
118
+
119
+ test('THEN: it includes correct series data', () => {
120
+ expect(configScript).toContain('"name":"Category 1"');
121
+ expect(configScript).toContain('"data":[5,15,25]');
122
+ expect(configScript).toContain('"name":"Category 2"');
123
+ expect(configScript).toContain('"data":[10,20,30]');
124
+ });
125
+ });
126
+ });
127
+
128
+ describe('GIVEN: Params: Percentage Height Desktop', () => {
129
+ describe('WHEN: percentage height desktop is provided', () => {
130
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS));
131
+ test('THEN: it includes correct percentage height desktop', () => {
132
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-percentage-height-desktop')).toBe('50');
133
+ });
134
+ });
135
+ });
136
+
137
+ describe('GIVEN: Params: Percentage Height Mobile', () => {
138
+ describe('WHEN: percentage height mobile is provided', () => {
139
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS));
140
+ test('THEN: it includes correct percentage height mobile', () => {
141
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-percentage-height-mobile')).toBe('120');
142
+ });
143
+ });
144
+ });
145
+
146
+ describe('GIVEN: Params: Caption', () => {
147
+ describe('WHEN: caption is provided', () => {
148
+ const $ = cheerio.load(
149
+ renderComponent('chart', {
150
+ ...EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
151
+ caption: 'This is an example caption for the chart.',
152
+ }),
153
+ );
154
+
155
+ test('THEN: it renders the caption when provided', () => {
156
+ expect($('figcaption').text()).toBe('This is an example caption for the chart.');
157
+ });
158
+ });
159
+ });
160
+
161
+ describe('GIVEN: Params: Description', () => {
162
+ describe('WHEN: description is provided', () => {
163
+ const $ = cheerio.load(
164
+ renderComponent('chart', {
165
+ ...EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
166
+ description: 'An accessible description for screen readers.',
167
+ }),
168
+ );
169
+
170
+ test('THEN: it renders the description for accessibility', () => {
171
+ expect($('.ons-u-vh').text()).toBe('An accessible description for screen readers.');
172
+ });
173
+ });
174
+ });
175
+
176
+ describe('GIVEN: Params: Download', () => {
177
+ describe('WHEN: download object are provided', () => {
178
+ const $ = cheerio.load(
179
+ renderComponent('chart', {
180
+ ...EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
181
+ download: {
182
+ title: 'Download Chart Data',
183
+ itemsList: [
184
+ { text: 'Download as PNG', url: 'https://example.com/chart.png' },
185
+ { text: 'Download as CSV', url: 'https://example.com/chart.csv' },
186
+ ],
187
+ },
188
+ }),
189
+ );
190
+
191
+ test('THEN: it renders the download section correctly', () => {
192
+ expect($('.ons-chart__download-title').text()).toBe('Download Chart Data');
193
+
194
+ const downloadLinks = $('.ons-chart__download-title').next().find('li a');
195
+ expect(downloadLinks.eq(0).text()).toBe('Download as PNG');
196
+ expect(downloadLinks.eq(0).attr('href')).toBe('https://example.com/chart.png');
197
+ expect(downloadLinks.eq(1).text()).toBe('Download as CSV');
198
+ expect(downloadLinks.eq(1).attr('href')).toBe('https://example.com/chart.csv');
199
+ });
200
+ });
201
+ });
202
+ });
203
+
204
+ describe('FOR: Bar Chart', () => {
205
+ describe('GIVEN: Params: required', () => {
206
+ describe('WHEN: required params are provided', () => {
207
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_BAR_CHART_PARAMS));
208
+
209
+ test('THEN: it passes jest-axe checks', async () => {
210
+ const results = await axe($.html());
211
+ expect(results).toHaveNoViolations();
212
+ });
213
+
214
+ test('THEN: it renders the chart container with the correct data attributes', () => {
215
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('bar');
216
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('alternate');
217
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Bar Chart');
218
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('bar-chart-123');
219
+ });
220
+
221
+ test('THEN: it does NOT render optional fields', () => {
222
+ expect($('figcaption').length).toBe(0);
223
+ expect($('.ons-chart__download-title').length).toBe(0);
224
+ });
225
+
226
+ test('THEN: it includes the Highcharts JSON config', () => {
227
+ const configScript = $(`script[data-highcharts-config--bar-chart-123]`).html();
228
+ expect(configScript).toContain('"text":"X Axis Title"');
229
+ expect(configScript).toContain('"text":"Y Axis Title"');
230
+ });
231
+ });
232
+ });
233
+
234
+ describe('GIVEN: Params: Config', () => {
235
+ describe('WHEN: config params are provided', () => {
236
+ const $ = cheerio.load(
237
+ renderComponent('chart', {
238
+ ...EXAMPLE_BAR_CHART_PARAMS,
239
+ legend: true,
240
+ }),
241
+ );
242
+
243
+ test('THEN: it renders the legend when enabled', () => {
244
+ const configScript = $(`script[data-highcharts-config--bar-chart-123]`).html();
245
+ expect(configScript).toContain('"enabled":true');
246
+ });
247
+
248
+ test('THEN: it includes correct xAxis and yAxis titles', () => {
249
+ const configScript = $(`script[data-highcharts-config--bar-chart-123]`).html();
250
+ expect(configScript).toContain('"text":"X Axis Title"');
251
+ expect(configScript).toContain('"text":"Y Axis Title"');
252
+ });
253
+ });
254
+ });
255
+
256
+ describe('GIVEN: Params: Percentage Height Desktop', () => {
257
+ describe('WHEN: percentage height desktop is provided', () => {
258
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_BAR_CHART_WITH_PERCENTAGE_HEIGHT_PARAMS));
259
+ test('THEN: it does not include percentage height desktop', () => {
260
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-percentage-height-desktop')).toBe(undefined);
261
+ });
262
+ });
263
+ });
264
+
265
+ describe('GIVEN: Params: Percentage Height Mobile', () => {
266
+ describe('WHEN: percentage height mobile is provided', () => {
267
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_BAR_CHART_WITH_PERCENTAGE_HEIGHT_PARAMS));
268
+ test('THEN: it does not include percentage height mobile', () => {
269
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-percentage-height-mobile')).toBe(undefined);
270
+ });
271
+ });
272
+ });
273
+
274
+ describe('GIVEN: Params: Caption', () => {
275
+ describe('WHEN: caption is provided', () => {
276
+ const $ = cheerio.load(
277
+ renderComponent('chart', {
278
+ ...EXAMPLE_BAR_CHART_PARAMS,
279
+ caption: 'This is an example caption for the chart.',
280
+ }),
281
+ );
282
+
283
+ test('THEN: it renders the caption when provided', () => {
284
+ expect($('figcaption').text()).toBe('This is an example caption for the chart.');
285
+ });
286
+ });
287
+ });
288
+
289
+ describe('GIVEN: Params: Description', () => {
290
+ describe('WHEN: description is provided', () => {
291
+ const $ = cheerio.load(
292
+ renderComponent('chart', {
293
+ ...EXAMPLE_BAR_CHART_PARAMS,
294
+ description: 'An accessible description for screen readers.',
295
+ }),
296
+ );
297
+
298
+ test('THEN: it renders the description for accessibility', () => {
299
+ expect($('.ons-u-vh').text()).toBe('An accessible description for screen readers.');
300
+ });
301
+ });
302
+ });
303
+
304
+ describe('GIVEN: Params: Download', () => {
305
+ describe('WHEN: download object are provided', () => {
306
+ const $ = cheerio.load(
307
+ renderComponent('chart', {
308
+ ...EXAMPLE_BAR_CHART_PARAMS,
309
+ download: {
310
+ title: 'Download Chart Data',
311
+ itemsList: [
312
+ { text: 'Download as PNG', url: 'https://example.com/chart.png' },
313
+ { text: 'Download as CSV', url: 'https://example.com/chart.csv' },
314
+ ],
315
+ },
316
+ }),
317
+ );
318
+
319
+ test('THEN: it renders the download section correctly', () => {
320
+ expect($('.ons-chart__download-title').text()).toBe('Download Chart Data');
321
+
322
+ const downloadLinks = $('.ons-chart__download-title').next().find('li a');
323
+ expect(downloadLinks.eq(0).text()).toBe('Download as PNG');
324
+ expect(downloadLinks.eq(0).attr('href')).toBe('https://example.com/chart.png');
325
+ expect(downloadLinks.eq(1).text()).toBe('Download as CSV');
326
+ expect(downloadLinks.eq(1).attr('href')).toBe('https://example.com/chart.csv');
327
+ });
328
+ });
329
+ });
330
+
331
+ describe('GIVEN: Stacked Bar Chart', () => {
332
+ describe('WHEN: Stacked layout is enabled', () => {
333
+ const $ = cheerio.load(
334
+ renderComponent('chart', {
335
+ ...EXAMPLE_BAR_CHART_PARAMS,
336
+ useStackedLayout: true,
337
+ }),
338
+ );
339
+
340
+ test('THEN: it includes stacking in the config', () => {
341
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-use-stacked-layout')).toBe('true');
342
+ });
343
+
344
+ test('THEN: it renders a bar chart with stacked series', () => {
345
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('bar');
346
+ });
347
+ });
348
+ });
349
+ });
350
+
351
+ describe('FOR: Column Chart', () => {
352
+ describe('GIVEN: Params: required', () => {
353
+ describe('WHEN: required params are provided', () => {
354
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_COLUMN_CHART_PARAMS));
355
+
356
+ test('THEN: it passes jest-axe checks', async () => {
357
+ const results = await axe($.html());
358
+ expect(results).toHaveNoViolations();
359
+ });
360
+
361
+ test('THEN: it renders the chart container with the correct data attributes', () => {
362
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('column');
363
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('alternate');
364
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Column Chart');
365
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('column-chart-123');
366
+ });
367
+
368
+ test('THEN: it does NOT render optional fields', () => {
369
+ expect($('figcaption').length).toBe(0);
370
+ expect($('.ons-chart__download-title').length).toBe(0);
371
+ });
372
+
373
+ test('THEN: it includes the Highcharts JSON config', () => {
374
+ const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
375
+ expect(configScript).toContain('"text":"X Axis Title"');
376
+ expect(configScript).toContain('"text":"Y Axis Title"');
377
+ });
378
+ });
379
+ });
380
+
381
+ describe('GIVEN: Params: Config', () => {
382
+ describe('WHEN: config params are provided', () => {
383
+ const $ = cheerio.load(
384
+ renderComponent('chart', {
385
+ ...EXAMPLE_COLUMN_CHART_PARAMS,
386
+ legend: true,
387
+ }),
388
+ );
389
+
390
+ test('THEN: it renders the legend when enabled', () => {
391
+ const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
392
+ expect(configScript).toContain('"enabled":true');
393
+ });
394
+
395
+ test('THEN: it includes correct xAxis and yAxis titles', () => {
396
+ const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
397
+ expect(configScript).toContain('"text":"X Axis Title"');
398
+ expect(configScript).toContain('"text":"Y Axis Title"');
399
+ });
400
+ });
401
+ });
402
+
403
+ describe('GIVEN: Params: Percentage Height Desktop', () => {
404
+ describe('WHEN: percentage height desktop is provided', () => {
405
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_COLUMN_CHART_PARAMS));
406
+ test('THEN: it includes correct percentage height desktop', () => {
407
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-percentage-height-desktop')).toBe('50');
408
+ });
409
+ });
410
+ });
411
+
412
+ describe('GIVEN: Params: Percentage Height Mobile', () => {
413
+ describe('WHEN: percentage height mobile is provided', () => {
414
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_COLUMN_CHART_PARAMS));
415
+ test('THEN: it includes correct percentage height mobile', () => {
416
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-percentage-height-mobile')).toBe('120');
417
+ });
418
+ });
419
+ });
420
+
421
+ describe('GIVEN: Params: Caption', () => {
422
+ describe('WHEN: caption is provided', () => {
423
+ const $ = cheerio.load(
424
+ renderComponent('chart', {
425
+ ...EXAMPLE_COLUMN_CHART_PARAMS,
426
+ caption: 'This is an example caption for the chart.',
427
+ }),
428
+ );
429
+
430
+ test('THEN: it renders the caption when provided', () => {
431
+ expect($('figcaption').text()).toBe('This is an example caption for the chart.');
432
+ });
433
+ });
434
+ });
435
+
436
+ describe('GIVEN: Params: Description', () => {
437
+ describe('WHEN: description is provided', () => {
438
+ const $ = cheerio.load(
439
+ renderComponent('chart', {
440
+ ...EXAMPLE_COLUMN_CHART_PARAMS,
441
+ description: 'An accessible description for screen readers.',
442
+ }),
443
+ );
444
+
445
+ test('THEN: it renders the description for accessibility', () => {
446
+ expect($('.ons-u-vh').text()).toBe('An accessible description for screen readers.');
447
+ });
448
+ });
449
+ });
450
+
451
+ describe('GIVEN: Params: Download', () => {
452
+ describe('WHEN: download object are provided', () => {
453
+ const $ = cheerio.load(
454
+ renderComponent('chart', {
455
+ ...EXAMPLE_COLUMN_CHART_PARAMS,
456
+ download: {
457
+ title: 'Download Chart Data',
458
+ itemsList: [
459
+ { text: 'Download as PNG', url: 'https://example.com/chart.png' },
460
+ { text: 'Download as CSV', url: 'https://example.com/chart.csv' },
461
+ ],
462
+ },
463
+ }),
464
+ );
465
+
466
+ test('THEN: it renders the download section correctly', () => {
467
+ expect($('.ons-chart__download-title').text()).toBe('Download Chart Data');
468
+
469
+ const downloadLinks = $('.ons-chart__download-title').next().find('li a');
470
+ expect(downloadLinks.eq(0).text()).toBe('Download as PNG');
471
+ expect(downloadLinks.eq(0).attr('href')).toBe('https://example.com/chart.png');
472
+ expect(downloadLinks.eq(1).text()).toBe('Download as CSV');
473
+ expect(downloadLinks.eq(1).attr('href')).toBe('https://example.com/chart.csv');
474
+ });
475
+ });
476
+ });
477
+
478
+ describe('GIVEN: Stacked Column Chart', () => {
479
+ describe('WHEN: Stacked layout is enabled', () => {
480
+ const $ = cheerio.load(
481
+ renderComponent('chart', {
482
+ ...EXAMPLE_COLUMN_CHART_PARAMS,
483
+ useStackedLayout: true,
484
+ }),
485
+ );
486
+
487
+ test('THEN: it includes stacking in the config', () => {
488
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-use-stacked-layout')).toBe('true');
489
+ });
490
+
491
+ test('THEN: it renders a column chart with stacked series', () => {
492
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('column');
493
+ });
494
+ });
495
+ });
496
+ });
497
+
498
+ describe('FOR: Line chart with annotations', () => {
499
+ describe('GIVEN: Params: Annotations', () => {
500
+ describe('WHEN: annotations params are provided', () => {
501
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_ANNOTATIONS_PARAMS));
502
+
503
+ test('THEN: it passes jest-axe checks', async () => {
504
+ const results = await axe($.html());
505
+ expect(results).toHaveNoViolations();
506
+ });
507
+
508
+ test('THEN: it renders the footnotes', () => {
509
+ expect($('.ons-chart__footnotes').text()).toContain('1');
510
+ expect($('.ons-chart__footnotes').text()).toContain('A test annotation');
511
+ expect($('.ons-chart__footnotes').text()).toContain('2');
512
+ expect($('.ons-chart__footnotes').text()).toContain('Another test annotation');
513
+ });
514
+
515
+ test('THEN: the footnotes are hidden from screen readers', () => {
516
+ expect($('.ons-chart__footnotes').attr('aria-hidden')).toBe('true');
517
+ });
518
+
519
+ test('THEN: it includes the Annotations JSON config', () => {
520
+ const configScript = $(`script[data-highcharts-annotations--line-chart-annotations-123]`).html();
521
+ expect(configScript).toContain('"text":"A test annotation"');
522
+ expect(configScript).toContain('"point":{"x":10,"y":1.3}');
523
+ expect(configScript).toContain('"labelOffsetX":10,"labelOffsetY":-50');
524
+ });
525
+ });
526
+ });
527
+ });
528
+
529
+ describe('FOR: Bar chart with annotations', () => {
530
+ describe('GIVEN: Params: Annotations', () => {
531
+ describe('WHEN: annotations params are provided', () => {
532
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_BAR_CHART_WITH_ANNOTATIONS_PARAMS));
533
+ test('THEN: it passes jest-axe checks', async () => {
534
+ const results = await axe($.html());
535
+ expect(results).toHaveNoViolations();
536
+ });
537
+
538
+ test('THEN: it renders the footnotes', () => {
539
+ expect($('.ons-chart__footnotes').text()).toContain('1');
540
+ expect($('.ons-chart__footnotes').text()).toContain('A test annotation');
541
+ });
542
+
543
+ test('THEN: the footnotes are hidden from screen readers', () => {
544
+ expect($('.ons-chart__footnotes').attr('aria-hidden')).toBe('true');
545
+ });
546
+
547
+ test('THEN: it includes the Annotations JSON config', () => {
548
+ const configScript = $(`script[data-highcharts-annotations--bar-chart-annotations-123]`).html();
549
+ expect(configScript).toContain('"text":"A test annotation"');
550
+ expect(configScript).toContain('"point":{"x":2,"y":3}');
551
+ expect(configScript).toContain('"labelOffsetX":10,"labelOffsetY":-50');
552
+ });
553
+ });
554
+ });
555
+ });
556
+
557
+ describe('FOR: Column chart with annotations', () => {
558
+ describe('GIVEN: Params: Annotations', () => {
559
+ describe('WHEN: annotations params are provided', () => {
560
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_COLUMN_CHART_WITH_ANNOTATIONS_PARAMS));
561
+
562
+ test('THEN: it passes jest-axe checks', async () => {
563
+ const results = await axe($.html());
564
+ expect(results).toHaveNoViolations();
565
+ });
566
+
567
+ test('THEN: it renders the footnotes', () => {
568
+ expect($('.ons-chart__footnotes').text()).toContain('1');
569
+ expect($('.ons-chart__footnotes').text()).toContain('A test annotation');
570
+ });
571
+
572
+ test('THEN: the footnotes are hidden from screen readers', () => {
573
+ expect($('.ons-chart__footnotes').attr('aria-hidden')).toBe('true');
574
+ });
575
+
576
+ test('THEN: it includes the Annotations JSON config', () => {
577
+ const configScript = $(`script[data-highcharts-annotations--column-chart-annotations-123]`).html();
578
+ expect(configScript).toContain('"text":"A test annotation"');
579
+ expect(configScript).toContain('"point":{"x":11,"y":31.8}');
580
+ expect(configScript).toContain('"labelOffsetX":10,"labelOffsetY":-50');
581
+ });
582
+ });
583
+ });
584
+ });
585
+
586
+ describe('FOR: Bar Chart with Line', () => {
587
+ describe('GIVEN: Params: required', () => {
588
+ describe('WHEN: required params are provided', () => {
589
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_BAR_WITH_LINE_CHART_PARAMS));
590
+ const configScript = $(`script[data-highcharts-config--bar-chart-123]`).html();
591
+
592
+ test('THEN: it passes jest-axe checks', async () => {
593
+ const results = await axe($.html());
594
+ expect(results).toHaveNoViolations();
595
+ });
596
+
597
+ test('THEN: it includes one series of type "bar" and another of type "line"', () => {
598
+ expect(configScript).toContain('"type":"bar"');
599
+ expect(configScript).toContain('"type":"line"');
600
+ });
601
+
602
+ test('THEN: it renders the chart container with correct data attributes', () => {
603
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('bar');
604
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('alternate');
605
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Bar Chart');
606
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('bar-chart-123');
607
+ });
608
+
609
+ test('THEN: it includes the Highcharts JSON config', () => {
610
+ expect(configScript).toContain('"text":"X Axis Title"');
611
+ expect(configScript).toContain('"text":"Y Axis Title"');
612
+ });
613
+ });
614
+ });
615
+
616
+ describe('GIVEN: Params: Legend', () => {
617
+ describe('WHEN: legend is enabled', () => {
618
+ const $ = cheerio.load(renderComponent('chart', { ...EXAMPLE_BAR_WITH_LINE_CHART_PARAMS, legend: false }));
619
+
620
+ test('THEN: it renders the legend', () => {
621
+ const configScript = $(`script[data-highcharts-config--bar-chart-123]`).html();
622
+ expect(configScript).toContain('"enabled":false');
623
+ });
624
+ });
625
+ });
626
+
627
+ describe('GIVEN: Params: Caption', () => {
628
+ describe('WHEN: caption is provided', () => {
629
+ const $ = cheerio.load(
630
+ renderComponent('chart', {
631
+ ...EXAMPLE_BAR_WITH_LINE_CHART_PARAMS,
632
+ caption: 'This is an example caption for the chart.',
633
+ }),
634
+ );
635
+
636
+ test('THEN: it renders the caption when provided', () => {
637
+ expect($('figcaption').text()).toBe('This is an example caption for the chart.');
638
+ });
639
+ });
640
+ });
641
+
642
+ describe('GIVEN: Params: Description', () => {
643
+ describe('WHEN: description is provided', () => {
644
+ const $ = cheerio.load(
645
+ renderComponent('chart', {
646
+ ...EXAMPLE_BAR_WITH_LINE_CHART_PARAMS,
647
+ description: 'An accessible description for screen readers.',
648
+ }),
649
+ );
650
+
651
+ test('THEN: it renders the description for accessibility', () => {
652
+ expect($('.ons-u-vh').text()).toBe('An accessible description for screen readers.');
653
+ });
654
+ });
655
+ });
656
+
657
+ describe('GIVEN: Params: Series: Type', () => {
658
+ describe('WHEN: a series has an invalid type', () => {
659
+ const invalidTypeParams = {
660
+ ...EXAMPLE_BAR_WITH_LINE_CHART_PARAMS,
661
+ series: [
662
+ { name: 'Invalid Series', data: [5, 15, 25], type: 'scatter' },
663
+ { name: 'Valid Line Series', data: [10, 20, 30], type: 'line' },
664
+ ],
665
+ };
666
+
667
+ const $ = cheerio.load(renderComponent('chart', invalidTypeParams));
668
+ const configScript = $(`script[data-highcharts-config--bar-chart-123]`).html();
669
+
670
+ test('THEN: it defaults non-line series type to the chartType', () => {
671
+ expect(configScript).not.toContain('"type":"scatter"');
672
+ expect(configScript).toContain('"type":"bar"');
673
+ expect(configScript).toContain('"type":"line"');
674
+ });
675
+ });
676
+ });
677
+
678
+ describe('GIVEN: Params: Download', () => {
679
+ describe('WHEN: download object are provided', () => {
680
+ const $ = cheerio.load(
681
+ renderComponent('chart', {
682
+ ...EXAMPLE_BAR_WITH_LINE_CHART_PARAMS,
683
+ download: {
684
+ title: 'Download Chart Data',
685
+ itemsList: [
686
+ { text: 'Download as PNG', url: 'https://example.com/chart.png' },
687
+ { text: 'Download as CSV', url: 'https://example.com/chart.csv' },
688
+ ],
689
+ },
690
+ }),
691
+ );
692
+
693
+ test('THEN: it renders the download section correctly', () => {
694
+ expect($('.ons-chart__download-title').text()).toBe('Download Chart Data');
695
+
696
+ const downloadLinks = $('.ons-chart__download-title').next().find('li a');
697
+ expect(downloadLinks.eq(0).text()).toBe('Download as PNG');
698
+ expect(downloadLinks.eq(0).attr('href')).toBe('https://example.com/chart.png');
699
+ expect(downloadLinks.eq(1).text()).toBe('Download as CSV');
700
+ expect(downloadLinks.eq(1).attr('href')).toBe('https://example.com/chart.csv');
701
+ });
702
+ });
703
+ });
704
+ });
705
+
706
+ describe('FOR: Column Chart with Line', () => {
707
+ describe('GIVEN: Params: required', () => {
708
+ describe('WHEN: required params are provided', () => {
709
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS));
710
+ const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
711
+
712
+ test('THEN: it passes jest-axe checks', async () => {
713
+ const results = await axe($.html());
714
+ expect(results).toHaveNoViolations();
715
+ });
716
+
717
+ test('THEN: it includes one series of type "column" and another of type "line"', () => {
718
+ expect(configScript).toContain('"type":"column"');
719
+ expect(configScript).toContain('"type":"line"');
720
+ });
721
+
722
+ test('THEN: it renders the chart container with correct data attributes', () => {
723
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('column');
724
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('alternate');
725
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Column Chart');
726
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('column-chart-123');
727
+ });
728
+
729
+ test('THEN: it includes the Highcharts JSON config', () => {
730
+ expect(configScript).toContain('"text":"X Axis Title"');
731
+ expect(configScript).toContain('"text":"Y Axis Title"');
732
+ });
733
+ });
734
+ });
735
+
736
+ describe('GIVEN: Params: Legend', () => {
737
+ describe('WHEN: legend is enabled', () => {
738
+ const $ = cheerio.load(renderComponent('chart', { ...EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS, legend: false }));
739
+
740
+ test('THEN: it renders the legend', () => {
741
+ const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
742
+ expect(configScript).toContain('"enabled":false');
743
+ });
744
+ });
745
+ });
746
+
747
+ describe('GIVEN: Params: Caption', () => {
748
+ describe('WHEN: caption is provided', () => {
749
+ const $ = cheerio.load(
750
+ renderComponent('chart', {
751
+ ...EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS,
752
+ caption: 'This is an example caption for the chart.',
753
+ }),
754
+ );
755
+
756
+ test('THEN: it renders the caption when provided', () => {
757
+ expect($('figcaption').text()).toBe('This is an example caption for the chart.');
758
+ });
759
+ });
760
+ });
761
+
762
+ describe('GIVEN: Params: Description', () => {
763
+ describe('WHEN: description is provided', () => {
764
+ const $ = cheerio.load(
765
+ renderComponent('chart', {
766
+ ...EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS,
767
+ description: 'An accessible description for screen readers.',
768
+ }),
769
+ );
770
+
771
+ test('THEN: it renders the description for accessibility', () => {
772
+ expect($('.ons-u-vh').text()).toBe('An accessible description for screen readers.');
773
+ });
774
+ });
775
+ });
776
+
777
+ describe('GIVEN: Params: Download', () => {
778
+ describe('WHEN: download object is provided', () => {
779
+ const $ = cheerio.load(
780
+ renderComponent('chart', {
781
+ ...EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS,
782
+ download: {
783
+ title: 'Download Chart Data',
784
+ itemsList: [
785
+ { text: 'Download as PNG', url: 'https://example.com/chart.png' },
786
+ { text: 'Download as CSV', url: 'https://example.com/chart.csv' },
787
+ ],
788
+ },
789
+ }),
790
+ );
791
+
792
+ test('THEN: it renders the download section correctly', () => {
793
+ expect($('.ons-chart__download-title').text()).toBe('Download Chart Data');
794
+
795
+ const downloadLinks = $('.ons-chart__download-title').next().find('li a');
796
+ expect(downloadLinks.eq(0).text()).toBe('Download as PNG');
797
+ expect(downloadLinks.eq(0).attr('href')).toBe('https://example.com/chart.png');
798
+ expect(downloadLinks.eq(1).text()).toBe('Download as CSV');
799
+ expect(downloadLinks.eq(1).attr('href')).toBe('https://example.com/chart.csv');
800
+ });
801
+ });
802
+ });
803
+
804
+ describe('GIVEN: Params: Series: Type', () => {
805
+ describe('WHEN: a series has an invalid type', () => {
806
+ const invalidTypeParams = {
807
+ ...EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS,
808
+ series: [
809
+ { name: 'Invalid Series', data: [5, 15, 25], type: 'scatter' },
810
+ { name: 'Valid Line Series', data: [10, 20, 30], type: 'line' },
811
+ ],
812
+ };
813
+
814
+ const $ = cheerio.load(renderComponent('chart', invalidTypeParams));
815
+ const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
816
+
817
+ test('THEN: it defaults non-line series type to the chartType', () => {
818
+ expect(configScript).not.toContain('"type":"scatter"');
819
+ expect(configScript).toContain('"type":"column"');
820
+ expect(configScript).toContain('"type":"line"');
821
+ });
822
+ });
823
+ });
824
+ });
825
+ });