mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
fix(ui): keep a wrapped label's colon on its last word
The label primitive was an inline flex box, so its ::after colon was a flex item of its own: when a long field name wrapped in a narrow column, the colon floated beside the whole text block instead of following the last word. The label now lays out inline and the colon wraps with the text. Call sites that set flex themselves keep their own layout.
This commit is contained in:
1 parent
537bad4212
commit
ce8074ad08
2 files changed
+48
-1
No files matched your search
@@ -0,0 +1,36 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
|
||||
import { Label } from "../label";
|
||||
|
||||
describe("<Label>", () => {
|
||||
it("lays out inline so the colon stays on the last word when it wraps", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<Label htmlFor="x">Body temperature</Label>,
|
||||
);
|
||||
expect(html).toContain("inline-block");
|
||||
// A flex box turns the ::after colon into a flex item of its own, which
|
||||
// floats beside a wrapped label instead of following its last word.
|
||||
expect(html).not.toMatch(/class="[^"]*\binline-flex\b/);
|
||||
expect(html).toContain("after:content-[':']");
|
||||
});
|
||||
|
||||
it("drops the colon when asked", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<Label htmlFor="x" noColon>
|
||||
Note
|
||||
</Label>,
|
||||
);
|
||||
expect(html).toContain('data-no-colon="true"');
|
||||
});
|
||||
|
||||
it("lets a call site take over the layout", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<Label htmlFor="x" className="flex items-baseline">
|
||||
Weight
|
||||
</Label>,
|
||||
);
|
||||
expect(html).toMatch(/class="[^"]*\bflex\b/);
|
||||
expect(html).not.toContain("inline-block");
|
||||
});
|
||||
});
|
||||
@@ -5,6 +5,17 @@ import { Label as LabelPrimitive } from "radix-ui";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
/**
|
||||
* Form label. The trailing colon is an `::after` on the label itself; pass
|
||||
* `noColon` where the label is not followed by its field.
|
||||
*
|
||||
* The label is `inline-block`, not `inline-flex`. In a flex box the colon is
|
||||
* a flex item of its own, so a label that wraps (a long German or French
|
||||
* field name in a narrow column) left the colon floating beside the whole
|
||||
* text block instead of after the last word. In inline flow the colon is
|
||||
* glued to the last word and wraps with it. A call site that lays out a name
|
||||
* beside a unit sets `flex` itself and owns the colon there.
|
||||
*/
|
||||
function Label({
|
||||
className,
|
||||
noColon = false,
|
||||
@@ -15,7 +26,7 @@ function Label({
|
||||
data-slot="label"
|
||||
data-no-colon={noColon}
|
||||
className={cn(
|
||||
"inline-flex items-center pl-1 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50 after:content-[':'] data-[no-colon=true]:after:content-none",
|
||||
"inline-block pl-1 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50 after:content-[':'] data-[no-colon=true]:after:content-none",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
Reference in new issue
Block a user