admin: ui polish pass, thirty findings from the emil audit
selected states stop changing font weight, buttons gain a pressed scale and scoped 120ms transitions with a reduced-motion override, every loading and empty state reserves its height, charts measure before first paint, hit targets rise to the 44px enhanced target where layout permits, long domains clamp to two lines on an unpadded inner span, chips and name cells truncate, tabular figures on counts and time columns, a z-index layer scale replaces magic numbers and fixes the dialog-over-confirm tie, history's empty state gains a clear-filters action, page headings balance, font smoothing and color-scheme land on the html reset.
This commit is contained in:
@@ -36,6 +36,7 @@ const styles = stylex.create({
|
||||
fontSize: "1.5rem",
|
||||
lineHeight: "2rem",
|
||||
fontWeight: 600,
|
||||
textWrap: "balance",
|
||||
},
|
||||
purgeAction: {
|
||||
marginInlineStart: "auto",
|
||||
@@ -107,6 +108,9 @@ const styles = stylex.create({
|
||||
color: colors.primaryOnSurface,
|
||||
},
|
||||
loading: {
|
||||
// The heading row, the subject and the seven-row facts panel, which is
|
||||
// what stands above the fold once the event lands.
|
||||
minHeight: "20rem",
|
||||
marginTop: "1rem",
|
||||
color: colors.textMuted,
|
||||
},
|
||||
|
||||
@@ -47,6 +47,15 @@ const RESOLVED = page([
|
||||
event(30, { code: "disk.space", component: "disk", subject: "data", resolved_at: NOW_S - 7200 }),
|
||||
]);
|
||||
|
||||
/**
|
||||
* The footer count sets its number in tabular figures, so the sentence is split
|
||||
* across elements. Matched on the paragraph's whole text rather than on a
|
||||
* fragment of it.
|
||||
*/
|
||||
function footerLine(text: string): HTMLElement {
|
||||
return screen.getByText((_content, element) => element?.tagName === "P" && element.textContent === text);
|
||||
}
|
||||
|
||||
/** A stubbed response that carries a non-200 status instead of a payload. */
|
||||
class Failure {
|
||||
constructor(
|
||||
@@ -153,7 +162,7 @@ test("active episodes come first, each with its title, subject, age and count",
|
||||
// The resolved history is a separate section, below the active list.
|
||||
const table = within(screen.getByRole("table"));
|
||||
expect(table.getByText("Disk space low")).toBeTruthy();
|
||||
expect(screen.getByText(/Showing 1 resolved entry — end of history/)).toBeTruthy();
|
||||
expect(footerLine("Showing 1 resolved entry — end of history")).toBeTruthy();
|
||||
});
|
||||
|
||||
test("nothing open reads as good news, not as a broken page", async () => {
|
||||
@@ -223,7 +232,7 @@ test("load more appends the next page of resolved history", async () => {
|
||||
fireEvent.click(screen.getByRole("button", { name: "Load more" }));
|
||||
|
||||
await screen.findByText("TLS certificate reload failed");
|
||||
expect(screen.getByText(/Showing 2 resolved entries — end of history/)).toBeTruthy();
|
||||
expect(footerLine("Showing 2 resolved entries — end of history")).toBeTruthy();
|
||||
});
|
||||
|
||||
test("an unavailable store reports the failure instead of loading forever", async () => {
|
||||
|
||||
@@ -46,6 +46,7 @@ const styles = stylex.create({
|
||||
fontSize: "1.5rem",
|
||||
lineHeight: "2rem",
|
||||
fontWeight: 600,
|
||||
textWrap: "balance",
|
||||
},
|
||||
intro: {
|
||||
marginTop: "0.25rem",
|
||||
@@ -94,6 +95,18 @@ const styles = stylex.create({
|
||||
lineHeight: "1.25rem",
|
||||
color: colors.textMuted,
|
||||
},
|
||||
/** One card: the badge and title row, the meta line, and the card's padding. */
|
||||
activeLoading: {
|
||||
minHeight: "4rem",
|
||||
},
|
||||
/**
|
||||
* The table header and three rows, at 2.25rem each. Deliberately short of a
|
||||
* full page of history: an install with nothing resolved collapses to one
|
||||
* muted line, and a taller reserve would leave a hole on the common case.
|
||||
*/
|
||||
historyLoading: {
|
||||
minHeight: "9rem",
|
||||
},
|
||||
cardList: {
|
||||
marginTop: "0.75rem",
|
||||
display: "flex",
|
||||
@@ -339,8 +352,8 @@ function HistoryRow({ event, onPurge, busy }: { event: DiagnosticEvent; onPurge:
|
||||
</Link>
|
||||
</td>
|
||||
<td {...stylex.props(styles.cell)}>{event.subject}</td>
|
||||
<td {...stylex.props(styles.cell, styles.nowrap)}>{formatTime(event.first_seen)}</td>
|
||||
<td {...stylex.props(styles.cell, styles.nowrap)}>
|
||||
<td {...stylex.props(styles.cell, styles.nowrap, shared.tabularNums)}>{formatTime(event.first_seen)}</td>
|
||||
<td {...stylex.props(styles.cell, styles.nowrap, shared.tabularNums)}>
|
||||
{event.resolved_at === null ? "—" : formatTime(event.resolved_at)}
|
||||
</td>
|
||||
<td {...stylex.props(styles.cell, styles.nowrap, shared.tabularNums)}>{event.occurrences}</td>
|
||||
@@ -436,7 +449,7 @@ export default function DiagnosticsPage() {
|
||||
{active.status === "error" ? (
|
||||
<InlineError error={active.error} onRetry={() => void active.refetch()} />
|
||||
) : active.data === undefined ? (
|
||||
<p {...stylex.props(styles.empty, shared.pulse)} role="status">
|
||||
<p {...stylex.props(styles.empty, styles.activeLoading, shared.pulse)} role="status">
|
||||
Loading diagnostics…
|
||||
</p>
|
||||
) : activeRows.length === 0 ? (
|
||||
@@ -474,7 +487,7 @@ export default function DiagnosticsPage() {
|
||||
{history.status === "error" ? (
|
||||
<InlineError error={history.error} onRetry={() => void history.refetch()} />
|
||||
) : history.data === undefined ? (
|
||||
<p {...stylex.props(styles.empty, shared.pulse)} role="status">
|
||||
<p {...stylex.props(styles.empty, styles.historyLoading, shared.pulse)} role="status">
|
||||
Loading history…
|
||||
</p>
|
||||
) : historyRows.length === 0 ? (
|
||||
@@ -509,7 +522,8 @@ export default function DiagnosticsPage() {
|
||||
</table>
|
||||
</div>
|
||||
<p {...stylex.props(styles.footer, styles.note)}>
|
||||
Showing {historyRows.length} resolved {historyRows.length === 1 ? "entry" : "entries"}
|
||||
Showing <span {...stylex.props(shared.tabularNums)}>{historyRows.length}</span> resolved{" "}
|
||||
{historyRows.length === 1 ? "entry" : "entries"}
|
||||
{hasMore(history) ? "" : " — end of history"}
|
||||
</p>
|
||||
<MoreButton section={history} />
|
||||
|
||||
@@ -109,6 +109,14 @@ const styles = stylex.create({
|
||||
lineHeight: "1.25rem",
|
||||
color: colors.textMuted,
|
||||
},
|
||||
/**
|
||||
* One row of facts — a fact's own line, its detail line, and the padding —
|
||||
* plus the extra top margin the list carries. A floor, not a match: below
|
||||
* 1100px the strip stacks and grows past it.
|
||||
*/
|
||||
loading: {
|
||||
minHeight: "3.5rem",
|
||||
},
|
||||
});
|
||||
|
||||
const TONES = { ok: styles.ok, notice: styles.notice, warn: styles.warn, danger: styles.danger } as const;
|
||||
@@ -182,7 +190,7 @@ export default function HealthStrip() {
|
||||
return health.isError ? (
|
||||
<InlineError error={health.error} onRetry={() => void health.refetch()} />
|
||||
) : (
|
||||
<p role="status" {...stylex.props(styles.message, shared.pulse)}>
|
||||
<p role="status" {...stylex.props(styles.message, styles.loading, shared.pulse)}>
|
||||
Loading status…
|
||||
</p>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user