fix(html): make fluent builders nil-safe
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-03 19:34:23 +00:00
parent 3c64352a3b
commit 9def509187
4 changed files with 45 additions and 0 deletions

View file

@ -80,6 +80,9 @@ func ValidateLayoutVariant(variant string) error {
// layout.go: H appends nodes to the Header slot.
// Example: NewLayout("HCF").H(Raw("head")).
func (l *Layout) H(nodes ...Node) *Layout {
if l == nil {
return nil
}
l.slots['H'] = append(l.slots['H'], nodes...)
return l
}
@ -87,6 +90,9 @@ func (l *Layout) H(nodes ...Node) *Layout {
// layout.go: L appends nodes to the Left aside slot.
// Example: NewLayout("HLCRF").L(Raw("nav")).
func (l *Layout) L(nodes ...Node) *Layout {
if l == nil {
return nil
}
l.slots['L'] = append(l.slots['L'], nodes...)
return l
}
@ -94,6 +100,9 @@ func (l *Layout) L(nodes ...Node) *Layout {
// layout.go: C appends nodes to the Content (main) slot.
// Example: NewLayout("C").C(Raw("body")).
func (l *Layout) C(nodes ...Node) *Layout {
if l == nil {
return nil
}
l.slots['C'] = append(l.slots['C'], nodes...)
return l
}
@ -101,6 +110,9 @@ func (l *Layout) C(nodes ...Node) *Layout {
// layout.go: R appends nodes to the Right aside slot.
// Example: NewLayout("HLCRF").R(Raw("aside")).
func (l *Layout) R(nodes ...Node) *Layout {
if l == nil {
return nil
}
l.slots['R'] = append(l.slots['R'], nodes...)
return l
}
@ -108,6 +120,9 @@ func (l *Layout) R(nodes ...Node) *Layout {
// layout.go: F appends nodes to the Footer slot.
// Example: NewLayout("HCF").F(Raw("foot")).
func (l *Layout) F(nodes ...Node) *Layout {
if l == nil {
return nil
}
l.slots['F'] = append(l.slots['F'], nodes...)
return l
}

View file

@ -250,6 +250,26 @@ func TestLayout_RenderNilReceiver(t *testing.T) {
}
}
func TestLayout_BuilderNilReceiver(t *testing.T) {
var layout *Layout
if got := layout.H(Raw("header")); got != nil {
t.Fatalf("nil Layout.H() should return nil, got %v", got)
}
if got := layout.L(Raw("left")); got != nil {
t.Fatalf("nil Layout.L() should return nil, got %v", got)
}
if got := layout.C(Raw("main")); got != nil {
t.Fatalf("nil Layout.C() should return nil, got %v", got)
}
if got := layout.R(Raw("right")); got != nil {
t.Fatalf("nil Layout.R() should return nil, got %v", got)
}
if got := layout.F(Raw("footer")); got != nil {
t.Fatalf("nil Layout.F() should return nil, got %v", got)
}
}
func TestLayout_RenderNilContext(t *testing.T) {
layout := NewLayout("C").C(Raw("content"))
got := layout.Render(nil)

View file

@ -100,6 +100,9 @@ func ScopeVariant(name, selector string) string {
// Example: r.Variant("mobile", NewLayout("C").C(Raw("body"))).
// Variants render in insertion order.
func (r *Responsive) Variant(name string, layout *Layout) *Responsive {
if r == nil {
return nil
}
r.variants = append(r.variants, responsiveVariant{name: name, layout: layout})
return r
}

View file

@ -96,6 +96,13 @@ func TestResponsive_RenderNilReceiver(t *testing.T) {
}
}
func TestResponsive_BuilderNilReceiver(t *testing.T) {
var r *Responsive
if got := r.Variant("desktop", NewLayout("C")); got != nil {
t.Fatalf("nil Responsive.Variant() should return nil, got %v", got)
}
}
func TestResponsive_RenderNilContext(t *testing.T) {
r := NewResponsive().
Variant("desktop", NewLayout("C").C(Raw("main")))