Skip to content

Commit

Permalink
Improve filter UI elems (#891)
Browse files Browse the repository at this point in the history
* update former FilterUIElems and its doc

* improve FilterUIElems unit tests

* cleanup test
  • Loading branch information
maxence-charriere authored Sep 28, 2023
1 parent d30c8bf commit 326e537
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 32 deletions.
49 changes: 25 additions & 24 deletions pkg/app/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,46 +84,47 @@ const (
RawHTML
)

// FilterUIElems returns a filtered version of the given UI elements where
// selector elements such as If and Range are interpreted and removed. It also
// remove nil elements.
// FilterUIElems processes and returns a filtered list of the provided UI
// elements.
//
// It should be used only when implementing components that can accept content
// with variadic arguments like HTML elements Body method.
// Specifically, it:
// - Interprets and removes selector elements such as Condition and RangeLoop.
// - Eliminates nil elements and nil pointers.
// - Flattens and includes the children of recognized selector elements.
//
// This function is primarily intended for components that accept ui elements as
// variadic arguments or slice, such as the Body method of HTML elements.
func FilterUIElems(v ...UI) []UI {
if len(v) == 0 {
return nil
}

remove := func(i int) {
removeELemAt := func(i int) {
copy(v[i:], v[i+1:])
v[len(v)-1] = nil
v = v[:len(v)-1]
}

var b []UI
replaceAt := func(i int, s ...UI) {
b = append(b, v[i+1:]...)
v = append(v[:i], s...)
v = append(v, b...)
b = b[:0]
var trailing []UI
replaceElemAt := func(i int, elems ...UI) {
trailing = append(trailing, v[i+1:]...)
v = append(v[:i], elems...)
v = append(v, trailing...)
trailing = trailing[:0]
}

for i := len(v) - 1; i >= 0; i-- {
e := v[i]
if ev := reflect.ValueOf(e); e == nil || ev.Kind() == reflect.Pointer && ev.IsNil() {
remove(i)
continue
elem := v[i]
if elem == nil {
removeELemAt(i)
}
if elemValue := reflect.ValueOf(elem); elemValue.Kind() == reflect.Pointer && elemValue.IsNil() {
removeELemAt(i)
}

switch e.Kind() {
case SimpleText, HTML, Component, RawHTML:

case Selector:
replaceAt(i, e.getChildren()...)

default:
remove(i)
switch elem.(type) {
case Condition, RangeLoop:
replaceElemAt(i, elem.getChildren()...)
}
}

Expand Down
90 changes: 82 additions & 8 deletions pkg/app/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,91 @@ func TestKindString(t *testing.T) {
}

func TestFilterUIElems(t *testing.T) {
var nilText *text
var foo *foo
t.Run("filter empty elements returns nil", func(t *testing.T) {
require.Nil(t, FilterUIElems())
})

simpleText := Text("hello")
t.Run("nil pointer is removed", func(t *testing.T) {
require.Empty(t, FilterUIElems(nil))
})

expectedResult := []UI{
simpleText,
}
t.Run("nil element is removed", func(t *testing.T) {
var foo *foo
require.Empty(t, FilterUIElems(foo))
})

t.Run("condition is inserted", func(t *testing.T) {
elems := FilterUIElems(
Div(),
If(true, func() UI {
return Span()
}),
)
require.Len(t, elems, 2)
require.IsType(t, Div(), elems[0])
require.IsType(t, Span(), elems[1])
})

t.Run("condition is removed", func(t *testing.T) {
elems := FilterUIElems(
Div(),
If(false, func() UI {
return Span()
}),
)
require.Len(t, elems, 1)
require.IsType(t, Div(), elems[0])
})

t.Run("range is inserted", func(t *testing.T) {
slice := []UI{Span()}

elems := FilterUIElems(
Div(),
Range(slice).Slice(func(i int) UI {
return slice[i]
}),
Div(),
)
require.Len(t, elems, 3)
require.IsType(t, Div(), elems[0])
require.IsType(t, Span(), elems[1])
require.IsType(t, Div(), elems[2])
})

t.Run("range is removed", func(t *testing.T) {
var slice []UI

elems := FilterUIElems(
Div(),
Range(slice).Slice(func(i int) UI {
return slice[i]
}),
Div(),
)
require.Len(t, elems, 2)
require.IsType(t, Div(), elems[0])
require.IsType(t, Div(), elems[1])
})

t.Run("no elements are removed", func(t *testing.T) {
foo := &foo{}
div := Div()
text := Text("hello")
raw := Raw("<br>")

res := FilterUIElems(nil, nilText, simpleText, foo)
require.Equal(t, expectedResult, res)
elems := FilterUIElems(
foo,
div,
text,
raw,
)
require.Len(t, elems, 4)
require.Equal(t, foo, elems[0])
require.Equal(t, div, elems[1])
require.Equal(t, text, elems[2])
require.Equal(t, raw, elems[3])
})
}

func BenchmarkFilterUIElems(b *testing.B) {
Expand Down

0 comments on commit 326e537

Please sign in to comment.