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

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:
2026-08-31 18:04:25 +02:00
parent b774b05456
commit 2525b01893
19 changed files with 123 additions and 53 deletions
@@ -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"
+5 -3
View File
@@ -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>
))}