From 3b6b5539b476e800076e2591f35f77c4c5f10582 Mon Sep 17 00:00:00 2001 From: Dumitrana Alinus Date: Sun, 24 May 2020 04:58:15 +0300 Subject: [PATCH] add: fix component like structures --- component.go | 34 ++++++++++++++++++++++++++- component_test.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++ fragment.go | 8 +++++++ render.go | 16 ++++++++----- 4 files changed, 110 insertions(+), 7 deletions(-) diff --git a/component.go b/component.go index f6f1065..f29e372 100644 --- a/component.go +++ b/component.go @@ -1,11 +1,43 @@ package react +import ( + "reflect" +) + type ComponentStruct interface { Render(ctx *Context) interface{} } func IsComponent(value interface{}) bool { - _, ok := value.(ComponentStruct) + ok := false + + if value != nil { + rVal := reflect.TypeOf(value) + _, ok = rVal.MethodByName("Render") + } return ok } + +func renderComponent(value interface{}, ctx interface{}) interface{} { + if !IsComponent(value) { + return nil + } + + rVal := reflect.ValueOf(value) + method := rVal.MethodByName("Render") + + if method.IsZero() || method.IsNil() { + return nil + } + + var result []reflect.Value + + if ctx != nil { + result = method.Call([]reflect.Value{reflect.ValueOf(ctx)}) + } else { + result = method.Call([]reflect.Value{reflect.ValueOf(NewContext())}) + } + + return result[0].Interface() +} diff --git a/component_test.go b/component_test.go index 917211d..2109808 100644 --- a/component_test.go +++ b/component_test.go @@ -22,6 +22,37 @@ func TestIsComponent(t *testing.T) { }) } +type LayoutMountPoints struct { + Head *Element + Header *Element + Content *Element +} + +type LayoutBaseComponent struct { + mounts *LayoutMountPoints +} + +func (l *LayoutBaseComponent) Render(ctx *Context) interface{} { + if l.mounts == nil { + l.mounts = &LayoutMountPoints{} + } + + return Document(&DocumentProps{ + Head: Fragment( + MetaCharset(), + l.mounts.Head, + ), + Body: Fragment( + CreateElement("header", nil, l.mounts.Header), + l.mounts.Content, + ), + }, nil) +} + +func LayoutBase(mounts *LayoutMountPoints) *LayoutBaseComponent { + return &LayoutBaseComponent{mounts: mounts} +} + func TestRenderClassLike(t *testing.T) { t.Run("it should correctly render component class like", func(t *testing.T) { content, err := Render(Div(nil, @@ -34,4 +65,32 @@ func TestRenderClassLike(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "
span el

Hello David! How are you?

", content) }) + + t.Run("it should correctly render advanced layout structure", func(t *testing.T) { + comp := LayoutBase(&LayoutMountPoints{ + Content: Fragment( + H1(nil, "Hello World!"), + ), + }) + + content, err := Render(Fragment( + comp, + )) + + assert.True(t, IsComponent(comp)) + assert.NoError(t, err) + assert.Equal(t, `

Hello World!

`, content) + }) +} + +type TestStringComp struct{} + +func (t TestStringComp) Render(ctx *Context) interface{} { + return "test string" +} + +func TestRenderComponent(t *testing.T) { + r := renderComponent(&TestStringComp{}, nil) + + assert.Equal(t, "test string", r) } diff --git a/fragment.go b/fragment.go index c1b4068..2d498a3 100644 --- a/fragment.go +++ b/fragment.go @@ -1,7 +1,15 @@ package react func IsFragment(value interface{}) bool { + if value == nil { + return false + } v, ok := value.(*Element) + + if v == nil { + return false + } + return ok && v.Fragment } diff --git a/render.go b/render.go index 14fed6d..8d4052b 100644 --- a/render.go +++ b/render.go @@ -47,6 +47,10 @@ func genHtml(tag string, attrs *Props, childs []string) string { } func render(component *Element, ctx *Context) (string, error) { + if component == nil { + return "", nil + } + if component.Tag == "" && !component.Fragment { return "", errors.New("component has an empty tag") } @@ -73,12 +77,12 @@ func render(component *Element, ctx *Context) (string, error) { var chunk string var errChunk error - switch c.(type) { - case ComponentStruct: - result := c.(ComponentStruct).Render(ctx) - chunk, errChunk = renderElement(result, ctx) - default: - chunk, errChunk = renderElement(c, ctx) + if c != nil { + if IsComponent(c) { + chunk, errChunk = renderElement(renderComponent(c, ctx), ctx) + } else { + chunk, errChunk = renderElement(c, ctx) + } } if errChunk != nil {