Skip to content

Commit 3263a9a

Browse files
refactor(table): table cell header content
1 parent 5a37431 commit 3263a9a

File tree

5 files changed

+130
-127
lines changed

5 files changed

+130
-127
lines changed

docs/app/views/examples/components/table/_preview.html.erb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ sample_table = {
123123
"Revenue",
124124
"Status"
125125
],
126+
# responsive_stack: true,
126127
rows: [
127128
[
128129
%(
@@ -200,6 +201,7 @@ sample_table = {
200201
"Last activity",
201202
""
202203
],
204+
responsive_stack: true,
203205
rows: [
204206
[
205207
%(

docs/lib/sage_rails/app/helpers/sage_table_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ def sage_table_classes(table)
222222
table_classlist << " sage-table--condensed" if table.condensed
223223
table_classlist << " sage-table--has-leading-input" if table.has_leading_input
224224
table_classlist << " sage-table--has-menu-options" if table.has_menu_options
225+
table_classlist << " sage-table--stack" if table.responsive_stack
225226

226227
return table_classlist
227228
end

docs/lib/sage_rails/app/sage_components/sage_table.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class SageTable < SageComponent
1010
reset_above: [:optional, NilClass, TrueClass],
1111
reset_below: [:optional, NilClass, TrueClass],
1212
responsive: [:optional, NilClass, TrueClass],
13+
responsive_stack: [:optional, NilClass, TrueClass],
1314
rows: [:optional, NilClass, Array],
1415
selectable: [:optional, NilClass, TrueClass],
1516
})

packages/sage-assets/lib/stylesheets/components/_table.scss

Lines changed: 75 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,10 @@ $-table-avatar-width: rem(32px);
273273
max-width: $-table-cell-truncate-width;
274274
}
275275

276+
.sage-table-cell__heading--responsive {
277+
display: none;
278+
}
279+
276280
.sage-table-cell--checkbox {
277281
width: $-table-checkbox-width;
278282
}
@@ -350,18 +354,23 @@ $-table-avatar-width: rem(32px);
350354
}
351355

352356
@media screen and (max-width: sage-breakpoint(sm-max)) {
353-
.sage-table {
357+
.sage-table--stack {
354358
thead {
355359
tr,
356360
&::after {
357-
display: none;
361+
@include visually-hidden;
358362
}
359363
}
360364

361365
tbody tr {
362366
position: relative;
363367
}
364368

369+
.sage-table-cell__heading--responsive {
370+
display: inline-block;
371+
align-items: center;
372+
}
373+
365374
&.sage-table--has-menu-options {
366375
td:last-of-type {
367376
position: absolute;
@@ -373,121 +382,88 @@ $-table-avatar-width: rem(32px);
373382
td {
374383
padding: rem(6px) 0;
375384
}
376-
}
377385

378-
table,
379-
thead,
380-
tbody,
381-
th,
382-
td,
383-
tr,
384-
caption {
385-
display: block;
386-
}
387-
388-
tr {
389-
padding: rem(16px) rem(18px);
390-
border: sage-border(default);
391-
border-radius: sage-border(radius);
392-
393-
& + & {
394-
margin-top: sage-spacing(sm);
386+
// table,
387+
thead,
388+
tbody,
389+
th,
390+
td,
391+
tr,
392+
caption {
393+
display: block;
395394
}
396-
}
397-
398-
td {
399-
display: grid;
400-
grid-template-columns: auto auto;
401-
grid-gap: 1em 0.5em;
402-
justify-content: space-between;
403-
}
404-
405-
td:nth-of-type(4)::before,
406-
td:nth-of-type(5)::before {
407-
text-align: left;
408-
}
409-
410-
// TODO UXD - QUINTON - find scalable solution
411-
td::before {
412-
color: sage-color(charcoal, 200);
413-
}
414-
td:nth-child(1)::before {
415-
content: "Name ";
416-
display: none;
417-
}
418-
419-
td:nth-child(2)::before {
420-
content: "Email ";
421-
}
422-
423-
td:nth-child(3)::before {
424-
content: "Labels ";
425-
}
426-
427-
td:nth-child(4)::before {
428-
content: "Status ";
429-
}
430-
431-
td:nth-child(5)::before {
432-
content: "Status ";
433-
}
434-
435-
td:nth-child(6)::before {
436-
content: "Status ";
437-
}
438-
439-
td:nth-child(7)::before {
440-
content: "Status ";
441-
}
442395

443-
.sage-table--has-menu-options td:last-child::before {
444-
display: none;
445-
}
396+
tr {
397+
padding: rem(16px) rem(18px);
398+
border: sage-border(default);
399+
border-radius: sage-border(radius);
446400

447-
.sage-table--has-leading-input {
448-
td:nth-child(2) {
449-
margin-left: sage-spacing(sm);
401+
& + & {
402+
margin-top: sage-spacing(sm);
403+
}
450404
}
451405

452-
td:nth-child(-n+2) {
453-
display: inline-flex;
406+
td {
407+
display: grid;
408+
grid-template-columns: auto auto;
409+
grid-gap: 1em 0.5em;
410+
justify-content: space-between;
454411
}
455412

456-
td:nth-child(n+3) {
457-
margin-left: rem(40px);
458-
}
413+
// td:nth-of-type(4)::before,
414+
// td:nth-of-type(5)::before {
415+
// text-align: left;
416+
// }
459417

460-
td:nth-child(2)::before {
461-
content: "Name ";
418+
.sage-table--has-menu-options td:last-child::before {
462419
display: none;
463420
}
464421

465-
td:nth-child(3)::before {
466-
content: "Email ";
467-
}
422+
.sage-table--has-leading-input {
423+
td:nth-child(2) {
424+
margin-left: sage-spacing(sm);
425+
}
468426

469-
td:nth-child(4)::before {
470-
content: "Labels ";
471-
}
427+
td:nth-child(-n+2) {
428+
display: inline-flex;
429+
}
472430

473-
td:nth-child(5)::before {
474-
content: "Status ";
475-
}
431+
td:nth-child(n+3) {
432+
margin-left: rem(40px);
433+
}
476434

477-
td:nth-child(6)::before {
478-
content: "Status ";
479-
}
435+
td:nth-child(2)::before {
436+
content: "Name ";
437+
display: none;
438+
}
480439

481-
td:nth-child(7)::before {
482-
content: "Status ";
483-
}
440+
td:nth-child(3)::before {
441+
content: "Email ";
442+
}
484443

485-
td:nth-child(8)::before {
486-
content: "Status ";
487-
}
444+
td:nth-child(4)::before {
445+
content: "Labels ";
446+
}
488447

489-
td:last-child::before {
490-
display: none;
448+
td:nth-child(5)::before {
449+
content: "Status ";
450+
}
451+
452+
td:nth-child(6)::before {
453+
content: "Status ";
454+
}
455+
456+
td:nth-child(7)::before {
457+
content: "Status ";
458+
}
459+
460+
td:nth-child(8)::before {
461+
content: "Status ";
462+
}
463+
464+
td:last-child::before {
465+
display: none;
466+
}
491467
}
492468
}
493469
}

packages/sage-system/lib/table.js

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ Sage.table = (function() {
33
// Variables
44
// ==================================================
55

6-
// const SELECTOR_TABLE = "[data-js-table]";
7-
const SELECTOR_TABLE = ".sage-table";
6+
const RESPONSIVE_TABLE = ".sage-table--stack";
87
const MOBILE_TABLE_MAX_WIDTH = "767"; // SM_MAX
98

109
// ==================================================
@@ -31,31 +30,55 @@ Sage.table = (function() {
3130
});
3231
}
3332

34-
// TODO: Q: do this for each table
33+
// create alternative table headings for responsive "stacked" tables
3534
function ResponsiveCellHeaders(elem) {
36-
var THarray = [];
37-
var tables = document.querySelectorAll(SELECTOR_TABLE);
35+
const tables = document.querySelectorAll(RESPONSIVE_TABLE);
36+
37+
const cellHeaderTemplate = (textLabel) => {
38+
const cellHeader = document.createElement('span');
39+
40+
cellHeader.classList.add('sage-table-cell__heading--responsive');
41+
cellHeader.innerText = textLabel.trim(); // trim whitespace just in case
42+
43+
// content must be hidden from screen readers since it duplicates table headers
44+
cellHeader.setAttribute('aria-hidden', true);
45+
46+
return cellHeader;
47+
};
48+
3849
tables.forEach(table => {
39-
// var table = document.querySelector(SELECTOR_TABLE);
40-
var ths = table.getElementsByTagName("th");
41-
for (var i = 0; i < ths.length; i++) {
42-
var headingText = ths[i].innerHTML.trim();
43-
THarray.push(headingText);
44-
}
45-
var styleElm = document.createElement("style"),
46-
styleSheet;
47-
document.head.appendChild(styleElm);
48-
styleSheet = styleElm.sheet;
49-
for (var i = 0; i < THarray.length; i++) {
50-
styleSheet.insertRule(
51-
"" +
52-
elem +
53-
" td:nth-child(" +
54-
(i + 1) +
55-
')::before { content:"' + THarray[i] + ': ";}',
56-
styleSheet.cssRules.length
57-
);
58-
}
50+
const headers = table.querySelectorAll('thead th');
51+
const rows = table.querySelectorAll('tbody tr');
52+
const tableHeadings = [];
53+
54+
// populate an array with each table's headers
55+
headers.forEach(header => {
56+
const label = header.textContent.trim();
57+
tableHeadings.push(label);
58+
})
59+
60+
rows.forEach(row => {
61+
if (!row.hasChildNodes()) return; // skip empty rows
62+
63+
const cells = Array.from(row.children);
64+
65+
// add header text to each cell
66+
const updatedCells = cells.map((cell, i) => {
67+
if (!tableHeadings[i].length) return; // skip empty headers
68+
69+
const newHeader = cellHeaderTemplate(tableHeadings[i]);
70+
cell.prepend(newHeader);
71+
});
72+
73+
// look for cells with positioned content
74+
cells.forEach((cell) => {
75+
const checkboxes = cell.querySelectorAll(".sage-checkbox");
76+
77+
if (checkboxes.length) {
78+
cell.classList.add('sage-table-cell--checkbox');
79+
}
80+
})
81+
})
5982
})
6083
}
6184

@@ -68,7 +91,7 @@ Sage.table = (function() {
6891

6992
group.forEach(el => {
7093
el.setAttribute('role', args.role);
71-
})
94+
});
7295
}
7396

7497
function addTableAria() {
@@ -96,9 +119,9 @@ Sage.table = (function() {
96119
if (document.querySelector('.sage-table--sortable') !== null) {
97120
sortEvents();
98121
}
99-
if (document.querySelector(SELECTOR_TABLE) !== null && window.innerWidth <= MOBILE_TABLE_MAX_WIDTH) {
122+
if (document.querySelector(RESPONSIVE_TABLE) !== null && window.innerWidth <= MOBILE_TABLE_MAX_WIDTH) {
100123
addTableAria();
101-
ResponsiveCellHeaders(SELECTOR_TABLE);
124+
ResponsiveCellHeaders(RESPONSIVE_TABLE);
102125
}
103126
}
104127

0 commit comments

Comments
 (0)