@ons/design-system 72.3.0 → 72.5.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.
- package/components/button/_button.scss +171 -3
- package/components/button/_macro.njk +4 -0
- package/components/card/_card.scss +5 -0
- package/components/card/_macro.njk +10 -2
- package/components/card/_macro.spec.js +58 -0
- package/components/card/example-card-set-with-headline-figures.njk +62 -0
- package/components/chart/_chart.scss +29 -0
- package/components/chart/_macro.njk +101 -0
- package/components/chart/_macro.spec.js +437 -0
- package/components/chart/bar-chart.js +160 -0
- package/components/chart/chart-constants.js +21 -0
- package/components/chart/chart.dom.js +8 -0
- package/components/chart/chart.js +157 -0
- package/components/chart/column-chart.js +48 -0
- package/components/chart/common-chart-options.js +200 -0
- package/components/chart/example-bar-chart.njk +54 -0
- package/components/chart/example-clustered-bar-chart.njk +60 -0
- package/components/chart/example-clustered-column-chart.njk +72 -0
- package/components/chart/example-column-chart.njk +52 -0
- package/components/chart/example-line-chart.njk +219 -0
- package/components/chart/example-stacked-bar-chart.njk +60 -0
- package/components/chart/example-stacked-column-chart.njk +65 -0
- package/components/chart/line-chart.js +47 -0
- package/components/chart/specific-chart-options.js +22 -0
- package/components/description-list/_description-list.scss +2 -0
- package/components/footer/_footer.scss +12 -0
- package/components/footer/_macro.njk +38 -17
- package/components/header/_header.scss +18 -2
- package/components/header/_macro.njk +81 -8
- package/components/header/_macro.spec.js +97 -0
- package/components/header/example-header-with-search-button.njk +190 -0
- package/components/header/header.spec.js +128 -0
- package/components/hero/_hero.scss +13 -10
- package/components/hero/_macro.njk +49 -18
- package/components/hero/_macro.spec.js +21 -2
- package/components/hero/example-hero-grey.njk +10 -8
- package/components/icon/_macro.njk +41 -8
- package/components/input/_input.scss +15 -0
- package/components/input/_macro.njk +3 -3
- package/components/navigation/navigation.dom.js +17 -0
- package/components/navigation/navigation.js +72 -2
- package/css/main.css +1 -1
- package/js/main.js +2 -0
- package/package.json +4 -1
- package/scripts/main.es5.js +1 -1
- package/scripts/main.js +1 -1
- package/scss/main.scss +1 -0
- package/scss/utilities/_grid.scss +13 -4
|
@@ -0,0 +1,437 @@
|
|
|
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_COLUMN_CHART_PARAMS,
|
|
12
|
+
} from './_test-examples';
|
|
13
|
+
|
|
14
|
+
describe('Macro: Chart', () => {
|
|
15
|
+
describe('FOR: Line Chart', () => {
|
|
16
|
+
describe('GIVEN: Params: required', () => {
|
|
17
|
+
describe('WHEN: required params are provided', () => {
|
|
18
|
+
const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_REQUIRED_PARAMS));
|
|
19
|
+
|
|
20
|
+
test('THEN: it passes jest-axe checks', async () => {
|
|
21
|
+
const results = await axe($.html());
|
|
22
|
+
expect(results).toHaveNoViolations();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('THEN: it renders the subtitle', () => {
|
|
26
|
+
expect($('.ons-chart__subtitle').text()).toBe('A sample subtitle');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('THEN: it renders the chart container with the correct data attributes', () => {
|
|
30
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('line');
|
|
31
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Line Chart');
|
|
32
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('chart-123');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('THEN: it includes the Highcharts JSON config', () => {
|
|
36
|
+
const configScript = $(`script[data-highcharts-config--chart-123]`).html();
|
|
37
|
+
expect(configScript).toContain('"text":"X Axis Title"');
|
|
38
|
+
expect(configScript).toContain('"text":"Y Axis Title"');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('THEN: it does NOT render optional fields', () => {
|
|
42
|
+
expect($('figcaption').length).toBe(0);
|
|
43
|
+
expect($('.ons-chart__download-title').length).toBe(0);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('GIVEN: Params: Theme', () => {
|
|
49
|
+
describe('WHEN: theme is provided', () => {
|
|
50
|
+
const $ = cheerio.load(
|
|
51
|
+
renderComponent('chart', {
|
|
52
|
+
...EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
|
|
53
|
+
theme: 'primary',
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
test('THEN: it renders the chart container with the correct theme', () => {
|
|
58
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('primary');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('GIVEN: Params: Heading Level', () => {
|
|
64
|
+
describe('WHEN: heading level is provided', () => {
|
|
65
|
+
const $ = cheerio.load(
|
|
66
|
+
renderComponent('chart', {
|
|
67
|
+
...EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
|
|
68
|
+
headingLevel: 3,
|
|
69
|
+
download: {
|
|
70
|
+
title: 'Download Chart Data',
|
|
71
|
+
itemsList: [
|
|
72
|
+
{ text: 'Download as PNG', url: 'https://example.com/chart.png' },
|
|
73
|
+
{ text: 'Download as CSV', url: 'https://example.com/chart.csv' },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
test('THEN: renders title with correct tag', () => {
|
|
80
|
+
expect($('.ons-chart__title')[0].tagName).toBe('h3');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('THEN: renders subtitle with correct tag', () => {
|
|
84
|
+
expect($('.ons-chart__subtitle')[0].tagName).toBe('h4');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('THEN: renders download title with correct tag', () => {
|
|
88
|
+
expect($('.ons-chart__download-title')[0].tagName).toBe('h5');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('GIVEN: Params: Config', () => {
|
|
94
|
+
describe('WHEN: config params are provided', () => {
|
|
95
|
+
const $ = cheerio.load(renderComponent('chart', EXAMPLE_LINE_CHART_WITH_CONFIG_PARAMS));
|
|
96
|
+
const configScript = $(`script[data-highcharts-config--chart-456]`).html();
|
|
97
|
+
|
|
98
|
+
test('THEN: it renders the legend when enabled', () => {
|
|
99
|
+
expect(configScript).toContain('"enabled":true');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('THEN: it includes correct xAxis properties', () => {
|
|
103
|
+
expect(configScript).toContain('"text":"X Axis Label"');
|
|
104
|
+
expect(configScript).toContain('"categories":["A","B","C"]');
|
|
105
|
+
expect(configScript).toContain('"type":"linear"');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('THEN: it includes correct yAxis properties', () => {
|
|
109
|
+
expect(configScript).toContain('"text":"Y Axis Label"');
|
|
110
|
+
expect(configScript).toContain('"labels":{"format":"{value:,.f}"}');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('THEN: it includes correct series data', () => {
|
|
114
|
+
expect(configScript).toContain('"name":"Category 1"');
|
|
115
|
+
expect(configScript).toContain('"data":[5,15,25]');
|
|
116
|
+
expect(configScript).toContain('"name":"Category 2"');
|
|
117
|
+
expect(configScript).toContain('"data":[10,20,30]');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('GIVEN: Params: Caption', () => {
|
|
123
|
+
describe('WHEN: caption is provided', () => {
|
|
124
|
+
const $ = cheerio.load(
|
|
125
|
+
renderComponent('chart', {
|
|
126
|
+
...EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
|
|
127
|
+
caption: 'This is an example caption for the chart.',
|
|
128
|
+
}),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
test('THEN: it renders the caption when provided', () => {
|
|
132
|
+
expect($('figcaption').text()).toBe('This is an example caption for the chart.');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('GIVEN: Params: Description', () => {
|
|
138
|
+
describe('WHEN: description is provided', () => {
|
|
139
|
+
const $ = cheerio.load(
|
|
140
|
+
renderComponent('chart', {
|
|
141
|
+
...EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
|
|
142
|
+
description: 'An accessible description for screen readers.',
|
|
143
|
+
}),
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
test('THEN: it renders the description for accessibility', () => {
|
|
147
|
+
expect($('.ons-u-vh').text()).toBe('An accessible description for screen readers.');
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('GIVEN: Params: Download', () => {
|
|
153
|
+
describe('WHEN: download object are provided', () => {
|
|
154
|
+
const $ = cheerio.load(
|
|
155
|
+
renderComponent('chart', {
|
|
156
|
+
...EXAMPLE_LINE_CHART_REQUIRED_PARAMS,
|
|
157
|
+
download: {
|
|
158
|
+
title: 'Download Chart Data',
|
|
159
|
+
itemsList: [
|
|
160
|
+
{ text: 'Download as PNG', url: 'https://example.com/chart.png' },
|
|
161
|
+
{ text: 'Download as CSV', url: 'https://example.com/chart.csv' },
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
test('THEN: it renders the download section correctly', () => {
|
|
168
|
+
expect($('.ons-chart__download-title').text()).toBe('Download Chart Data');
|
|
169
|
+
|
|
170
|
+
const downloadLinks = $('.ons-chart__download-title').next().find('li a');
|
|
171
|
+
expect(downloadLinks.eq(0).text()).toBe('Download as PNG');
|
|
172
|
+
expect(downloadLinks.eq(0).attr('href')).toBe('https://example.com/chart.png');
|
|
173
|
+
expect(downloadLinks.eq(1).text()).toBe('Download as CSV');
|
|
174
|
+
expect(downloadLinks.eq(1).attr('href')).toBe('https://example.com/chart.csv');
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe('FOR: Bar Chart', () => {
|
|
181
|
+
describe('GIVEN: Params: required', () => {
|
|
182
|
+
describe('WHEN: required params are provided', () => {
|
|
183
|
+
const $ = cheerio.load(renderComponent('chart', EXAMPLE_BAR_CHART_PARAMS));
|
|
184
|
+
|
|
185
|
+
test('THEN: it passes jest-axe checks', async () => {
|
|
186
|
+
const results = await axe($.html());
|
|
187
|
+
expect(results).toHaveNoViolations();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('THEN: it renders the chart container with the correct data attributes', () => {
|
|
191
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('bar');
|
|
192
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('alternate');
|
|
193
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Bar Chart');
|
|
194
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('bar-chart-123');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('THEN: it does NOT render optional fields', () => {
|
|
198
|
+
expect($('figcaption').length).toBe(0);
|
|
199
|
+
expect($('.ons-chart__download-title').length).toBe(0);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('THEN: it includes the Highcharts JSON config', () => {
|
|
203
|
+
const configScript = $(`script[data-highcharts-config--bar-chart-123]`).html();
|
|
204
|
+
expect(configScript).toContain('"text":"X Axis Title"');
|
|
205
|
+
expect(configScript).toContain('"text":"Y Axis Title"');
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe('GIVEN: Params: Config', () => {
|
|
211
|
+
describe('WHEN: config params are provided', () => {
|
|
212
|
+
const $ = cheerio.load(
|
|
213
|
+
renderComponent('chart', {
|
|
214
|
+
...EXAMPLE_BAR_CHART_PARAMS,
|
|
215
|
+
legend: true,
|
|
216
|
+
}),
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
test('THEN: it renders the legend when enabled', () => {
|
|
220
|
+
const configScript = $(`script[data-highcharts-config--bar-chart-123]`).html();
|
|
221
|
+
expect(configScript).toContain('"enabled":true');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test('THEN: it includes correct xAxis and yAxis titles', () => {
|
|
225
|
+
const configScript = $(`script[data-highcharts-config--bar-chart-123]`).html();
|
|
226
|
+
expect(configScript).toContain('"text":"X Axis Title"');
|
|
227
|
+
expect(configScript).toContain('"text":"Y Axis Title"');
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe('GIVEN: Params: Caption', () => {
|
|
233
|
+
describe('WHEN: caption is provided', () => {
|
|
234
|
+
const $ = cheerio.load(
|
|
235
|
+
renderComponent('chart', {
|
|
236
|
+
...EXAMPLE_BAR_CHART_PARAMS,
|
|
237
|
+
caption: 'This is an example caption for the chart.',
|
|
238
|
+
}),
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
test('THEN: it renders the caption when provided', () => {
|
|
242
|
+
expect($('figcaption').text()).toBe('This is an example caption for the chart.');
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe('GIVEN: Params: Description', () => {
|
|
248
|
+
describe('WHEN: description is provided', () => {
|
|
249
|
+
const $ = cheerio.load(
|
|
250
|
+
renderComponent('chart', {
|
|
251
|
+
...EXAMPLE_BAR_CHART_PARAMS,
|
|
252
|
+
description: 'An accessible description for screen readers.',
|
|
253
|
+
}),
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
test('THEN: it renders the description for accessibility', () => {
|
|
257
|
+
expect($('.ons-u-vh').text()).toBe('An accessible description for screen readers.');
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe('GIVEN: Params: Download', () => {
|
|
263
|
+
describe('WHEN: download object are provided', () => {
|
|
264
|
+
const $ = cheerio.load(
|
|
265
|
+
renderComponent('chart', {
|
|
266
|
+
...EXAMPLE_BAR_CHART_PARAMS,
|
|
267
|
+
download: {
|
|
268
|
+
title: 'Download Chart Data',
|
|
269
|
+
itemsList: [
|
|
270
|
+
{ text: 'Download as PNG', url: 'https://example.com/chart.png' },
|
|
271
|
+
{ text: 'Download as CSV', url: 'https://example.com/chart.csv' },
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
}),
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
test('THEN: it renders the download section correctly', () => {
|
|
278
|
+
expect($('.ons-chart__download-title').text()).toBe('Download Chart Data');
|
|
279
|
+
|
|
280
|
+
const downloadLinks = $('.ons-chart__download-title').next().find('li a');
|
|
281
|
+
expect(downloadLinks.eq(0).text()).toBe('Download as PNG');
|
|
282
|
+
expect(downloadLinks.eq(0).attr('href')).toBe('https://example.com/chart.png');
|
|
283
|
+
expect(downloadLinks.eq(1).text()).toBe('Download as CSV');
|
|
284
|
+
expect(downloadLinks.eq(1).attr('href')).toBe('https://example.com/chart.csv');
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
describe('GIVEN: Stacked Bar Chart', () => {
|
|
290
|
+
describe('WHEN: Stacked layout is enabled', () => {
|
|
291
|
+
const $ = cheerio.load(
|
|
292
|
+
renderComponent('chart', {
|
|
293
|
+
...EXAMPLE_BAR_CHART_PARAMS,
|
|
294
|
+
useStackedLayout: true,
|
|
295
|
+
}),
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
test('THEN: it includes stacking in the config', () => {
|
|
299
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-use-stacked-layout')).toBe('true');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test('THEN: it renders a bar chart with stacked series', () => {
|
|
303
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('bar');
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
describe('FOR: Column Chart', () => {
|
|
310
|
+
describe('GIVEN: Params: required', () => {
|
|
311
|
+
describe('WHEN: required params are provided', () => {
|
|
312
|
+
const $ = cheerio.load(renderComponent('chart', EXAMPLE_COLUMN_CHART_PARAMS));
|
|
313
|
+
|
|
314
|
+
test('THEN: it passes jest-axe checks', async () => {
|
|
315
|
+
const results = await axe($.html());
|
|
316
|
+
expect(results).toHaveNoViolations();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test('THEN: it renders the chart container with the correct data attributes', () => {
|
|
320
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('column');
|
|
321
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-theme')).toBe('alternate');
|
|
322
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-title')).toBe('Example Column Chart');
|
|
323
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-id')).toBe('column-chart-123');
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test('THEN: it does NOT render optional fields', () => {
|
|
327
|
+
expect($('figcaption').length).toBe(0);
|
|
328
|
+
expect($('.ons-chart__download-title').length).toBe(0);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test('THEN: it includes the Highcharts JSON config', () => {
|
|
332
|
+
const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
|
|
333
|
+
expect(configScript).toContain('"text":"X Axis Title"');
|
|
334
|
+
expect(configScript).toContain('"text":"Y Axis Title"');
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
describe('GIVEN: Params: Config', () => {
|
|
340
|
+
describe('WHEN: config params are provided', () => {
|
|
341
|
+
const $ = cheerio.load(
|
|
342
|
+
renderComponent('chart', {
|
|
343
|
+
...EXAMPLE_COLUMN_CHART_PARAMS,
|
|
344
|
+
legend: true,
|
|
345
|
+
}),
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
test('THEN: it renders the legend when enabled', () => {
|
|
349
|
+
const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
|
|
350
|
+
expect(configScript).toContain('"enabled":true');
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test('THEN: it includes correct xAxis and yAxis titles', () => {
|
|
354
|
+
const configScript = $(`script[data-highcharts-config--column-chart-123]`).html();
|
|
355
|
+
expect(configScript).toContain('"text":"X Axis Title"');
|
|
356
|
+
expect(configScript).toContain('"text":"Y Axis Title"');
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
describe('GIVEN: Params: Caption', () => {
|
|
362
|
+
describe('WHEN: caption is provided', () => {
|
|
363
|
+
const $ = cheerio.load(
|
|
364
|
+
renderComponent('chart', {
|
|
365
|
+
...EXAMPLE_COLUMN_CHART_PARAMS,
|
|
366
|
+
caption: 'This is an example caption for the chart.',
|
|
367
|
+
}),
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
test('THEN: it renders the caption when provided', () => {
|
|
371
|
+
expect($('figcaption').text()).toBe('This is an example caption for the chart.');
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
describe('GIVEN: Params: Description', () => {
|
|
377
|
+
describe('WHEN: description is provided', () => {
|
|
378
|
+
const $ = cheerio.load(
|
|
379
|
+
renderComponent('chart', {
|
|
380
|
+
...EXAMPLE_COLUMN_CHART_PARAMS,
|
|
381
|
+
description: 'An accessible description for screen readers.',
|
|
382
|
+
}),
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
test('THEN: it renders the description for accessibility', () => {
|
|
386
|
+
expect($('.ons-u-vh').text()).toBe('An accessible description for screen readers.');
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
describe('GIVEN: Params: Download', () => {
|
|
392
|
+
describe('WHEN: download object are provided', () => {
|
|
393
|
+
const $ = cheerio.load(
|
|
394
|
+
renderComponent('chart', {
|
|
395
|
+
...EXAMPLE_COLUMN_CHART_PARAMS,
|
|
396
|
+
download: {
|
|
397
|
+
title: 'Download Chart Data',
|
|
398
|
+
itemsList: [
|
|
399
|
+
{ text: 'Download as PNG', url: 'https://example.com/chart.png' },
|
|
400
|
+
{ text: 'Download as CSV', url: 'https://example.com/chart.csv' },
|
|
401
|
+
],
|
|
402
|
+
},
|
|
403
|
+
}),
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
test('THEN: it renders the download section correctly', () => {
|
|
407
|
+
expect($('.ons-chart__download-title').text()).toBe('Download Chart Data');
|
|
408
|
+
|
|
409
|
+
const downloadLinks = $('.ons-chart__download-title').next().find('li a');
|
|
410
|
+
expect(downloadLinks.eq(0).text()).toBe('Download as PNG');
|
|
411
|
+
expect(downloadLinks.eq(0).attr('href')).toBe('https://example.com/chart.png');
|
|
412
|
+
expect(downloadLinks.eq(1).text()).toBe('Download as CSV');
|
|
413
|
+
expect(downloadLinks.eq(1).attr('href')).toBe('https://example.com/chart.csv');
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
describe('GIVEN: Stacked Column Chart', () => {
|
|
419
|
+
describe('WHEN: Stacked layout is enabled', () => {
|
|
420
|
+
const $ = cheerio.load(
|
|
421
|
+
renderComponent('chart', {
|
|
422
|
+
...EXAMPLE_COLUMN_CHART_PARAMS,
|
|
423
|
+
useStackedLayout: true,
|
|
424
|
+
}),
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
test('THEN: it includes stacking in the config', () => {
|
|
428
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-use-stacked-layout')).toBe('true');
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test('THEN: it renders a column chart with stacked series', () => {
|
|
432
|
+
expect($('[data-highcharts-base-chart]').attr('data-highcharts-type')).toBe('column');
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
});
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import ChartConstants from './chart-constants';
|
|
2
|
+
|
|
3
|
+
class BarChart {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.constants = ChartConstants.constants();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
getBarChartOptions = (useStackedLayout) => {
|
|
9
|
+
return {
|
|
10
|
+
plotOptions: {
|
|
11
|
+
bar: {
|
|
12
|
+
// Set the width of the bars to be 30px
|
|
13
|
+
// The spacing is worked out in the specific-chart-options.js file
|
|
14
|
+
pointWidth: 30, // Fixed bar height
|
|
15
|
+
pointPadding: 0,
|
|
16
|
+
groupPadding: 0,
|
|
17
|
+
borderWidth: 0,
|
|
18
|
+
borderRadius: 0,
|
|
19
|
+
// Set the data labels to be enabled and positioned outside the bars
|
|
20
|
+
// We can add custom formatting on each chart to move the labels inside the bars if the bar is wide enough
|
|
21
|
+
dataLabels: {
|
|
22
|
+
enabled: true,
|
|
23
|
+
inside: false,
|
|
24
|
+
style: {
|
|
25
|
+
textOutline: 'none',
|
|
26
|
+
// The design system does not include a semibold font weight, so we use 700 (bold) as an alternative.
|
|
27
|
+
fontWeight: '700',
|
|
28
|
+
color: this.constants.labelColor,
|
|
29
|
+
fontSize: this.constants.mobileFontSize,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
series: {
|
|
34
|
+
stacking: useStackedLayout ? 'normal' : null,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
xAxis: {
|
|
38
|
+
// Update the category label colours for bar charts
|
|
39
|
+
labels: {
|
|
40
|
+
style: {
|
|
41
|
+
color: this.constants.categoryLabelColor,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
// remove the tick marks for bar charts
|
|
45
|
+
tickWidth: 0,
|
|
46
|
+
tickLength: 0,
|
|
47
|
+
tickColor: 'transparent',
|
|
48
|
+
title: { align: 'high', textAlign: 'middle', reserveSpace: false, rotation: 0, y: -25, useHTML: true },
|
|
49
|
+
},
|
|
50
|
+
yAxis: {
|
|
51
|
+
title: {
|
|
52
|
+
// Override the y Axis title settings for bar charts where the y axis is horizontal
|
|
53
|
+
textAlign: 'right',
|
|
54
|
+
offset: undefined,
|
|
55
|
+
y: 0,
|
|
56
|
+
reserveSpace: true,
|
|
57
|
+
useHTML: false,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Updates the config to move the data labels inside the bars, but only if the bar is wide enough
|
|
64
|
+
// This may also need to run when the chart is resized
|
|
65
|
+
postLoadDataLabels = (currentChart) => {
|
|
66
|
+
const insideOptions = {
|
|
67
|
+
dataLabels: this.getBarChartLabelsInsideOptions(),
|
|
68
|
+
};
|
|
69
|
+
const outsideOptions = {
|
|
70
|
+
dataLabels: this.getBarChartLabelsOutsideOptions(),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
currentChart.series.forEach((series) => {
|
|
74
|
+
const points = series.data;
|
|
75
|
+
points.forEach((point) => {
|
|
76
|
+
// Get the actual width of the data label
|
|
77
|
+
const labelWidth = point.dataLabel && point.dataLabel.getBBox().width;
|
|
78
|
+
// Move the data labels inside the bar if the bar is wider than the label plus some padding
|
|
79
|
+
if (point.shapeArgs.height > labelWidth + 20) {
|
|
80
|
+
point.update(insideOptions, false);
|
|
81
|
+
} else {
|
|
82
|
+
point.update(outsideOptions, false);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
currentChart.redraw();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
getBarChartLabelsInsideOptions = () => ({
|
|
91
|
+
inside: true,
|
|
92
|
+
align: 'right',
|
|
93
|
+
verticalAlign: 'middle',
|
|
94
|
+
style: {
|
|
95
|
+
color: 'white',
|
|
96
|
+
fontWeight: 'bold',
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
getBarChartLabelsOutsideOptions = () => ({
|
|
101
|
+
inside: false,
|
|
102
|
+
align: undefined,
|
|
103
|
+
verticalAlign: undefined,
|
|
104
|
+
style: {
|
|
105
|
+
textOutline: 'none',
|
|
106
|
+
// The design system does not include a semibold font weight, so we use 700 (bold) as an alternative.
|
|
107
|
+
fontWeight: '700',
|
|
108
|
+
color: this.constants.labelColor,
|
|
109
|
+
fontSize: this.constants.mobileFontSize,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// This updates the height of the vertical axis and overall chart to fit the number of categories
|
|
114
|
+
// Note that the vertical axis on a bar chart is the x axis
|
|
115
|
+
updateBarChartHeight = (config, currentChart, useStackedLayout) => {
|
|
116
|
+
const numberOfCategories = config.xAxis.categories.length;
|
|
117
|
+
const numberOfSeries = currentChart.series.length; // Get number of bar series
|
|
118
|
+
let barHeight = 30; // Height of each individual bar - set in bar-chart-plot-options
|
|
119
|
+
let groupSpacing = 0; // Space we want between category groups, or between series groups for cluster charts
|
|
120
|
+
let categoriesTotalHeight = 0;
|
|
121
|
+
let totalSpaceHeight = 0;
|
|
122
|
+
if (useStackedLayout == false && numberOfSeries > 1) {
|
|
123
|
+
// slightly lower bar height for cluster charts
|
|
124
|
+
barHeight = 28;
|
|
125
|
+
// for cluster charts there is no space between the bars within a series, and 14px between each series
|
|
126
|
+
groupSpacing = 14;
|
|
127
|
+
// lower barHeight for series with 3 categories or more
|
|
128
|
+
if (numberOfSeries >= 3) {
|
|
129
|
+
barHeight = 20;
|
|
130
|
+
}
|
|
131
|
+
categoriesTotalHeight = numberOfCategories * barHeight * numberOfSeries;
|
|
132
|
+
|
|
133
|
+
totalSpaceHeight = numberOfCategories * groupSpacing;
|
|
134
|
+
// work out the group padding for cluster charts which is measured in xAxis units.
|
|
135
|
+
const plotHeight = categoriesTotalHeight + totalSpaceHeight;
|
|
136
|
+
const xUnitHeight = plotHeight / numberOfCategories;
|
|
137
|
+
const groupPadding = groupSpacing / 2 / xUnitHeight;
|
|
138
|
+
currentChart.series.forEach((series) => {
|
|
139
|
+
series.update({
|
|
140
|
+
groupPadding: groupPadding,
|
|
141
|
+
pointWidth: barHeight,
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
} else {
|
|
145
|
+
groupSpacing = 10;
|
|
146
|
+
categoriesTotalHeight = numberOfCategories * barHeight;
|
|
147
|
+
totalSpaceHeight = (numberOfCategories - 1) * groupSpacing;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
config.xAxis.height = categoriesTotalHeight + totalSpaceHeight;
|
|
151
|
+
const totalHeight = currentChart.plotTop + config.xAxis.height + currentChart.marginBottom;
|
|
152
|
+
if (totalHeight !== currentChart.chartHeight) {
|
|
153
|
+
currentChart.setSize(null, totalHeight, false);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
currentChart.redraw();
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export default BarChart;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class ChartConstants {
|
|
2
|
+
static constants() {
|
|
3
|
+
const constants = {
|
|
4
|
+
primaryTheme: ['#206095', '#27a0cc', '#003c57', '#118c7b', '#a8bd3a', '#871a5b', '#f66068', '#746cb1', '#22d0b6'],
|
|
5
|
+
// Alternate theme colours from https://service-manual.ons.gov.uk/data-visualisation/colours/using-colours-in-charts
|
|
6
|
+
alternateTheme: ['#206095', '#27A0CC', '#871A5B', '#A8BD3A', '#F66068'],
|
|
7
|
+
labelColor: '#414042',
|
|
8
|
+
axisLabelColor: '#707071',
|
|
9
|
+
categoryLabelColor: '#414042',
|
|
10
|
+
gridLineColor: '#d9d9d9',
|
|
11
|
+
zeroLineColor: '#b3b3b3',
|
|
12
|
+
// Responsive font sizes
|
|
13
|
+
mobileFontSize: '0.75rem', // 12px
|
|
14
|
+
desktopFontSize: '0.875rem', // 14px
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return constants;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default ChartConstants;
|