@ons/design-system 72.10.7 → 72.10.8

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.
@@ -110,6 +110,12 @@
110
110
  aspect-ratio: 16 / 9;
111
111
  }
112
112
  }
113
+
114
+ &__non-js-hide {
115
+ @media (scripting: none) {
116
+ display: none;
117
+ }
118
+ }
113
119
  }
114
120
 
115
121
  // This is a workaround to position the axis title to the left
@@ -14,6 +14,7 @@
14
14
  {% set closingSubtitleTag = "</h" ~ (headingLevel + 1) ~ ">" %}
15
15
  {% set openingDownloadTitleTag = "<h" ~ (headingLevel + 2) %}
16
16
  {% set closingDownloadTitleTag = "</h" ~ (headingLevel + 2) ~ ">" %}
17
+ {% set audioDescriptionId = "chart-audio-description-" ~ params.id %}
17
18
  <div
18
19
  {% if not params.iframeUrl %}
19
20
  data-highcharts-base-chart data-highcharts-type="{{ params.chartType }}" data-highcharts-theme="{{ params.theme }}"
@@ -48,18 +49,23 @@
48
49
  {% endif %}
49
50
  {% endif %}
50
51
  >
51
- <figure class="ons-chart">
52
+ <figure class="ons-chart" aria-describedby="{{ audioDescriptionId }}">
52
53
  {{ openingTitleTag | safe }} class="ons-chart__title">{{ params.title }}{{ closingTitleTag | safe }}
53
54
  {% if params.subtitle %}
54
55
  {{ openingSubtitleTag | safe }}
55
56
  class="ons-chart__subtitle">{{ params.subtitle }}{{ closingSubtitleTag | safe }}
56
57
  {% endif %}
57
- {% if params.description %}
58
- <p class="ons-u-vh">{{ params.description }}</p>
59
- {% endif %}
58
+
59
+ <p class="ons-u-vh" id="{{ audioDescriptionId }}">{{ params.description }}</p>
60
+
60
61
  {% if params.iframeUrl %}
61
- <div data-chart-iframe id="{{ params.id }}" data-url="{{ params.iframeUrl }}" data-title="{{ params.title }}">
62
- <iframe src="{{ params.iframeUrl }}" title="{{ params.title }}" class="ons-chart__iframe"> </iframe>
62
+ <div
63
+ class="ons-chart__iframe-wrapper{% if params.fallbackImageUrl %} ons-chart__non-js-hide{% endif %}"
64
+ id="{{ params.id }}"
65
+ data-url="{{ params.iframeUrl }}"
66
+ data-title="{{ params.title }}"
67
+ >
68
+ <iframe src="{{ params.iframeUrl }}" title="{{ params.title }}" class="ons-chart__iframe"></iframe>
63
69
  </div>
64
70
  {% else %}
65
71
  {% if params.chartType == 'boxplot' and (params.estimateLineLabel or params.uncertaintyRangeLabel) and params.legend == true %}
@@ -86,6 +92,18 @@
86
92
  </p>
87
93
  {% endif %}
88
94
  {% endif %}
95
+
96
+ {% if params.fallbackImageUrl %}
97
+ {% set fallbackImageId = ["fallback-image--", params.id] | join %}
98
+ <noscript id="{{ fallbackImageId }}">
99
+ {# Note that a more detailed description of the chart is provided in the audio description above #}
100
+ <img
101
+ src="{{ params.fallbackImageUrl }}"
102
+ alt="{{ params.fallbackImageAlt or 'Fallback image for the chart as JavaScript is disabled' }}"
103
+ class="ons-chart__fallback-image"
104
+ />
105
+ </noscript>
106
+ {% endif %}
89
107
  {#
90
108
  Footnotes for the annotations at mobile
91
109
  Hidden from screen readers because the full text will be read out where they appear in the chart
@@ -193,6 +193,52 @@ describe('Macro: Chart', () => {
193
193
  });
194
194
  });
195
195
 
196
+ describe('GIVEN: Params: fallbackImageUrl', () => {
197
+ describe('WHEN: fallbackImageUrl is provided', () => {
198
+ const $ = cheerio.load(
199
+ renderComponent('chart', {
200
+ ...EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS,
201
+ fallbackImageUrl: '/img/small/line-chart-screenshot.png',
202
+ }),
203
+ );
204
+ const noScriptFallbackImage = $(`#fallback-image--chart-456`).html();
205
+ test('THEN: it renders the fallback image', () => {
206
+ expect(noScriptFallbackImage).toContain('/img/small/line-chart-screenshot.png');
207
+ });
208
+ });
209
+ });
210
+
211
+ describe('GIVEN: Params: fallbackImageAlt', () => {
212
+ describe('WHEN: fallbackImageAlt is provided', () => {
213
+ const $ = cheerio.load(
214
+ renderComponent('chart', {
215
+ ...EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS,
216
+ fallbackImageUrl: '/img/small/line-chart-screenshot.png',
217
+ fallbackImageAlt: 'A description of the fallback image for screen readers',
218
+ }),
219
+ );
220
+ const noScriptFallbackImage = $(`#fallback-image--chart-456`).html();
221
+ test('THEN: it renders the customised fallback image alt text', () => {
222
+ expect(noScriptFallbackImage).toContain('alt="A description of the fallback image for screen readers"');
223
+ });
224
+ });
225
+ });
226
+
227
+ describe('GIVEN: Params: no fallbackImageAlt', () => {
228
+ describe('WHEN: fallbackImageAlt is not provided', () => {
229
+ const $ = cheerio.load(
230
+ renderComponent('chart', {
231
+ ...EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS,
232
+ fallbackImageUrl: '/img/small/line-chart-screenshot.png',
233
+ }),
234
+ );
235
+ const noScriptFallbackImage = $(`#fallback-image--chart-456`).html();
236
+ test('THEN: it renders the default fallback image alt text', () => {
237
+ expect(noScriptFallbackImage).toContain('alt="Fallback image for the chart as JavaScript is disabled"');
238
+ });
239
+ });
240
+ });
241
+
196
242
  describe('GIVEN: Params: Caption', () => {
197
243
  describe('WHEN: caption is provided', () => {
198
244
  const $ = cheerio.load(
@@ -1489,11 +1535,11 @@ describe('Macro: Chart', () => {
1489
1535
  });
1490
1536
 
1491
1537
  test('THEN: it renders the iframe', () => {
1492
- expect($('[data-chart-iframe]').length).toBe(1);
1538
+ expect($('.ons-chart__iframe-wrapper').length).toBe(1);
1493
1539
  });
1494
1540
 
1495
1541
  test('THEN: it includes the iframe title as a data attribute', () => {
1496
- const iframe = $('[data-chart-iframe]');
1542
+ const iframe = $('.ons-chart__iframe-wrapper');
1497
1543
  expect(iframe.attr('data-title')).toBe(EXAMPLE_IFRAME_CHART_PARAMS.title);
1498
1544
  });
1499
1545
 
@@ -1648,6 +1694,19 @@ describe('Macro: Chart', () => {
1648
1694
  expect($('[data-invalid-chart-type]').length).toBe(0);
1649
1695
  });
1650
1696
  });
1697
+
1698
+ describe('WHEN: Params: fallbackImageUrl is set', () => {
1699
+ const $ = cheerio.load(
1700
+ renderComponent('chart', {
1701
+ ...EXAMPLE_IFRAME_CHART_PARAMS,
1702
+ fallbackImageUrl: '/img/small/line-chart-screenshot.png',
1703
+ }),
1704
+ );
1705
+ const iframe = $(`.ons-chart__iframe-wrapper`);
1706
+ test('THEN: the iframe is hidden when JavaScript is disabled', () => {
1707
+ expect(iframe.attr('class')).toContain('ons-chart__non-js-hide');
1708
+ });
1709
+ });
1651
1710
  });
1652
1711
  });
1653
1712
  });
@@ -71,6 +71,7 @@ class BarChart {
71
71
  };
72
72
 
73
73
  // Updates the config to move the data labels inside the bars, but only if the bar is wide enough
74
+ // See the checkHideDataLabels function in chart.js for more details about when this function is run
74
75
  // This also runs when the chart is resized
75
76
  postLoadDataLabels = (currentChart) => {
76
77
  const insideOptions = this.getBarChartLabelsInsideOptions();
@@ -2,7 +2,7 @@ import pym from 'pym.js';
2
2
 
3
3
  class ChartIframeResize {
4
4
  static selector() {
5
- return '[data-chart-iframe]';
5
+ return '.ons-chart__iframe-wrapper';
6
6
  }
7
7
 
8
8
  constructor(node) {
@@ -1,8 +1,10 @@
1
1
  import HighchartsBaseChart from './chart';
2
2
  import domready from '../../js/domready';
3
3
 
4
- domready(async () => {
5
- [HighchartsBaseChart].forEach((Component) => {
6
- document.querySelectorAll(Component.selector()).forEach((el) => new Component(el));
4
+ document.fonts.ready.then(() => {
5
+ domready(async () => {
6
+ [HighchartsBaseChart].forEach((Component) => {
7
+ document.querySelectorAll(Component.selector()).forEach((el) => new Component(el));
8
+ });
7
9
  });
8
10
  });
@@ -192,9 +192,15 @@ class HighchartsBaseChart {
192
192
  };
193
193
 
194
194
  // Check if the data labels should be hidden
195
- // They should be hidden for clustered bar charts with more than 2 series, and also for stacked bar charts
195
+ // They should be hidden where there are more than 20 data points in a series, for clustered bar charts with more than 2 series, and also for stacked bar charts
196
196
  checkHideDataLabels = () => {
197
- return (this.chartType === 'bar' && this.config.series.length > 2) || this.useStackedLayout === true;
197
+ let hideDataLabels = (this.chartType === 'bar' && this.config.series.length > 2) || this.useStackedLayout === true;
198
+ this.config.series.forEach((series) => {
199
+ if (series.data.length > 20) {
200
+ hideDataLabels = true;
201
+ }
202
+ });
203
+ return hideDataLabels;
198
204
  };
199
205
 
200
206
  // Adjust font size and annotations for smaller width of chart
@@ -63,6 +63,7 @@
63
63
  "title": "Y axis title"
64
64
  },
65
65
  "percentageHeightDesktop": 50,
66
- "percentageHeightMobile": 120
66
+ "percentageHeightMobile": 120,
67
+ "fallbackImageUrl": '/img/small/area-chart-screenshot.png'
67
68
  })
68
69
  }}
@@ -27,6 +27,7 @@ may not be available in the future once Florence is decommissioned. #}
27
27
  }
28
28
  ]
29
29
  },
30
- "iframeUrl": "https://www.ons.gov.uk/visualisations/dvc3294/fig01/index.html"
30
+ "iframeUrl": "https://www.ons.gov.uk/visualisations/dvc3294/fig01/index.html",
31
+ "fallbackImageUrl": "/img/small/line-chart-screenshot.png"
31
32
  })
32
33
  }}
@@ -222,6 +222,7 @@
222
222
  }
223
223
  ],
224
224
  "percentageHeightDesktop": 35,
225
- "percentageHeightMobile": 90
225
+ "percentageHeightMobile": 90,
226
+ "fallbackImageUrl": '/img/small/line-chart-screenshot.png'
226
227
  })
227
228
  }}
@@ -134,7 +134,7 @@ export default class Tabs {
134
134
  window.removeEventListener('hashchange', this.component.boundOnHashChange, true);
135
135
  }
136
136
 
137
- // Handle haschange so that the browser can cycle through the tab history
137
+ // Handle hashchange so that the browser can cycle through the tab history
138
138
  onHashChange() {
139
139
  const hash = window.location.hash;
140
140
  const tabWithHash = this.getTab(hash);
@@ -259,7 +259,7 @@ export default class Tabs {
259
259
  }
260
260
 
261
261
  getPanel(tab) {
262
- const panelSelector = this.getHref(tab).replace(/\./g, '\\.');
262
+ const panelSelector = this.getHref(tab).replace(/([ .:[\]{}()<>?+*~=|^$!\\])/g, '\\$1');
263
263
  const panel = this.component.querySelector(panelSelector);
264
264
  return panel;
265
265
  }