@ons/design-system 72.5.0 → 72.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/components/breadcrumbs/_breadcrumbs.scss +1 -0
  2. package/components/chart/_chart.scss +56 -0
  3. package/components/chart/_macro.njk +43 -5
  4. package/components/chart/_macro.spec.js +420 -0
  5. package/components/chart/annotations-options.js +78 -0
  6. package/components/chart/bar-chart.js +15 -2
  7. package/components/chart/chart.js +119 -27
  8. package/components/chart/column-chart.js +2 -2
  9. package/components/chart/common-chart-options.js +104 -51
  10. package/components/chart/example-bar-chart-with-annotations.njk +62 -0
  11. package/components/chart/example-bar-chart.njk +1 -0
  12. package/components/chart/example-bar-with-line-chart.njk +64 -0
  13. package/components/chart/example-clustered-column-chart.njk +5 -2
  14. package/components/chart/example-column-chart-with-annotations.njk +63 -0
  15. package/components/chart/example-column-chart.njk +5 -2
  16. package/components/chart/example-column-with-line-chart.njk +63 -0
  17. package/components/chart/example-line-chart-with-annotations.njk +235 -0
  18. package/components/chart/example-line-chart.njk +7 -3
  19. package/components/chart/example-stacked-column-chart.njk +3 -1
  20. package/components/chart/line-chart.js +2 -7
  21. package/components/details/details.js +5 -0
  22. package/components/details-panel/_details-panel.scss +107 -0
  23. package/components/details-panel/_macro.njk +41 -0
  24. package/components/details-panel/_macro.spec.js +54 -0
  25. package/components/details-panel/example-details-panel-open.njk +29 -0
  26. package/components/details-panel/example-details-panel.njk +28 -0
  27. package/components/hero/_hero.scss +101 -43
  28. package/components/hero/_macro.njk +21 -10
  29. package/components/hero/_macro.spec.js +94 -0
  30. package/components/hero/example-hero-dark.njk +1 -1
  31. package/components/hero/example-hero-grey.njk +8 -0
  32. package/components/icon/_macro.njk +1 -1
  33. package/components/pagination/_macro.njk +1 -1
  34. package/components/pagination/_pagination.scss +6 -0
  35. package/components/summary/_macro.njk +2 -2
  36. package/components/summary/_macro.spec.js +43 -21
  37. package/components/summary/_summary.scss +5 -1
  38. package/components/summary/example-summary-grouped.njk +0 -12
  39. package/components/table-of-contents/_macro.njk +40 -0
  40. package/components/table-of-contents/_macro.spec.js +72 -0
  41. package/components/table-of-contents/_table-of-contents.scss +11 -0
  42. package/components/table-of-contents/example-table-of-contents-related-links-with-button.njk +60 -0
  43. package/css/main.css +1 -1
  44. package/js/cookies-functions.js +11 -6
  45. package/js/cookies-functions.spec.js +44 -0
  46. package/package.json +1 -1
  47. package/scripts/main.es5.js +1 -1
  48. package/scripts/main.js +1 -1
  49. package/scss/main.scss +1 -0
  50. package/scss/vars/_colors.scss +1 -0
@@ -77,6 +77,7 @@ $breadcrumb-chevron-height: 0.65rem;
77
77
 
78
78
  &:hover {
79
79
  color: var(--ons-color-text);
80
+ text-decoration: underline solid var(--ons-color-text) 2px;
80
81
  }
81
82
  }
82
83
  }
@@ -1,5 +1,7 @@
1
1
  .ons-chart {
2
2
  margin: 0;
3
+ container-type: inline-size;
4
+ max-width: $grid-max-width;
3
5
 
4
6
  &__download-title {
5
7
  @extend .ons-u-pt-l;
@@ -21,9 +23,63 @@
21
23
  line-height: 1.2rem;
22
24
  color: var(--ons-color-grey-75);
23
25
  }
26
+
27
+ &__footnote-number {
28
+ display: flex;
29
+ flex-shrink: 0;
30
+ flex-grow: 0;
31
+ align-items: center;
32
+ justify-content: center;
33
+ min-width: 1.125rem; // 18px
34
+ min-height: 1.125rem; // 18px
35
+ padding: 0.2143em; // 3px;
36
+ aspect-ratio: 1;
37
+ border: 1px solid var(--ons-color-grey-100);
38
+ border-radius: 50%;
39
+ font-size: 0.875rem; // 14px
40
+ line-height: 0;
41
+ background-color: var(--ons-color-white);
42
+ color: var(--ons-color-grey-100);
43
+ }
44
+
45
+ /* Override Highcharts styling */
46
+ &__footnote-number span {
47
+ position: static !important;
48
+ transform-origin: 0 0 !important;
49
+ font-size: inherit !important;
50
+ color: inherit !important;
51
+ top: 0 !important;
52
+ left: 0 !important;
53
+ }
54
+
55
+ &__footnotes {
56
+ list-style: none;
57
+ padding: 0;
58
+ margin: 0 0 1rem;
59
+
60
+ // We need to use a container query here in order to match the responsive rules for Highcharts
61
+ // which is based on the width that the chart renders not the viewport
62
+ // Update the breakpoint value here if the Highcharts responsive rules change
63
+ @container (min-width: 601px) {
64
+ display: none;
65
+ }
66
+ }
67
+
68
+ &__footnote_item {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: 0.5rem;
72
+ font-size: 0.875rem; // 14px
73
+ color: var(--ons-color-grey-100);
74
+ }
24
75
  }
25
76
 
26
77
  // This is a workaround to position the axis title to the left
27
78
  .highcharts-axis-title {
28
79
  width: 100%;
29
80
  }
81
+
82
+ .highcharts-xaxis-labels span {
83
+ display: block !important;
84
+ white-space: nowrap !important;
85
+ }
@@ -23,6 +23,24 @@
23
23
  data-highcharts-id="{{ params.id }}"
24
24
  {% if params.useStackedLayout %}data-highcharts-use-stacked-layout="{{ params.useStackedLayout }}"{% endif %}
25
25
  id="{{ params.id }}"
26
+ {% if params.percentageHeightDesktop and params.chartType != 'bar' %}
27
+ data-highcharts-percentage-height-desktop="{{ params.percentageHeightDesktop }}"
28
+ {% endif %}
29
+ {% if params.percentageHeightMobile and params.chartType != 'bar' %}
30
+ data-highcharts-percentage-height-mobile="{{ params.percentageHeightMobile }}"
31
+ {% endif %}
32
+ {% if params.xAxis.tickIntervalMobile %}
33
+ data-highcharts-x-axis-tick-interval-mobile="{{ params.xAxis.tickIntervalMobile }}"
34
+ {% endif %}
35
+ {% if params.xAxis.tickIntervalDesktop %}
36
+ data-highcharts-x-axis-tick-interval-desktop="{{ params.xAxis.tickIntervalDesktop }}"
37
+ {% endif %}
38
+ {% if params.yAxis.tickIntervalMobile %}
39
+ data-highcharts-y-axis-tick-interval-mobile="{{ params.yAxis.tickIntervalMobile }}"
40
+ {% endif %}
41
+ {% if params.yAxis.tickIntervalDesktop %}
42
+ data-highcharts-y-axis-tick-interval-desktop="{{ params.yAxis.tickIntervalDesktop }}"
43
+ {% endif %}
26
44
  >
27
45
  {% if params.chartType in supportedChartTypes %}
28
46
  <figure class="ons-chart">
@@ -33,6 +51,18 @@
33
51
  <p class="ons-u-vh">{{ params.description }}</p>
34
52
  {% endif %}
35
53
  <div data-highcharts-chart></div>
54
+ {#
55
+ Footnotes for the annotations at mobile
56
+ Hidden from screen readers because the full text will be read out where they appear in the chart
57
+ #}
58
+ <ul class="ons-chart__footnotes" aria-hidden="true">
59
+ {% for annotation in params.annotations %}
60
+ <li class="ons-chart__footnote_item">
61
+ <span class="ons-chart__footnote-number">{{ loop.index }}</span>
62
+ {{ annotation.text }}
63
+ </li>
64
+ {% endfor %}
65
+ </ul>
36
66
  {% if params.caption %}
37
67
  <figcaption class="ons-chart__caption">{{ params.caption }}</figcaption>
38
68
  {% endif %}
@@ -52,7 +82,7 @@
52
82
  {% set series = [] %}
53
83
  {% for item in params.series %}
54
84
  {%
55
- set series = series.concat({
85
+ set seriesItem = {
56
86
  "name": item.name if item.name else '',
57
87
  "data": item.data if item.data else [],
58
88
  "marker": {
@@ -60,14 +90,17 @@
60
90
  },
61
91
  "dataLabels": {
62
92
  "enabled": item.dataLabels if item.dataLabels else false
63
- }
64
- })
93
+ },
94
+ "type": item.type if item.type and item.type == 'line' else params.chartType
95
+ }
65
96
  %}
97
+ {# Use `set` tag to avoid printing the return value of extend #}
98
+ {% set _ = extend(series, seriesItem) %}
66
99
  {% endfor %}
67
100
  {%
68
101
  set config = {
69
102
  "legend": {
70
- "enabled" : params.legend
103
+ "enabled" : params.legend if params.legend else true
71
104
  },
72
105
  "yAxis": {
73
106
  "title": {
@@ -94,8 +127,13 @@
94
127
  <!-- Set scripts to pass the config values as json to the javascript -->
95
128
  <!-- prettier-ignore-start -->
96
129
  <script type="application/json" data-highcharts-config--{{ params.id }}>
97
- {{ config | dump | safe }}
130
+ {{ config | tojson }}
98
131
  </script>
132
+ {% if params.annotations %}
133
+ <script type="application/json" data-highcharts-annotations--{{ params.id }}>
134
+ {{ params.annotations | tojson }}
135
+ </script>
136
+ {% endif %}
99
137
  <!-- prettier-ignore-end -->
100
138
  </div>
101
139
  {% endmacro %}
@@ -8,7 +8,13 @@ import {
8
8
  EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
9
9
  EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS,
10
10
  EXAMPLE_BAR_CHART_PARAMS,
11
+ EXAMPLE_BAR_CHART_WITH_PERCENTAGE_HEIGHT_PARAMS,
11
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,
12
18
  } from './_test-examples';
13
19
 
14
20
  describe('Macro: Chart', () => {
@@ -90,6 +96,38 @@ describe('Macro: Chart', () => {
90
96
  });
91
97
  });
92
98
 
99
+ describe('GIVEN: Params: Tick Interval', () => {
100
+ describe('WHEN: tick interval is provided', () => {
101
+ const $ = cheerio.load(
102
+ renderComponent('chart', {
103
+ ...EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
104
+ xAxis: {
105
+ ...EXAMPLE_LINE_CHART_REQUIRED_PARAMS.xAxis,
106
+ tickIntervalMobile: 2,
107
+ tickIntervalDesktop: 5,
108
+ },
109
+ }),
110
+ );
111
+ test('THEN: it includes the tick interval in the config', () => {
112
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-x-axis-tick-interval-mobile')).toBe('2');
113
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-x-axis-tick-interval-desktop')).toBe('5');
114
+ });
115
+ });
116
+ describe('WHEN: tick interval is not provided', () => {
117
+ const $ = cheerio.load(
118
+ renderComponent('chart', {
119
+ ...EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
120
+ }),
121
+ );
122
+ test('THEN: it does not include the tick interval in the config', () => {
123
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-x-axis-tick-interval-mobile')).toBe(undefined);
124
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-x-axis-tick-interval-desktop')).toBe(undefined);
125
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-y-axis-tick-interval-desktop')).toBe(undefined);
126
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-y-axis-tick-interval-mobile')).toBe(undefined);
127
+ });
128
+ });
129
+ });
130
+
93
131
  describe('GIVEN: Params: Config', () => {
94
132
  describe('WHEN: config params are provided', () => {
95
133
  const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS));
@@ -119,6 +157,24 @@ describe('Macro: Chart', () => {
119
157
  });
120
158
  });
121
159
 
160
+ describe('GIVEN: Params: Percentage Height Desktop', () => {
161
+ describe('WHEN: percentage height desktop is provided', () => {
162
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS));
163
+ test('THEN: it includes correct percentage height desktop', () => {
164
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-percentage-height-desktop')).toBe('50');
165
+ });
166
+ });
167
+ });
168
+
169
+ describe('GIVEN: Params: Percentage Height Mobile', () => {
170
+ describe('WHEN: percentage height mobile is provided', () => {
171
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS));
172
+ test('THEN: it includes correct percentage height mobile', () => {
173
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-percentage-height-mobile')).toBe('120');
174
+ });
175
+ });
176
+ });
177
+
122
178
  describe('GIVEN: Params: Caption', () => {
123
179
  describe('WHEN: caption is provided', () => {
124
180
  const $ = cheerio.load(
@@ -229,6 +285,24 @@ describe('Macro: Chart', () => {
229
285
  });
230
286
  });
231
287
 
288
+ describe('GIVEN: Params: Percentage Height Desktop', () => {
289
+ describe('WHEN: percentage height desktop is provided', () => {
290
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_BAR_CHART_WITH_PERCENTAGE_HEIGHT_PARAMS));
291
+ test('THEN: it does not include percentage height desktop', () => {
292
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-percentage-height-desktop')).toBe(undefined);
293
+ });
294
+ });
295
+ });
296
+
297
+ describe('GIVEN: Params: Percentage Height Mobile', () => {
298
+ describe('WHEN: percentage height mobile is provided', () => {
299
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_BAR_CHART_WITH_PERCENTAGE_HEIGHT_PARAMS));
300
+ test('THEN: it does not include percentage height mobile', () => {
301
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-percentage-height-mobile')).toBe(undefined);
302
+ });
303
+ });
304
+ });
305
+
232
306
  describe('GIVEN: Params: Caption', () => {
233
307
  describe('WHEN: caption is provided', () => {
234
308
  const $ = cheerio.load(
@@ -358,6 +432,24 @@ describe('Macro: Chart', () => {
358
432
  });
359
433
  });
360
434
 
435
+ describe('GIVEN: Params: Percentage Height Desktop', () => {
436
+ describe('WHEN: percentage height desktop is provided', () => {
437
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_COLUMN_CHART_PARAMS));
438
+ test('THEN: it includes correct percentage height desktop', () => {
439
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-percentage-height-desktop')).toBe('50');
440
+ });
441
+ });
442
+ });
443
+
444
+ describe('GIVEN: Params: Percentage Height Mobile', () => {
445
+ describe('WHEN: percentage height mobile is provided', () => {
446
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_COLUMN_CHART_PARAMS));
447
+ test('THEN: it includes correct percentage height mobile', () => {
448
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-percentage-height-mobile')).toBe('120');
449
+ });
450
+ });
451
+ });
452
+
361
453
  describe('GIVEN: Params: Caption', () => {
362
454
  describe('WHEN: caption is provided', () => {
363
455
  const $ = cheerio.load(
@@ -434,4 +526,332 @@ describe('Macro: Chart', () => {
434
526
  });
435
527
  });
436
528
  });
529
+
530
+ describe('FOR: Line chart with annotations', () => {
531
+ describe('GIVEN: Params: Annotations', () => {
532
+ describe('WHEN: annotations params are provided', () => {
533
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_ANNOTATIONS_PARAMS));
534
+
535
+ test('THEN: it passes jest-axe checks', async () => {
536
+ const results = await axe($.html());
537
+ expect(results).toHaveNoViolations();
538
+ });
539
+
540
+ test('THEN: it renders the footnotes', () => {
541
+ expect($('.ons-chart__footnotes').text()).toContain('1');
542
+ expect($('.ons-chart__footnotes').text()).toContain('A test annotation');
543
+ expect($('.ons-chart__footnotes').text()).toContain('2');
544
+ expect($('.ons-chart__footnotes').text()).toContain('Another test annotation');
545
+ });
546
+
547
+ test('THEN: the footnotes are hidden from screen readers', () => {
548
+ expect($('.ons-chart__footnotes').attr('aria-hidden')).toBe('true');
549
+ });
550
+
551
+ test('THEN: it includes the Annotations JSON config', () => {
552
+ const configScript = $(`script[data-highcharts-annotations--line-chart-annotations-123]`).html();
553
+ expect(configScript).toContain('"text":"A test annotation"');
554
+ expect(configScript).toContain('"point":{"x":10,"y":1.3}');
555
+ expect(configScript).toContain('"labelOffsetX":10,"labelOffsetY":-50');
556
+ });
557
+ });
558
+ });
559
+ });
560
+
561
+ describe('FOR: Bar chart with annotations', () => {
562
+ describe('GIVEN: Params: Annotations', () => {
563
+ describe('WHEN: annotations params are provided', () => {
564
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_BAR_CHART_WITH_ANNOTATIONS_PARAMS));
565
+ test('THEN: it passes jest-axe checks', async () => {
566
+ const results = await axe($.html());
567
+ expect(results).toHaveNoViolations();
568
+ });
569
+
570
+ test('THEN: it renders the footnotes', () => {
571
+ expect($('.ons-chart__footnotes').text()).toContain('1');
572
+ expect($('.ons-chart__footnotes').text()).toContain('A test annotation');
573
+ });
574
+
575
+ test('THEN: the footnotes are hidden from screen readers', () => {
576
+ expect($('.ons-chart__footnotes').attr('aria-hidden')).toBe('true');
577
+ });
578
+
579
+ test('THEN: it includes the Annotations JSON config', () => {
580
+ const configScript = $(`script[data-highcharts-annotations--bar-chart-annotations-123]`).html();
581
+ expect(configScript).toContain('"text":"A test annotation"');
582
+ expect(configScript).toContain('"point":{"x":2,"y":3}');
583
+ expect(configScript).toContain('"labelOffsetX":10,"labelOffsetY":-50');
584
+ });
585
+ });
586
+ });
587
+ });
588
+
589
+ describe('FOR: Column chart with annotations', () => {
590
+ describe('GIVEN: Params: Annotations', () => {
591
+ describe('WHEN: annotations params are provided', () => {
592
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_COLUMN_CHART_WITH_ANNOTATIONS_PARAMS));
593
+
594
+ test('THEN: it passes jest-axe checks', async () => {
595
+ const results = await axe($.html());
596
+ expect(results).toHaveNoViolations();
597
+ });
598
+
599
+ test('THEN: it renders the footnotes', () => {
600
+ expect($('.ons-chart__footnotes').text()).toContain('1');
601
+ expect($('.ons-chart__footnotes').text()).toContain('A test annotation');
602
+ });
603
+
604
+ test('THEN: the footnotes are hidden from screen readers', () => {
605
+ expect($('.ons-chart__footnotes').attr('aria-hidden')).toBe('true');
606
+ });
607
+
608
+ test('THEN: it includes the Annotations JSON config', () => {
609
+ const configScript = $(`script[data-highcharts-annotations--column-chart-annotations-123]`).html();
610
+ expect(configScript).toContain('"text":"A test annotation"');
611
+ expect(configScript).toContain('"point":{"x":11,"y":31.8}');
612
+ expect(configScript).toContain('"labelOffsetX":10,"labelOffsetY":-50');
613
+ });
614
+ });
615
+ });
616
+ });
617
+
618
+ describe('FOR: Bar Chart with Line', () => {
619
+ describe('GIVEN: Params: required', () => {
620
+ describe('WHEN: required params are provided', () => {
621
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_BAR_WITH_LINE_CHART_PARAMS));
622
+ const configScript = $(`script[data-highcharts-config--bar-chart-123]`).html();
623
+
624
+ test('THEN: it passes jest-axe checks', async () => {
625
+ const results = await axe($.html());
626
+ expect(results).toHaveNoViolations();
627
+ });
628
+
629
+ test('THEN: it includes one series of type "bar" and another of type "line"', () => {
630
+ expect(configScript).toContain('"type":"bar"');
631
+ expect(configScript).toContain('"type":"line"');
632
+ });
633
+
634
+ test('THEN: it renders the chart container with correct data attributes', () => {
635
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('bar');
636
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('alternate');
637
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Bar Chart');
638
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('bar-chart-123');
639
+ });
640
+
641
+ test('THEN: it includes the Highcharts JSON config', () => {
642
+ expect(configScript).toContain('"text":"X Axis Title"');
643
+ expect(configScript).toContain('"text":"Y Axis Title"');
644
+ });
645
+ });
646
+ });
647
+
648
+ describe('GIVEN: Params: Legend', () => {
649
+ describe('WHEN: legend is enabled', () => {
650
+ const $ = cheerio.load(renderComponent('chart', { ...EXAMPLE_BAR_WITH_LINE_CHART_PARAMS, legend: false }));
651
+
652
+ test('THEN: it renders the legend', () => {
653
+ const configScript = $(`script[data-highcharts-config--bar-chart-123]`).html();
654
+ expect(configScript).toContain('"enabled":false');
655
+ });
656
+ });
657
+ });
658
+
659
+ describe('GIVEN: Params: Caption', () => {
660
+ describe('WHEN: caption is provided', () => {
661
+ const $ = cheerio.load(
662
+ renderComponent('chart', {
663
+ ...EXAMPLE_BAR_WITH_LINE_CHART_PARAMS,
664
+ caption: 'This is an example caption for the chart.',
665
+ }),
666
+ );
667
+
668
+ test('THEN: it renders the caption when provided', () => {
669
+ expect($('figcaption').text()).toBe('This is an example caption for the chart.');
670
+ });
671
+ });
672
+ });
673
+
674
+ describe('GIVEN: Params: Description', () => {
675
+ describe('WHEN: description is provided', () => {
676
+ const $ = cheerio.load(
677
+ renderComponent('chart', {
678
+ ...EXAMPLE_BAR_WITH_LINE_CHART_PARAMS,
679
+ description: 'An accessible description for screen readers.',
680
+ }),
681
+ );
682
+
683
+ test('THEN: it renders the description for accessibility', () => {
684
+ expect($('.ons-u-vh').text()).toBe('An accessible description for screen readers.');
685
+ });
686
+ });
687
+ });
688
+
689
+ describe('GIVEN: Params: Series: Type', () => {
690
+ describe('WHEN: a series has an invalid type', () => {
691
+ const invalidTypeParams = {
692
+ ...EXAMPLE_BAR_WITH_LINE_CHART_PARAMS,
693
+ series: [
694
+ { name: 'Invalid Series', data: [5, 15, 25], type: 'scatter' },
695
+ { name: 'Valid Line Series', data: [10, 20, 30], type: 'line' },
696
+ ],
697
+ };
698
+
699
+ const $ = cheerio.load(renderComponent('chart', invalidTypeParams));
700
+ const configScript = $(`script[data-highcharts-config--bar-chart-123]`).html();
701
+
702
+ test('THEN: it defaults non-line series type to the chartType', () => {
703
+ expect(configScript).not.toContain('"type":"scatter"');
704
+ expect(configScript).toContain('"type":"bar"');
705
+ expect(configScript).toContain('"type":"line"');
706
+ });
707
+ });
708
+ });
709
+
710
+ describe('GIVEN: Params: Download', () => {
711
+ describe('WHEN: download object are provided', () => {
712
+ const $ = cheerio.load(
713
+ renderComponent('chart', {
714
+ ...EXAMPLE_BAR_WITH_LINE_CHART_PARAMS,
715
+ download: {
716
+ title: 'Download Chart Data',
717
+ itemsList: [
718
+ { text: 'Download as PNG', url: 'https://example.com/chart.png' },
719
+ { text: 'Download as CSV', url: 'https://example.com/chart.csv' },
720
+ ],
721
+ },
722
+ }),
723
+ );
724
+
725
+ test('THEN: it renders the download section correctly', () => {
726
+ expect($('.ons-chart__download-title').text()).toBe('Download Chart Data');
727
+
728
+ const downloadLinks = $('.ons-chart__download-title').next().find('li a');
729
+ expect(downloadLinks.eq(0).text()).toBe('Download as PNG');
730
+ expect(downloadLinks.eq(0).attr('href')).toBe('https://example.com/chart.png');
731
+ expect(downloadLinks.eq(1).text()).toBe('Download as CSV');
732
+ expect(downloadLinks.eq(1).attr('href')).toBe('https://example.com/chart.csv');
733
+ });
734
+ });
735
+ });
736
+ });
737
+
738
+ describe('FOR: Column Chart with Line', () => {
739
+ describe('GIVEN: Params: required', () => {
740
+ describe('WHEN: required params are provided', () => {
741
+ const $ = cheerio.load(renderComponent('chart', EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS));
742
+ const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
743
+
744
+ test('THEN: it passes jest-axe checks', async () => {
745
+ const results = await axe($.html());
746
+ expect(results).toHaveNoViolations();
747
+ });
748
+
749
+ test('THEN: it includes one series of type "column" and another of type "line"', () => {
750
+ expect(configScript).toContain('"type":"column"');
751
+ expect(configScript).toContain('"type":"line"');
752
+ });
753
+
754
+ test('THEN: it renders the chart container with correct data attributes', () => {
755
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('column');
756
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('alternate');
757
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Column Chart');
758
+ expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('column-chart-123');
759
+ });
760
+
761
+ test('THEN: it includes the Highcharts JSON config', () => {
762
+ expect(configScript).toContain('"text":"X Axis Title"');
763
+ expect(configScript).toContain('"text":"Y Axis Title"');
764
+ });
765
+ });
766
+ });
767
+
768
+ describe('GIVEN: Params: Legend', () => {
769
+ describe('WHEN: legend is enabled', () => {
770
+ const $ = cheerio.load(renderComponent('chart', { ...EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS, legend: false }));
771
+
772
+ test('THEN: it renders the legend', () => {
773
+ const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
774
+ expect(configScript).toContain('"enabled":false');
775
+ });
776
+ });
777
+ });
778
+
779
+ describe('GIVEN: Params: Caption', () => {
780
+ describe('WHEN: caption is provided', () => {
781
+ const $ = cheerio.load(
782
+ renderComponent('chart', {
783
+ ...EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS,
784
+ caption: 'This is an example caption for the chart.',
785
+ }),
786
+ );
787
+
788
+ test('THEN: it renders the caption when provided', () => {
789
+ expect($('figcaption').text()).toBe('This is an example caption for the chart.');
790
+ });
791
+ });
792
+ });
793
+
794
+ describe('GIVEN: Params: Description', () => {
795
+ describe('WHEN: description is provided', () => {
796
+ const $ = cheerio.load(
797
+ renderComponent('chart', {
798
+ ...EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS,
799
+ description: 'An accessible description for screen readers.',
800
+ }),
801
+ );
802
+
803
+ test('THEN: it renders the description for accessibility', () => {
804
+ expect($('.ons-u-vh').text()).toBe('An accessible description for screen readers.');
805
+ });
806
+ });
807
+ });
808
+
809
+ describe('GIVEN: Params: Download', () => {
810
+ describe('WHEN: download object is provided', () => {
811
+ const $ = cheerio.load(
812
+ renderComponent('chart', {
813
+ ...EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS,
814
+ download: {
815
+ title: 'Download Chart Data',
816
+ itemsList: [
817
+ { text: 'Download as PNG', url: 'https://example.com/chart.png' },
818
+ { text: 'Download as CSV', url: 'https://example.com/chart.csv' },
819
+ ],
820
+ },
821
+ }),
822
+ );
823
+
824
+ test('THEN: it renders the download section correctly', () => {
825
+ expect($('.ons-chart__download-title').text()).toBe('Download Chart Data');
826
+
827
+ const downloadLinks = $('.ons-chart__download-title').next().find('li a');
828
+ expect(downloadLinks.eq(0).text()).toBe('Download as PNG');
829
+ expect(downloadLinks.eq(0).attr('href')).toBe('https://example.com/chart.png');
830
+ expect(downloadLinks.eq(1).text()).toBe('Download as CSV');
831
+ expect(downloadLinks.eq(1).attr('href')).toBe('https://example.com/chart.csv');
832
+ });
833
+ });
834
+ });
835
+
836
+ describe('GIVEN: Params: Series: Type', () => {
837
+ describe('WHEN: a series has an invalid type', () => {
838
+ const invalidTypeParams = {
839
+ ...EXAMPLE_COLUMN_WITH_LINE_CHART_PARAMS,
840
+ series: [
841
+ { name: 'Invalid Series', data: [5, 15, 25], type: 'scatter' },
842
+ { name: 'Valid Line Series', data: [10, 20, 30], type: 'line' },
843
+ ],
844
+ };
845
+
846
+ const $ = cheerio.load(renderComponent('chart', invalidTypeParams));
847
+ const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
848
+
849
+ test('THEN: it defaults non-line series type to the chartType', () => {
850
+ expect(configScript).not.toContain('"type":"scatter"');
851
+ expect(configScript).toContain('"type":"column"');
852
+ expect(configScript).toContain('"type":"line"');
853
+ });
854
+ });
855
+ });
856
+ });
437
857
  });