fix(ui): stack DataTable rows into cards on phones for Servers, Fleet proxy, Incidents, Jobs

This commit is contained in:
Juan Denis committed 2026-11-03 13:10:03 -05:00
1 parent eeb2792d06
commit 33febcaf1b
6 files changed
+68 -2

No files matched your search

+9 -2
View File
@@ -105,6 +105,9 @@ export function DataTable({
className,
rowClassName,
tableClassName,
// Below 640px each row becomes a card: first column is the title, the rest
// are label/value pairs (CSS-only, see .sk-dtable-wrap--stack).
stackOnMobile = false,
}) {
const { t } = useTranslation();
const internal = useTableSort({
@@ -365,10 +368,14 @@ export function DataTable({
/>
</TableCell>
)}
{visibleColumns.map((column) => (
{visibleColumns.map((column, columnIndex) => (
<TableCell
key={`${key}-${column.key}`}
className={column.cellClassName}
data-label={stackOnMobile && typeof column.header === 'string'
? translateLabel(t, column, 'header') || undefined
: undefined}
data-primary={stackOnMobile && columnIndex === 0 ? '' : undefined}
>
{column.render
? column.render(row)
@@ -380,7 +387,7 @@ export function DataTable({
};
return (
<div className={cn('sk-dtable-wrap', className)} ref={wrapRef}>
<div className={cn('sk-dtable-wrap', stackOnMobile && 'sk-dtable-wrap--stack', className)} ref={wrapRef}>
<Table className={cn('sk-dtable', tableClassName)}>
<TableHeader>
<TableRow>
+1
View File
@@ -400,6 +400,7 @@ const FleetProxy = () => {
<div className="fleet-proxy__table">
<DataTable
stackOnMobile
loading={loading}
columns={chrome.columns}
data={rows}
+1
View File
@@ -533,6 +533,7 @@ export default function Incidents() {
/>
) : (
<DataTable
stackOnMobile
{...chrome.tableProps}
columns={chrome.columns}
data={shown}
+2
View File
@@ -482,6 +482,7 @@ export default function Jobs() {
/>
) : scheduledView ? (
<DataTable
stackOnMobile
columns={scheduledColumns}
data={scheduled}
keyField="id"
@@ -531,6 +532,7 @@ export default function Jobs() {
/>
) : (
<DataTable
stackOnMobile
columns={chrome.columns}
data={jobs}
keyField="id"
+1
View File
@@ -447,6 +447,7 @@ const Servers = () => {
/>
) : (
<DataTable
stackOnMobile
columns={chrome.columns}
data={filteredServers}
keyField="id"
@@ -510,6 +510,60 @@ $sk-tones: ('accent', 'green', 'cyan', 'amber', 'red', 'violet');
border-radius: $radius-card;
}
// Opt-in (DataTable `stackOnMobile`): phones get one card per row instead of a
// sideways-scrolling grid. Header is hidden; each cell is "label value", the
// first column is the title and an unlabelled cell (actions) sits on the right.
@media (max-width: $breakpoint-sm - 1px) {
.sk-dtable-wrap--stack {
overflow-x: visible;
.sk-dtable,
.sk-dtable tbody { display: block; }
.sk-dtable thead { display: none; }
.sk-dtable tbody tr {
display: flex;
flex-direction: column;
gap: $space-1;
padding: $space-3 $space-4;
border-bottom: 1px solid var(--border-soft);
}
.sk-dtable tbody tr:last-child { border-bottom: none; }
.sk-dtable tbody td,
.sk-dtable tbody td:first-child,
.sk-dtable tbody td:last-child {
display: flex;
align-items: center;
justify-content: space-between;
gap: $space-3;
height: auto;
min-width: 0;
padding: 0;
border: 0;
text-align: right;
}
.sk-dtable tbody td[data-label]::before {
content: attr(data-label);
flex-shrink: 0;
font-size: $text-xs;
color: var(--text-faint);
}
.sk-dtable tbody td:not([data-label]) { justify-content: flex-end; }
.sk-dtable tbody td[data-primary] { justify-content: flex-start; text-align: left; font-size: $text-md; }
.sk-dtable tbody td[data-primary]::before { content: none; }
.sk-dtable tbody td:empty { display: none; }
// A cell that is only a "open" chevron rides the card's top-right corner
// instead of costing a row of its own.
.sk-dtable tbody tr { position: relative; }
.sk-dtable tbody td:has(> svg:only-child) {
position: absolute;
inset-block-start: $space-3;
inset-inline-end: $space-4;
}
}
}
// A table that is one section of a titled card/panel runs flush inside it:
// two frames a pixel apart is the seam this rule removes. A page-specific
// panel that frames its own header + table opts in with --flush.