diff --git a/CHANGELOG.md b/CHANGELOG.md index 243d404..5295f26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.13.2 — 2026-10-04 + +- **Forms show their labels and the privacy checkbox.** Bricks prints form labels only with "Show labels", and a checkbox's text only from its options. Every BricksSnap form left both out, so on the site the fields had no labels and the required privacy checkbox was missing. This affected contact, hero with form, sign-in and coming-soon layouts. Forms now set `showLabels`, and the checkbox carries its text as an option under a "Privacy" label. +- **Preview** renders forms the same way: no labels without `showLabels`, checkbox texts from the options, honeypot fields hidden. +- **Tests** check every form layout for labels and checkbox options. + ## 0.13.1 — 2026-10-01 Fixes found while rebuilding a real club website on a Bricks 2.4.2 site. See the [release notes](docs/RELEASE-0.13.1.md). diff --git a/package-lock.json b/package-lock.json index a8115dc..fa842c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "brickssnap", - "version": "0.13.1", + "version": "0.13.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "brickssnap", - "version": "0.13.1", + "version": "0.13.2", "license": "PolyForm-Shield-1.0.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.30.1", diff --git a/package.json b/package.json index 391f9e6..371f596 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "brickssnap", - "version": "0.13.1", + "version": "0.13.2", "scripts": { "dev": "next dev --webpack", "dev:local": "node scripts/dev-local.mjs", diff --git a/src/lib/kit/preview.ts b/src/lib/kit/preview.ts index 9230b70..1a99492 100644 --- a/src/lib/kit/preview.ts +++ b/src/lib/kit/preview.ts @@ -174,10 +174,12 @@ function formHtml(el: BricksElement): string { const s = el.settings; const fields = Array.isArray(s.fields) ? s.fields as Array> : []; const input = (f: Record) => { - const label = esc(String(f.label ?? "")); - if (f.type === "checkbox") return ``; + // Like Bricks: labels only with showLabels, honeypots hidden, checkbox texts from the options. + if (f.isHoneypot) return ""; + const label = s.showLabels ? `` : ""; + if (f.type === "checkbox") return `
${label}${String(f.options ?? "").split("\n").filter(Boolean).map(option => ``).join("")}
`; const control = f.type === "textarea" ? `` : ``; - return `
${control}
`; + return `
${label}${control}
`; }; return `
${fields.map(input).join("")}
`; } diff --git a/src/lib/kit/sections/more.ts b/src/lib/kit/sections/more.ts index d42420e..50d382a 100644 --- a/src/lib/kit/sections/more.ts +++ b/src/lib/kit/sections/more.ts @@ -120,8 +120,12 @@ function moreClasses(r: ResolvedKit) { export function form(ctx: Ctx, fields: Array<{ type: string; label: string; required?: boolean; width?: number }>, submit: string, actions: string[] = ["email"]): KitNode { const { x } = ctx; const fieldId = (i: number) => `f${String(i + 1).padStart(5, "0")}`; + // Bricks prints a checkbox's text from its options (one per line); the label becomes its heading. + const checkboxLabel = ctx.c.lang === "de" ? "Datenschutz" : "Privacy"; return node("form", "bs-form", { - fields: fields.map((f, i) => ({ id: fieldId(i), type: f.type, label: f.label, required: !!f.required, ...(f.width ? { width: f.width } : {}) })), + fields: fields.map((f, i) => ({ id: fieldId(i), type: f.type, ...(f.type === "checkbox" ? { label: checkboxLabel, options: f.label } : { label: f.label }), required: !!f.required, ...(f.width ? { width: f.width } : {}) })), + // Without it Bricks renders no labels at all. + showLabels: true, ...(actions.includes("login") ? { loginName: fieldId(0), loginPassword: fieldId(1) } : {}), submitButtonText: submit, actions, diff --git a/tests/kit.test.ts b/tests/kit.test.ts index 5ac7efe..e32df6d 100644 --- a/tests/kit.test.ts +++ b/tests/kit.test.ts @@ -423,3 +423,24 @@ describe("class CSS as Bricks 2.4 stores and renders it", () => { expect([...weak]).toEqual([]); }); }); + +describe("forms as Bricks renders them", () => { + it("show their labels and give checkboxes the options Bricks prints", () => { + const forms = LAYOUTS.flatMap(layout => generateKitTemplate({ kit: {}, profile: { industry: "kfz", language: "de" }, sections: [layout] }).template.content.filter(el => el.name === "form")); + expect(forms.length).toBeGreaterThanOrEqual(6); + for (const form of forms) { + expect(form.settings.showLabels).toBe(true); + for (const field of form.settings.fields as Array>) if (field.type === "checkbox") expect(String(field.options ?? "")).toMatch(/Datenschutzerklärung/); + } + }); + + it("previews labels only when Bricks would show them", () => { + const { template } = generateKitTemplate({ kit: {}, profile: { industry: "kfz", language: "de" }, sections: [{ type: "contact", variant: "split" }] }); + const html = renderPreview(template).html; + expect(html).toContain(""); + expect(html).toContain("Ich stimme der Verarbeitung meiner Daten gemäß Datenschutzerklärung zu."); + const form = template.content.find(el => el.name === "form")!; + const unlabeled = { ...template, content: template.content.map(el => (el === form ? { ...el, settings: { ...el.settings, showLabels: false } } : el)) }; + expect(renderPreview(unlabeled).html).not.toContain(""); + }); +});