admin: phosphor icons replace hand-drawn svgs and text-character glyphs
Release / guard (push) Successful in 39s
Gates / frontend (push) Failing after 2m25s
Gates / package (push) Skipped
Gates / container (push) Skipped
Gates / frontend (push) Failing after 1m59s
Gates / package (push) Skipped
Gates / container (push) Skipped
Gates / test-aarch64 (push) Successful in 8m18s
CI / gates (push) Failing after 11m34s
Gates / test (push) Successful in 2m18s
Gates / test-aarch64 (push) Successful in 7m23s
Release / gates (push) Failing after 18m51s
Release / publish (push) Skipped
Gates / test (push) Successful in 2m36s
Release / guard (push) Successful in 39s
Gates / frontend (push) Failing after 2m25s
Gates / package (push) Skipped
Gates / container (push) Skipped
Gates / frontend (push) Failing after 1m59s
Gates / package (push) Skipped
Gates / container (push) Skipped
Gates / test-aarch64 (push) Successful in 8m18s
CI / gates (push) Failing after 11m34s
Gates / test (push) Successful in 2m18s
Gates / test-aarch64 (push) Successful in 7m23s
Release / gates (push) Failing after 18m51s
Release / publish (push) Skipped
Gates / test (push) Successful in 2m36s
health strip marks, menu ticks, chip crosses, the search magnifier, the checkbox check and the back arrows all come from @phosphor-icons/react now, pinned exactly and entered in the license ledger. changelog and version bump for 0.0.15, including the bundle budget raise to 900000 bytes.
This commit is contained in:
@@ -252,7 +252,7 @@ test("the back link restores the investigation the reader came from", async () =
|
||||
renderDetail(19, "?mode=history&domain=shop&since=1600000000&blocked=true");
|
||||
|
||||
await screen.findByRole("heading", { name: "shop.example" });
|
||||
expect(hrefSearch(screen.getByRole("link", { name: "← Activity" }))).toEqual({
|
||||
expect(hrefSearch(within(screen.getByRole("main")).getByRole("link", { name: "Activity" }))).toEqual({
|
||||
mode: "history",
|
||||
domain: "shop",
|
||||
since: "1600000000",
|
||||
@@ -307,7 +307,7 @@ test("a row retention has pruned explains the 404 and keeps the way back to the
|
||||
|
||||
await screen.findByRole("alert");
|
||||
expect(screen.getByText(/no such query/)).toBeTruthy();
|
||||
expect(hrefSearch(screen.getByRole("link", { name: "← Activity" }))).toEqual({
|
||||
expect(hrefSearch(within(screen.getByRole("main")).getByRole("link", { name: "Activity" }))).toEqual({
|
||||
mode: "history",
|
||||
domain: "gone",
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ArrowLeft } from "@phosphor-icons/react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Link, useParams, useSearch } from "@tanstack/react-router";
|
||||
import * as stylex from "@stylexjs/stylex";
|
||||
@@ -12,11 +13,17 @@ import type { ActivitySearch } from "./search";
|
||||
|
||||
const styles = stylex.create({
|
||||
back: {
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: "0.25rem",
|
||||
fontSize: "0.875rem",
|
||||
lineHeight: "1.25rem",
|
||||
color: colors.primaryOnSurface,
|
||||
textDecorationLine: "none",
|
||||
},
|
||||
backIcon: {
|
||||
display: "inline-flex",
|
||||
},
|
||||
loading: {
|
||||
marginTop: "1rem",
|
||||
color: colors.textMuted,
|
||||
@@ -32,7 +39,10 @@ const styles = stylex.create({
|
||||
function BackLink({ origin }: { origin: ActivitySearch }) {
|
||||
return (
|
||||
<Link to="/activity" search={origin} {...stylex.props(styles.back, shared.focusRing)}>
|
||||
← Activity
|
||||
<span aria-hidden="true" {...stylex.props(styles.backIcon)}>
|
||||
<ArrowLeft size={12} />
|
||||
</span>
|
||||
Activity
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ function clientChips(): string[] {
|
||||
return within(screen.getByRole("group", { name: "Clients" }))
|
||||
.getAllByRole("button")
|
||||
.slice(1)
|
||||
.map((chip) => chip.textContent?.replace("\u00d7", "") ?? "");
|
||||
.map((chip) => chip.textContent ?? "");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,7 +368,7 @@ test("the domain field is search-shaped, unspellchecked, and labelled without a
|
||||
expect(domain.getAttribute("autocomplete")).toBe("off");
|
||||
expect(domain.getAttribute("placeholder")).toBe("Filter domains…");
|
||||
// The magnifier is decoration over the field, never a second thing to read.
|
||||
expect(document.querySelector("svg[aria-hidden='true']")).toBeTruthy();
|
||||
expect(domain.parentElement?.querySelector("[aria-hidden='true'] svg")).toBeTruthy();
|
||||
});
|
||||
|
||||
test("a bound change does not reset the domain draft that is still being typed", () => {
|
||||
@@ -553,7 +553,7 @@ test("the menu ticks the clients the url names, not the ones last picked here",
|
||||
const ticked = screen
|
||||
.getAllByRole("menuitemcheckbox")
|
||||
.filter((item) => item.getAttribute("aria-checked") === "true");
|
||||
expect(ticked.map((item) => item.textContent?.replace("✓", ""))).toEqual(["laptop.lan — 192.0.2.11"]);
|
||||
expect(ticked.map((item) => item.textContent)).toEqual(["laptop.lan — 192.0.2.11"]);
|
||||
});
|
||||
|
||||
test("a rename moves the label and leaves the filter on the address", async () => {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
* would promise filtering that is not happening.
|
||||
*/
|
||||
|
||||
import { MagnifyingGlass } from "@phosphor-icons/react";
|
||||
import { useCallback, useEffect, useState, type FormEvent, type KeyboardEvent } from "react";
|
||||
import * as stylex from "@stylexjs/stylex";
|
||||
import { Button, Menu, MenuItem, MenuTrigger, Popover, Radio, RadioGroup } from "react-aria-components";
|
||||
@@ -76,6 +77,7 @@ const styles = stylex.create({
|
||||
display: "flex",
|
||||
},
|
||||
searchIcon: {
|
||||
display: "inline-flex",
|
||||
position: "absolute",
|
||||
insetInlineStart: "0.5rem",
|
||||
top: "50%",
|
||||
@@ -502,19 +504,9 @@ export default function ActivityFilters({ applied, onApply, onClear }: Props) {
|
||||
<form onSubmit={flush}>
|
||||
<div {...stylex.props(styles.toolbar)}>
|
||||
<div {...stylex.props(styles.searchWrap)}>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
viewBox="0 0 16 16"
|
||||
width="14"
|
||||
height="14"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
{...stylex.props(styles.searchIcon)}
|
||||
>
|
||||
<circle cx="7" cy="7" r="4.5" />
|
||||
<path d="M10.5 10.5 14 14" strokeLinecap="round" />
|
||||
</svg>
|
||||
<span aria-hidden="true" {...stylex.props(styles.searchIcon)}>
|
||||
<MagnifyingGlass size={14} />
|
||||
</span>
|
||||
<input
|
||||
type="search"
|
||||
aria-label="Filter domains"
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* visible and removable either way.
|
||||
*/
|
||||
|
||||
import { CaretDown } from "@phosphor-icons/react";
|
||||
import { CaretDown, Check, X } from "@phosphor-icons/react";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import * as stylex from "@stylexjs/stylex";
|
||||
import { Button, Menu, MenuItem, MenuTrigger, Popover } from "react-aria-components";
|
||||
@@ -113,6 +113,7 @@ const styles = stylex.create({
|
||||
tick: {
|
||||
width: "0.75rem",
|
||||
flexShrink: 0,
|
||||
display: "inline-flex",
|
||||
},
|
||||
chip: {
|
||||
cursor: "pointer",
|
||||
@@ -131,6 +132,7 @@ const styles = stylex.create({
|
||||
lineHeight: "1.25rem",
|
||||
},
|
||||
chipCross: {
|
||||
display: "inline-flex",
|
||||
color: colors.textMuted,
|
||||
},
|
||||
/** Not a button: it removes nothing, and nothing about it is pressable. */
|
||||
@@ -329,7 +331,7 @@ export default function ClientFilter({ options, selected, onChange }: Props) {
|
||||
}
|
||||
>
|
||||
<span aria-hidden="true" {...stylex.props(styles.tick)}>
|
||||
{chosen.has(option.ip) ? "✓" : ""}
|
||||
{chosen.has(option.ip) && <Check size={12} />}
|
||||
</span>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
@@ -353,7 +355,7 @@ export default function ClientFilter({ options, selected, onChange }: Props) {
|
||||
>
|
||||
{chipFor(ip, options)}
|
||||
<span aria-hidden="true" {...stylex.props(styles.chipCross)}>
|
||||
×
|
||||
<X size={10} />
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
@@ -48,7 +48,7 @@ test("an id the list does not contain renders the missing-client state (D9)", as
|
||||
|
||||
await screen.findByRole("heading", { name: "No such client" });
|
||||
expect(screen.getByText(/no client with id 99/i)).toBeTruthy();
|
||||
expect(screen.getByRole("link", { name: "← All clients" })).toBeTruthy();
|
||||
expect(screen.getByRole("link", { name: "All clients" })).toBeTruthy();
|
||||
});
|
||||
|
||||
test("policy links to the group that filters this client", async () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { ArrowLeft } from "@phosphor-icons/react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Link, useParams } from "@tanstack/react-router";
|
||||
import * as stylex from "@stylexjs/stylex";
|
||||
@@ -17,11 +18,17 @@ const nowInSeconds = () => Math.floor(Date.now() / 1000);
|
||||
|
||||
const styles = stylex.create({
|
||||
back: {
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: "0.25rem",
|
||||
fontSize: "0.875rem",
|
||||
lineHeight: "1.25rem",
|
||||
color: colors.primaryOnSurface,
|
||||
textDecorationLine: "none",
|
||||
},
|
||||
backIcon: {
|
||||
display: "inline-flex",
|
||||
},
|
||||
heading: {
|
||||
marginTop: "0.5rem",
|
||||
fontSize: "1.5rem",
|
||||
@@ -95,7 +102,10 @@ const styles = stylex.create({
|
||||
function BackLink() {
|
||||
return (
|
||||
<Link to="/clients" search={{}} {...stylex.props(styles.back, shared.focusRing)}>
|
||||
← All clients
|
||||
<span aria-hidden="true" {...stylex.props(styles.backIcon)}>
|
||||
<ArrowLeft size={12} />
|
||||
</span>
|
||||
All clients
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ test("every code renders its own title, impact and remediation", async () => {
|
||||
test("an event retention has removed shows the server's message, not an empty page", async () => {
|
||||
renderDetail(999);
|
||||
await screen.findByText("no such event");
|
||||
expect(screen.getByRole("link", { name: "← All diagnostics" })).toBeTruthy();
|
||||
expect(screen.getByRole("link", { name: "All diagnostics" })).toBeTruthy();
|
||||
});
|
||||
|
||||
test("an unavailable store reports the failure instead of loading forever", async () => {
|
||||
@@ -219,5 +219,5 @@ test("an unavailable store reports the failure instead of loading forever", asyn
|
||||
const alert = await screen.findByRole("alert");
|
||||
expect(alert.textContent).toContain("The server is starting or degraded.");
|
||||
expect(screen.queryByText("Loading event…")).toBeNull();
|
||||
expect(screen.getByRole("link", { name: "← All diagnostics" })).toBeTruthy();
|
||||
expect(screen.getByRole("link", { name: "All diagnostics" })).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { ArrowLeft } from "@phosphor-icons/react";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { Link, useNavigate, useParams } from "@tanstack/react-router";
|
||||
import * as stylex from "@stylexjs/stylex";
|
||||
@@ -13,11 +14,17 @@ import { componentLabel, copyFor } from "./eventCopy";
|
||||
|
||||
const styles = stylex.create({
|
||||
back: {
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: "0.25rem",
|
||||
fontSize: "0.875rem",
|
||||
lineHeight: "1.25rem",
|
||||
color: colors.primaryOnSurface,
|
||||
textDecorationLine: "none",
|
||||
},
|
||||
backIcon: {
|
||||
display: "inline-flex",
|
||||
},
|
||||
headingRow: {
|
||||
marginTop: "0.5rem",
|
||||
display: "flex",
|
||||
@@ -105,6 +112,17 @@ const styles = stylex.create({
|
||||
},
|
||||
});
|
||||
|
||||
function BackLink() {
|
||||
return (
|
||||
<Link to="/diagnostics" {...stylex.props(styles.back, shared.focusRing)}>
|
||||
<span aria-hidden="true" {...stylex.props(styles.backIcon)}>
|
||||
<ArrowLeft size={12} />
|
||||
</span>
|
||||
All diagnostics
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export default function DiagnosticDetailPage() {
|
||||
const { id } = useParams({ from: "/shell/diagnostics/$id" });
|
||||
const eventId = Number(id);
|
||||
@@ -132,9 +150,7 @@ export default function DiagnosticDetailPage() {
|
||||
if (data === undefined) {
|
||||
return (
|
||||
<section>
|
||||
<Link to="/diagnostics" {...stylex.props(styles.back, shared.focusRing)}>
|
||||
← All diagnostics
|
||||
</Link>
|
||||
<BackLink />
|
||||
<InlineError error={error} onRetry={() => void refetch()} />
|
||||
</section>
|
||||
);
|
||||
@@ -146,9 +162,7 @@ export default function DiagnosticDetailPage() {
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Link to="/diagnostics" {...stylex.props(styles.back, shared.focusRing)}>
|
||||
← All diagnostics
|
||||
</Link>
|
||||
<BackLink />
|
||||
<div {...stylex.props(styles.headingRow)}>
|
||||
<h1 {...stylex.props(styles.heading)}>{copy.title}</h1>
|
||||
<SeverityBadge severity={data.severity} />
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
* a claim about the current state, until a poll succeeds again.
|
||||
*/
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { Circle, Pause, Warning, X } from "@phosphor-icons/react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
import * as stylex from "@stylexjs/stylex";
|
||||
@@ -95,6 +97,9 @@ const styles = stylex.create({
|
||||
danger: {
|
||||
color: colors.dangerText,
|
||||
},
|
||||
icon: {
|
||||
display: "inline-flex",
|
||||
},
|
||||
message: {
|
||||
marginTop: "0.5rem",
|
||||
fontSize: "0.875rem",
|
||||
@@ -106,7 +111,12 @@ const styles = stylex.create({
|
||||
const TONES = { ok: styles.ok, notice: styles.notice, warn: styles.warn, danger: styles.danger } as const;
|
||||
|
||||
/** Text and icon carry the state; the colour only agrees with them. */
|
||||
const ICONS: Record<FactTone, string> = { ok: "●", notice: "‖", warn: "!", danger: "✕" };
|
||||
const ICONS: Record<FactTone, ReactNode> = {
|
||||
ok: <Circle size={12} weight="fill" />,
|
||||
notice: <Pause size={12} />,
|
||||
warn: <Warning size={12} />,
|
||||
danger: <X size={12} />,
|
||||
};
|
||||
|
||||
function FactLinkAnchor({ link }: { link: FactLink }) {
|
||||
if (link.kind === "filter") {
|
||||
@@ -151,7 +161,7 @@ function FactLinkAnchor({ link }: { link: FactLink }) {
|
||||
function Fact({ fact }: { fact: HealthFact }) {
|
||||
return (
|
||||
<li {...stylex.props(styles.fact, fact.tone === "ok" ? styles.quiet : styles.highlighted)}>
|
||||
<span aria-hidden="true" {...stylex.props(TONES[fact.tone])}>
|
||||
<span aria-hidden="true" {...stylex.props(styles.icon, TONES[fact.tone])}>
|
||||
{ICONS[fact.tone]}
|
||||
</span>
|
||||
<span {...stylex.props(styles.label)}>{fact.label}</span>
|
||||
|
||||
Reference in New Issue
Block a user