Sidebar: soon rows and the project row keep their styles inside a hint; tiny amounts read as zero
Radix asChild merges className as a string, so a NavLink's className function was lost inside Hint: Insights, Boards, Roadmap and Observability rendered bold and bright, and the project row lost its highlight. Hint now puts a plain box around a child whose className is a function, and the two sidebar rows have one. money() printed four zero decimals for a few millionths of a dollar; under a hundredth of a cent it now reads as zero dollars to the cent.
4 files+20−10/4 viewed
| 175 | 175 | }) { | |
| 176 | 176 | const { pathname } = useLocation(); | |
| 177 | 177 | return ( | |
| 178 | + | // The hint wraps a plain box: Radix's asChild merges className as a | |
| 179 | + | // string, which would break NavLink's className function. | |
| 178 | 180 | <Hint label={about} side="right"> | |
| 181 | + | <div> | |
| 179 | 182 | <NavLink | |
| 180 | 183 | to={to} | |
| 181 | 184 | prefetch="intent" | |
| 190 | 193 | <span className="min-w-0 grow truncate">{children}</span> | |
| 191 | 194 | <SoonPill /> | |
| 192 | 195 | </NavLink> | |
| 196 | + | </div> | |
| 193 | 197 | </Hint> | |
| 194 | 198 | ); | |
| 195 | 199 | } | |
| 945 | 949 | <nav aria-label={`${repo.namespace}/${repo.name}`} className={PANEL}> | |
| 946 | 950 | <BackRow to={back.to} label={back.label} /> | |
| 947 | 951 | <Hint label="Overview" side="right"> | |
| 952 | + | <div> | |
| 948 | 953 | <NavLink | |
| 949 | 954 | to={base} | |
| 950 | 955 | end | |
| 961 | 966 | <span className="font-semibold text-fg">{repo.name}</span> | |
| 962 | 967 | </span> | |
| 963 | 968 | </NavLink> | |
| 969 | + | </div> | |
| 964 | 970 | </Hint> | |
| 965 | 971 | {/* A project's pages, in the order people use them. A page with more | |
| 966 | 972 | than one view shows them as tabs across its top. */} |
| 37 | 37 | const [open, setOpen] = useState(false); | |
| 38 | 38 | const touch = useRef(false); | |
| 39 | 39 | if (label == null || label === false || label === "") return children; | |
| 40 | + | // asChild merges className as a string, so a child whose className is a | |
| 41 | + | // function (NavLink's) would lose its classes: it gets a plain box instead. | |
| 42 | + | const functionClass = typeof (children.props as { className?: unknown }).className === "function"; | |
| 40 | 43 | const trigger = disabled ? ( | |
| 41 | 44 | <span tabIndex={0} className="inline-flex rounded-md outline-none focus-visible:ring-2 focus-visible:ring-accent *:pointer-events-none"> | |
| 42 | 45 | {children} | |
| 43 | 46 | </span> | |
| 47 | + | ) : functionClass ? ( | |
| 48 | + | <div>{children}</div> | |
| 44 | 49 | ) : ( | |
| 45 | 50 | children | |
| 46 | 51 | ); |
| 92 | 92 | assert.ok(!isStaff({ workspaces: [{ slug: "acme" }] })); | |
| 93 | 93 | assert.ok(!isStaff(null)); | |
| 94 | 94 | }); | |
| 95 | + | ||
| 96 | + | test("money under a hundredth of a cent reads $0.00, never $0.0000", () => { | |
| 97 | + | assert.equal(money(3), "$0.00"); | |
| 98 | + | assert.equal(money(0), "$0.00"); | |
| 99 | + | assert.equal(money(4_000), "$0.004"); | |
| 100 | + | assert.equal(money(250), "$0.0003"); | |
| 101 | + | }); |
| 172 | 172 | export function money(micros: number): string { | |
| 173 | 173 | const sign = micros < 0 ? "−" : ""; | |
| 174 | 174 | const d = Math.abs(micros) / MICROS_PER_DOLLAR; | |
| 175 | − | const digits = d === 0 || d >= 0.01 ? 2 : d >= 0.001 ? 3 : 4; | |
| 175 | + | // Under a hundredth of a cent there is nothing worth saying: "$0.00". | |
| 176 | + | const digits = d < 0.0001 || d >= 0.01 ? 2 : d >= 0.001 ? 3 : 4; | |
| 176 | 177 | return `${sign}$${d.toLocaleString("en-US", { minimumFractionDigits: digits, maximumFractionDigits: digits })}`; | |
| 177 | 178 | } | |
| 178 | 179 |