@ons/design-system 72.10.4 → 72.10.5

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.
@@ -8,19 +8,7 @@
8
8
  @extend .ons-u-fs-r--b;
9
9
  }
10
10
 
11
- &__title {
12
- font-size: 1.25rem;
13
- line-height: 1.625rem;
14
- }
15
-
16
- &__subtitle {
17
- font-size: 1rem;
18
- line-height: 1.3rem;
19
- }
20
-
21
11
  &__caption {
22
- font-size: 1rem;
23
- line-height: 1.2rem;
24
12
  color: var(--ons-color-grey-75);
25
13
  }
26
14
 
@@ -91,8 +79,8 @@
91
79
 
92
80
  &-item {
93
81
  &--uncertainty {
94
- width: 14px;
95
- height: 14px;
82
+ width: 12px;
83
+ height: 12px;
96
84
  background-color: rgb(32 96 149 / 65%);
97
85
  }
98
86
 
@@ -30,6 +30,9 @@ class Boxplot {
30
30
  color: this.constants.legendLabelColor,
31
31
  },
32
32
  },
33
+ tooltip: {
34
+ enabled: false,
35
+ },
33
36
  };
34
37
  };
35
38
  }
@@ -20,6 +20,9 @@ class ColumnRangeChart {
20
20
  },
21
21
  },
22
22
  },
23
+ tooltip: {
24
+ enabled: false,
25
+ },
23
26
  xAxis: {
24
27
  // Update the category label colours for bar charts
25
28
  labels: {
@@ -150,8 +150,9 @@ class SpecificChartOptions {
150
150
  fill: item.color,
151
151
  stroke: item.color,
152
152
  'stroke-width': 1,
153
- width: 12,
154
- height: 12,
153
+ // 10px accounts for the stroke width to end up at 12px overall
154
+ width: 10,
155
+ height: 10,
155
156
  'data-custom-legend-symbol': true,
156
157
  });
157
158
  legendSymbol.add(label.parentGroup);
@@ -159,11 +160,14 @@ class SpecificChartOptions {
159
160
  x: 15, // Adjust label position to account for shorter space that the symbol takes up
160
161
  });
161
162
  }
162
- } else if (seriesType === 'columnrange') {
163
+ } else if (seriesType === 'scatter') {
164
+ // Because the scatter icons have a white border, we have to render them
165
+ // slightly larger to make them appear at 12 x 12 px
166
+ // Also the text looks better aligned if the y value is tweaked slightly for the scatter icons
163
167
  symbol.attr({
164
- width: 14,
165
- height: 14,
166
- y: 8,
168
+ width: 13,
169
+ height: 13,
170
+ y: 7,
167
171
  });
168
172
  } else {
169
173
  if (!symbol) return;
@@ -857,7 +857,7 @@ describe('FOR: Macro: Header', () => {
857
857
  const $ = cheerio.load(renderComponent('header', { ...EXAMPLE_HEADER_SEARCH_LINKS, variants: 'basic' }));
858
858
 
859
859
  test('THEN: it renders heading with provided text', () => {
860
- expect($('.ons-header-nav-search__heading').text()).toBe('Header Search');
860
+ expect($('.ons-header-nav-search__heading').text().trim()).toBe('Header Search');
861
861
  });
862
862
  });
863
863
 
@@ -102,7 +102,7 @@ describe('macro: language-selector', () => {
102
102
  it('has the `abbrText` rendered', () => {
103
103
  const $ = cheerio.load(renderComponent('language-selector', EXAMPLE_WITH_TWO_LANGUAGES));
104
104
 
105
- expect($('.ons-language-links__item a span:first-child').text()).toBe('CY');
105
+ expect($('.ons-language-links__item a span:nth-child(2)').text()).toBe('CY');
106
106
  });
107
107
  });
108
108
 
@@ -124,8 +124,9 @@ describe('macro: language-selector', () => {
124
124
  it('does not show the current language', () => {
125
125
  const $ = cheerio.load(renderComponent('language-selector', EXAMPLE_WITH_THREE_LANGUAGES));
126
126
 
127
- expect($('.ons-language-links__item:first-child a').text()).toBe('English');
128
- expect($('.ons-language-links__item:last-child a').text()).toBe('Polski');
127
+ // .replace(/\s+/g, ' ') will replace any sequence of whitespace characters (spaces, tabs, newlines) with a single space
128
+ expect($('.ons-language-links__item:first-child a').text().replace(/\s+/g, ' ').trim()).toBe('Change language to English');
129
+ expect($('.ons-language-links__item:last-child a').text().replace(/\s+/g, ' ').trim()).toBe('Change language to Polski');
129
130
  });
130
131
 
131
132
  it('has the visibility class applied', () => {
@@ -2,11 +2,27 @@
2
2
  {% from "components/button/_macro.njk" import onsButton %}
3
3
  {% from "components/icon/_macro.njk" import onsIcon %}
4
4
  {% set variants = params.variants if params.variants %}
5
+ {% set hasRowSpan = false %}
6
+ {% for row in params.thList %}
7
+ {% for th in row.ths %}
8
+ {% if th.rowspan %}
9
+ {% set hasRowSpan = true %}
10
+ {% endif %}
11
+ {% endfor %}
12
+ {% endfor %}
13
+
14
+ {% for row in params.trs %}
15
+ {% for td in row.tds %}
16
+ {% if td.rowspan %}
17
+ {% set hasRowSpan = true %}
18
+ {% endif %}
19
+ {% endfor %}
20
+ {% endfor %}
5
21
 
6
22
  {% set table %}
7
23
  <table
8
24
  {% if params.id %}id="{{ params.id }}"{% endif %}
9
- class="ons-table{{ ' ' + params.tableClasses if params.tableClasses else '' }}{% if variants and variants is not string %}{% for variant in variants %}{{ ' ' }}ons-table--{{ variant }}{% endfor %}{% else %}{{ ' ' }}ons-table--{{ variants }}{% endif %}"
25
+ class="ons-table{{ ' ' + params.tableClasses if params.tableClasses else '' }}{% if hasRowSpan %}{{ ' ' }}ons-table--has-rowspan{% endif %}{% if variants %}{% if variants is not string %}{% for variant in variants %}{{ ' ' }}ons-table--{{ variant }}{% endfor %}{% else %}{{ ' ' }}ons-table--{{ variants }}{% endif %}{% endif %}"
10
26
  {% if params.sortBy and 'sortable' in variants %}
11
27
  data-aria-sort="{{ params.sortBy }}" data-aria-asc="{{ params.ariaAsc }}" data-aria-desc="{{ params.ariaDesc }}"
12
28
  {% endif %}
@@ -17,55 +33,78 @@
17
33
  </caption>
18
34
  {% endif %}
19
35
  <thead class="ons-table__head">
20
- <tr class="ons-table__row">
21
- {% for th in params.ths %}
22
- <th
23
- scope="col"
24
- class="ons-table__header{{ ' ' + th.thClasses if th.thClasses else '' }}{{ ' ons-table__header--numeric'if th.numeric }}"
25
- {% if 'sortable' in variants %}aria-sort="{{- th.ariaSort | default('none') -}}"{% endif %}{% if th.widthPercentage %}width="{{ th.widthPercentage }}%"{% endif %}
26
- >
27
- <span class="ons-table__header-text">{{- th.value -}}</span>
28
- {% if 'sortable' in variants %}
29
- {{-
30
- onsIcon({
31
- "iconType": "sort-sprite",
32
- "id": th.value | replace(' ', '-'),
33
- "classes": 'ons-u-d-no'
34
- })
35
- -}}
36
- {% endif %}
37
- </th>
38
- {% endfor %}
39
- </tr>
36
+ {# Uses thList if multiple-row headers, otherwise wrap single ths array into a default group #}
37
+ {% if params.thList %}
38
+ {% set thGroups = params.thList %}
39
+ {% else %}
40
+ {% set thGroups = [ { ths: params.ths } ] %}
41
+ {% endif %}
42
+ {% for headerCols in thGroups %}
43
+ <tr class="ons-table__row">
44
+ {% for th in headerCols.ths %}
45
+ <th
46
+ scope="col"
47
+ class="ons-table__header{{ ' ' + th.thClasses if th.thClasses else '' }}{{ ' ons-table__header--numeric' if th.numeric }}"
48
+ {% if th.colspan %}colspan="{{ th.colspan }}"{% endif %}
49
+ {% if th.rowspan %}rowspan="{{ th.rowspan }}"{% endif %}
50
+ {% if 'sortable' in variants %}aria-sort="{{ th.ariaSort | default('none') }}"{% endif %}
51
+ {% if th.widthPercentage %}width="{{ th.widthPercentage }}%"{% endif %}
52
+ >
53
+ <span class="ons-table__header-text">{{ th.value | safe }}</span>
54
+ {% if 'sortable' in variants %}
55
+ {{-
56
+ onsIcon({
57
+ "iconType": "sort-sprite",
58
+ "id": th.value | replace(' ', '-'),
59
+ "classes": 'ons-u-d-no'
60
+ })
61
+ -}}
62
+ {% endif %}
63
+ </th>
64
+ {% endfor %}
65
+ </tr>
66
+ {% endfor %}
40
67
  </thead>
41
68
  <tbody class="ons-table__body">
42
69
  {% for tr in params.trs %}
43
70
  <tr class="ons-table__row{{ ' ons-table__row--highlight' if tr.highlight }}" {% if tr.id %}id="{{ tr.id }}"{% endif %}>
44
71
  {% for td in tr.tds %}
45
- <td
46
- class="ons-table__cell{{ ' ' + td.tdClasses if td.tdClasses else '' }}{{ ' ons-table__cell--numeric' if td.numeric }}"
47
- {% if td.id %}id="{{ td.id }}"{% endif %}
48
- {% if td.data %}{{ ' ' }}data-th="{{ td.data }}"{% endif %}
49
- {% if td.dataSort %}{{ ' ' }}data-sort-value="{{ td.dataSort }}"{% endif %}
72
+ {% if td.heading == true %}
73
+ {% set openingTag = '<th' %}
74
+ {% set closingTag = '</th>' %}
75
+ {% else %}
76
+ {% set openingTag = '<td' %}
77
+ {% set closingTag = '</td>' %}
78
+ {% endif %}
79
+ {% set isFirstRow = loop.index == 0 and td.rowspan %}
80
+ {% set rowSpan = isFirstRow and loop.index0 == rowspanCount %}
81
+ {{ openingTag | safe }}
82
+ class="ons-table__cell{{ ' ' + td.tdClasses if td.tdClasses else '' }}{{ ' ons-table__cell--numeric' if td.numeric }}"
83
+ {% if td.id %}id="{{ td.id }}"{% endif %}
84
+ {% if td.data %}{{ ' ' }}data-th="{{ td.data }}"{% endif %}
85
+ {% if td.colspan %}colspan="{{ td.colspan }}"{% endif %}
86
+ {% if td.rowspan %}rowspan="{{ td.rowspan }}"{% endif %}
87
+ {% if td.dataSort %}{{ ' ' }}data-sort-value="{{ td.dataSort }}"{% endif %}
88
+ {% if td.heading %}scope="row"{% endif %}
50
89
  >
51
- {% if td.form %}
52
- <form action="{{ td.form.action }}" method="{{ td.form.method | default('POST') }}">
53
- {{ onsButton(td.form.button) }}
54
- {% if td.form.hiddenFormField %}
55
- {% for hiddenField in td.form.hiddenFormField %}
56
- <input
57
- type="hidden"
58
- {% if hiddenField.name %}name="{{ hiddenField.name }}"{% endif %}
59
- {% if hiddenField.value %}value="{{ hiddenField.value }}"{% endif %}
60
- />
61
- {% endfor %}
62
- {% endif %}
63
- </form>
64
- {% endif %}
65
- {% if td.value %}
66
- {{ td.value | safe }}
67
- {% endif %}
68
- </td>
90
+ {% if td.form %}
91
+ <form action="{{ td.form.action }}" method="{{ td.form.method | default('POST') }}">
92
+ {{ onsButton(td.form.button) }}
93
+ {% if td.form.hiddenFormField %}
94
+ {% for hiddenField in td.form.hiddenFormField %}
95
+ <input
96
+ type="hidden"
97
+ {% if hiddenField.name %}name="{{ hiddenField.name }}"{% endif %}
98
+ {% if hiddenField.value %}value="{{ hiddenField.value }}"{% endif %}
99
+ />
100
+ {% endfor %}
101
+ {% endif %}
102
+ </form>
103
+ {% endif %}
104
+ {% if td.value %}
105
+ {{ td.value | safe }}
106
+ {% endif %}
107
+ {{ closingTag | safe }}
69
108
  {% endfor %}
70
109
  </tr>
71
110
  {% endfor %}
@@ -95,6 +95,36 @@ describe('macro: table', () => {
95
95
 
96
96
  expect($('.ons-table__header').attr('width')).toBe('50%');
97
97
  });
98
+ it('adds additional colspan attribute to column header', () => {
99
+ const $ = cheerio.load(
100
+ renderComponent('table', {
101
+ ...EXAMPLE_TABLE,
102
+ ths: [
103
+ {
104
+ value: 'Column 1',
105
+ colspan: 2,
106
+ },
107
+ ],
108
+ }),
109
+ );
110
+
111
+ expect($('.ons-table__header').attr('colspan')).toBe('2');
112
+ });
113
+ it('adds additional rowspan attribute to column header', () => {
114
+ const $ = cheerio.load(
115
+ renderComponent('table', {
116
+ ...EXAMPLE_TABLE,
117
+ ths: [
118
+ {
119
+ value: 'Column 1',
120
+ rowspan: 2,
121
+ },
122
+ ],
123
+ }),
124
+ );
125
+
126
+ expect($('.ons-table__header').attr('rowspan')).toBe('2');
127
+ });
98
128
 
99
129
  it('does not add "numeric" modifier class to column header when `td.numeric` is not provided', () => {
100
130
  const $ = cheerio.load(renderComponent('table', EXAMPLE_TABLE));
@@ -135,6 +165,28 @@ describe('macro: table', () => {
135
165
  });
136
166
  });
137
167
 
168
+ describe('Multiple Header Rows', () => {
169
+ it('renders expected header row cells', () => {
170
+ const $ = cheerio.load(
171
+ renderComponent('table', {
172
+ ...EXAMPLE_TABLE,
173
+ thList: [
174
+ {
175
+ ths: [{ value: 'Column 1' }, { value: 'Column 2' }],
176
+ },
177
+ {
178
+ ths: [{ value: 'Column A' }, { value: 'Column B' }],
179
+ },
180
+ ],
181
+ }),
182
+ );
183
+ const row1Values = mapAll($('.ons-table__row:nth-child(1) .ons-table__header'), (node) => node.text().trim());
184
+ expect(row1Values).toEqual(['Column 1', 'Column 2']);
185
+ const row2Values = mapAll($('.ons-table__row:nth-child(2) .ons-table__header'), (node) => node.text().trim());
186
+ expect(row2Values).toEqual(['Column A', 'Column B']);
187
+ });
188
+ });
189
+
138
190
  describe('row', () => {
139
191
  it('renders expected row cells', () => {
140
192
  const $ = cheerio.load(renderComponent('table', EXAMPLE_TABLE));
@@ -291,6 +343,44 @@ describe('macro: table', () => {
291
343
 
292
344
  expect($('.ons-table__cell').hasClass('ons-table__cell--numeric')).toBe(true);
293
345
  });
346
+ it('adds additional colspan attribute to row header', () => {
347
+ const $ = cheerio.load(
348
+ renderComponent('table', {
349
+ ...EXAMPLE_TABLE,
350
+ trs: [
351
+ {
352
+ tds: [
353
+ {
354
+ value: 'Column 1',
355
+ colspan: 2,
356
+ },
357
+ ],
358
+ },
359
+ ],
360
+ }),
361
+ );
362
+
363
+ expect($('.ons-table__cell').attr('colspan')).toBe('2');
364
+ });
365
+ it('adds additional rowspan attribute to row header', () => {
366
+ const $ = cheerio.load(
367
+ renderComponent('table', {
368
+ ...EXAMPLE_TABLE,
369
+ trs: [
370
+ {
371
+ tds: [
372
+ {
373
+ value: 'Column 1',
374
+ rowspan: 2,
375
+ },
376
+ ],
377
+ },
378
+ ],
379
+ }),
380
+ );
381
+
382
+ expect($('.ons-table__cell').attr('rowspan')).toBe('2');
383
+ });
294
384
 
295
385
  describe('form', () => {
296
386
  const params = {
@@ -13,10 +13,15 @@
13
13
  text-align: left;
14
14
  }
15
15
 
16
+ &__row:not(.ons-table__row--after-rowspan) {
17
+ .ons-table__cell,
18
+ .ons-table__header {
19
+ @include nth-element(1, 0);
20
+ }
21
+ }
22
+
16
23
  &__header,
17
24
  &__cell {
18
- @include nth-element(1, 0);
19
-
20
25
  border-bottom: 2px solid var(--ons-color-grey-75);
21
26
  overflow: hidden;
22
27
  padding: 0.5rem 0 0.5rem 1rem;
@@ -27,6 +32,13 @@
27
32
  }
28
33
  }
29
34
 
35
+ &--has-rowspan {
36
+ .ons-table__header,
37
+ .ons-table__cell {
38
+ padding: 0.5rem 1.5rem 0.5rem 0;
39
+ }
40
+ }
41
+
30
42
  &__cell,
31
43
  &__header--row {
32
44
  border-bottom: 1px solid var(--ons-color-borders);
@@ -36,7 +48,7 @@
36
48
  background: var(--ons-color-highlight);
37
49
  }
38
50
 
39
- &:not(.ons-table--responsive) .ons-table__body .ons-table__row:last-child {
51
+ &:not(.ons-table--responsive, .ons-table--has-rowspan) .ons-table__body .ons-table__row:last-child {
40
52
  .ons-table__cell,
41
53
  .ons-table__header--row {
42
54
  border: 0;
@@ -0,0 +1,139 @@
1
+ {% from "components/table/_macro.njk" import onsTable %}
2
+ {{
3
+ onsTable({
4
+ "caption": "Table with cells merged, multiple row headers and column headers",
5
+ "id": "basic-table",
6
+ "ths": [
7
+ {
8
+ "value": "Trade Category"
9
+ },
10
+ {
11
+ "value": "Metrics"
12
+ },
13
+ {
14
+ "value": "Exports"
15
+ },
16
+ {
17
+ "value": "Imports"
18
+ },
19
+ {
20
+ "value": "Balance"
21
+ }
22
+
23
+ ],
24
+ "trs": [
25
+ {
26
+ "tds": [
27
+ {
28
+ "heading": true,
29
+ "value": "Total trade in goods: December 2024 vs November 2024",
30
+ "rowspan":3
31
+ },
32
+ {
33
+ "heading": true,
34
+ "value": "Value (£bn)"
35
+ },
36
+ {
37
+ "value": "28.8"
38
+ },
39
+ {
40
+ "value": "45.7"
41
+ },
42
+ {
43
+ "value": "-16.9"
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ "tds": [
49
+ {
50
+ "heading": true,
51
+ "value": "Change (£bn)"
52
+ },
53
+ {
54
+ "value": "0.0"
55
+ },
56
+ {
57
+ "value": "-1.2"
58
+ },
59
+ {
60
+ "value": "1.2"
61
+ }
62
+ ]
63
+ },
64
+ {
65
+ "tds": [
66
+ {
67
+ "heading": true,
68
+ "value": "%Change"
69
+ },
70
+ {
71
+ "value": "0.0"
72
+ },
73
+ {
74
+ "value": "2.6"
75
+ },
76
+ {
77
+ "value": ""
78
+ }
79
+ ]
80
+ },
81
+ {
82
+ "tds": [
83
+ {
84
+ "heading": true,
85
+ "value": "EU: December 2024 vs November 2024",
86
+ "rowspan": 3
87
+ },
88
+ {
89
+ "value": "Value (£bn)",
90
+ "heading": true
91
+ },
92
+ {
93
+ "value": "13.6"
94
+ },
95
+ {
96
+ "value": "24.9"
97
+ },
98
+ {
99
+ "value": "-11.3"
100
+ }
101
+ ]
102
+ },
103
+ {
104
+ "tds": [
105
+ {
106
+ "value": "Change (£bn)",
107
+ "heading": true
108
+ },
109
+ {
110
+ "value": "-0.5"
111
+ },
112
+ {
113
+ "value": "-0.8"
114
+ },
115
+ {
116
+ "value": "0.3"
117
+ }
118
+ ]
119
+ },
120
+ {
121
+ "tds": [
122
+ {
123
+ "heading": true,
124
+ "value": "%Change"
125
+ },
126
+ {
127
+ "value": "3.5"
128
+ },
129
+ {
130
+ "value": "-1.8"
131
+ },
132
+ {
133
+ "value": ""
134
+ }
135
+ ]
136
+ }
137
+ ]
138
+ })
139
+ }}