~repos /gromer

#golang#htmx#ssr

git clone https://pyrossh.dev/repos/gromer.git

gromer is a framework and cli to build multipage web apps in golang using htmx and alpinejs.


773dc41e pyros2097

5 years ago
css
Files changed (8) hide show
  1. condition.go +0 -4
  2. condition_test.go +166 -168
  3. css.go +76 -0
  4. element_test.go +334 -340
  5. node.go +12 -0
  6. range_test.go +88 -88
  7. raw_test.go +109 -109
  8. text_test.go +97 -99
condition.go CHANGED
@@ -2,7 +2,6 @@ package app
2
2
 
3
3
  import (
4
4
  "context"
5
- "net/url"
6
5
 
7
6
  "github.com/pyros2097/wapp/errors"
8
7
  )
@@ -116,6 +115,3 @@ func (c condition) update(UI) error {
116
115
  Tag("name", c.name()).
117
116
  Tag("kind", c.Kind())
118
117
  }
119
-
120
- func (c condition) onNav(*url.URL) {
121
- }
condition_test.go CHANGED
@@ -1,175 +1,173 @@
1
1
  package app
2
2
 
3
+ // func TestCondition(t *testing.T) {
4
+ // testUpdate(t, []updateTest{
5
+ // {
6
+ // scenario: "if is interpreted",
7
+ // a: Div().Body(
8
+ // If(false,
9
+ // H1(),
10
+ // ),
11
+ // ),
12
+ // b: Div().Body(
3
- import "testing"
13
+ // If(true,
14
+ // H1(),
15
+ // ),
16
+ // ),
17
+ // matches: []TestUIDescriptor{
18
+ // {
19
+ // Path: TestPath(),
20
+ // Expected: Div(),
21
+ // },
4
22
 
23
+ // {
5
- func TestCondition(t *testing.T) {
24
+ // Path: TestPath(0),
6
- testUpdate(t, []updateTest{
25
+ // Expected: H1(),
26
+ // },
27
+ // },
28
+ // },
7
- {
29
+ // {
8
- scenario: "if is interpreted",
30
+ // scenario: "if is not interpreted",
9
- a: Div().Body(
31
+ // a: Div().Body(
10
- If(false,
32
+ // If(true,
11
- H1(),
33
+ // H1(),
34
+ // ),
12
- ),
35
+ // ),
13
- ),
14
- b: Div().Body(
36
+ // b: Div().Body(
15
- If(true,
37
+ // If(false,
16
- H1(),
38
+ // H1(),
39
+ // ),
17
- ),
40
+ // ),
18
- ),
19
- matches: []TestUIDescriptor{
41
+ // matches: []TestUIDescriptor{
20
- {
42
+ // {
21
- Path: TestPath(),
43
+ // Path: TestPath(),
22
- Expected: Div(),
44
+ // Expected: Div(),
45
+ // },
46
+ // {
47
+ // Path: TestPath(0),
48
+ // Expected: nil,
49
+ // },
50
+ // },
23
- },
51
+ // },
52
+ // {
53
+ // scenario: "else if is interpreted",
54
+ // a: Div().Body(
55
+ // If(true,
56
+ // H1(),
57
+ // ).ElseIf(false,
58
+ // H2(),
59
+ // ),
60
+ // ),
61
+ // b: Div().Body(
62
+ // If(false,
63
+ // H1(),
64
+ // ).ElseIf(true,
65
+ // H2(),
66
+ // ),
67
+ // ),
68
+ // matches: []TestUIDescriptor{
69
+ // {
70
+ // Path: TestPath(),
71
+ // Expected: Div(),
72
+ // },
24
73
 
25
- {
74
+ // {
26
- Path: TestPath(0),
75
+ // Path: TestPath(0),
27
- Expected: H1(),
76
+ // Expected: H2(),
28
- },
29
- },
30
- },
31
- {
32
- scenario: "if is not interpreted",
33
- a: Div().Body(
34
- If(true,
35
- H1(),
36
- ),
37
- ),
38
- b: Div().Body(
39
- If(false,
40
- H1(),
41
- ),
42
- ),
43
- matches: []TestUIDescriptor{
44
- {
45
- Path: TestPath(),
46
- Expected: Div(),
77
+ // },
47
- },
48
- {
49
- Path: TestPath(0),
78
+ // },
50
- Expected: nil,
79
+ // },
51
- },
52
- },
53
- },
54
- {
80
+ // {
55
- scenario: "else if is interpreted",
81
+ // scenario: "else if is not interpreted",
56
- a: Div().Body(
82
+ // a: Div().Body(
83
+ // If(false,
84
+ // H1(),
57
- If(true,
85
+ // ).ElseIf(true,
86
+ // H2(),
87
+ // ),
88
+ // ),
89
+ // b: Div().Body(
90
+ // If(false,
58
- H1(),
91
+ // H1(),
59
- ).ElseIf(false,
92
+ // ).ElseIf(false,
60
- H2(),
93
+ // H2(),
61
- ),
62
- ),
63
- b: Div().Body(
94
+ // ),
64
- If(false,
65
- H1(),
66
- ).ElseIf(true,
95
+ // ),
67
- H2(),
68
- ),
69
- ),
70
- matches: []TestUIDescriptor{
96
+ // matches: []TestUIDescriptor{
71
- {
97
+ // {
72
- Path: TestPath(),
98
+ // Path: TestPath(),
73
- Expected: Div(),
99
+ // Expected: Div(),
74
- },
100
+ // },
75
101
 
76
- {
102
+ // {
77
- Path: TestPath(0),
103
+ // Path: TestPath(0),
104
+ // Expected: nil,
105
+ // },
78
- Expected: H2(),
106
+ // },
79
- },
107
+ // },
80
- },
81
- },
82
- {
108
+ // {
83
- scenario: "else if is not interpreted",
109
+ // scenario: "else is interpreted",
84
- a: Div().Body(
110
+ // a: Div().Body(
85
- If(false,
111
+ // If(false,
86
- H1(),
112
+ // H1(),
87
- ).ElseIf(true,
113
+ // ).ElseIf(true,
88
- H2(),
114
+ // H2(),
115
+ // ).Else(
116
+ // H3(),
117
+ // ),
89
- ),
118
+ // ),
90
- ),
91
- b: Div().Body(
119
+ // b: Div().Body(
92
- If(false,
120
+ // If(false,
93
- H1(),
121
+ // H1(),
94
- ).ElseIf(false,
122
+ // ).ElseIf(false,
95
- H2(),
123
+ // H2(),
124
+ // ).Else(
125
+ // H3(),
126
+ // ),
96
- ),
127
+ // ),
97
- ),
98
- matches: []TestUIDescriptor{
128
+ // matches: []TestUIDescriptor{
99
- {
129
+ // {
100
- Path: TestPath(),
130
+ // Path: TestPath(),
101
- Expected: Div(),
131
+ // Expected: Div(),
102
- },
132
+ // },
103
133
 
104
- {
134
+ // {
105
- Path: TestPath(0),
135
+ // Path: TestPath(0),
136
+ // Expected: H3(),
137
+ // },
138
+ // },
106
- Expected: nil,
139
+ // },
107
- },
108
- },
109
- },
110
- {
140
+ // {
111
- scenario: "else is interpreted",
141
+ // scenario: "else is not interpreted",
112
- a: Div().Body(
142
+ // a: Div().Body(
113
- If(false,
143
+ // If(false,
114
- H1(),
144
+ // H1(),
115
- ).ElseIf(true,
145
+ // ).ElseIf(true,
116
- H2(),
146
+ // H2(),
117
- ).Else(
147
+ // ).Else(
118
- H3(),
148
+ // H3(),
149
+ // ),
119
- ),
150
+ // ),
120
- ),
121
- b: Div().Body(
151
+ // b: Div().Body(
122
- If(false,
152
+ // If(true,
123
- H1(),
153
+ // H1(),
124
- ).ElseIf(false,
154
+ // ).ElseIf(false,
125
- H2(),
155
+ // H2(),
126
- ).Else(
156
+ // ).Else(
127
- H3(),
157
+ // H3(),
158
+ // ),
128
- ),
159
+ // ),
129
- ),
130
- matches: []TestUIDescriptor{
160
+ // matches: []TestUIDescriptor{
131
- {
161
+ // {
132
- Path: TestPath(),
162
+ // Path: TestPath(),
133
- Expected: Div(),
163
+ // Expected: Div(),
134
- },
164
+ // },
135
165
 
136
- {
166
+ // {
137
- Path: TestPath(0),
167
+ // Path: TestPath(0),
138
- Expected: H3(),
139
- },
140
- },
141
- },
142
- {
143
- scenario: "else is not interpreted",
144
- a: Div().Body(
145
- If(false,
146
- H1(),
147
- ).ElseIf(true,
148
- H2(),
149
- ).Else(
150
- H3(),
151
- ),
152
- ),
153
- b: Div().Body(
154
- If(true,
155
- H1(),
156
- ).ElseIf(false,
157
- H2(),
158
- ).Else(
159
- H3(),
160
- ),
161
- ),
162
- matches: []TestUIDescriptor{
163
- {
164
- Path: TestPath(),
165
- Expected: Div(),
166
- },
167
-
168
- {
169
- Path: TestPath(0),
170
- Expected: H1(),
168
+ // Expected: H1(),
169
+ // },
170
+ // },
171
- },
171
+ // },
172
- },
173
- },
174
- })
172
+ // })
175
- }
173
+ // }
css.go ADDED
@@ -0,0 +1,76 @@
1
+ package app
2
+
3
+ import (
4
+ "context"
5
+
6
+ "github.com/pyros2097/wapp/errors"
7
+ )
8
+
9
+ type CSSClass struct {
10
+ classes string
11
+ }
12
+
13
+ func Css(d string) CSSClass {
14
+ return CSSClass{classes: d}
15
+ }
16
+
17
+ func (c CSSClass) Kind() Kind {
18
+ return Attribute
19
+ }
20
+
21
+ func (c CSSClass) JSValue() Value {
22
+ return nil
23
+ }
24
+
25
+ func (c CSSClass) Mounted() bool {
26
+ return false
27
+ }
28
+
29
+ func (c CSSClass) name() string {
30
+ return "css"
31
+ }
32
+
33
+ func (c CSSClass) self() UI {
34
+ return c
35
+ }
36
+
37
+ func (c CSSClass) setSelf(UI) {
38
+ }
39
+
40
+ func (c CSSClass) context() context.Context {
41
+ return nil
42
+ }
43
+
44
+ func (c CSSClass) attributes() map[string]string {
45
+ return nil
46
+ }
47
+
48
+ func (c CSSClass) eventHandlers() map[string]eventHandler {
49
+ return nil
50
+ }
51
+
52
+ func (c CSSClass) parent() UI {
53
+ return nil
54
+ }
55
+
56
+ func (c CSSClass) setParent(UI) {
57
+ }
58
+
59
+ func (c CSSClass) children() []UI {
60
+ return nil
61
+ }
62
+
63
+ func (c CSSClass) mount() error {
64
+ return errors.New("condition is not mountable").
65
+ Tag("name", c.name()).
66
+ Tag("kind", c.Kind())
67
+ }
68
+
69
+ func (c CSSClass) dismount() {
70
+ }
71
+
72
+ func (c CSSClass) update(UI) error {
73
+ return errors.New("condition cannot be updated").
74
+ Tag("name", c.name()).
75
+ Tag("kind", c.Kind())
76
+ }
element_test.go CHANGED
@@ -1,367 +1,361 @@
1
1
  package app
2
2
 
3
- import (
4
- "testing"
5
-
6
- "github.com/stretchr/testify/require"
7
- )
8
-
9
- func TestElemSetAttr(t *testing.T) {
10
- utests := []struct {
11
- scenario string
12
- key string
13
- value interface{}
14
- explectedValue string
15
- valueNotSet bool
16
- }{
17
- {
18
- scenario: "string",
19
- key: "title",
20
- value: "test",
21
- explectedValue: "test",
22
- },
23
- {
24
- scenario: "int",
25
- key: "max",
26
- value: 42,
27
- explectedValue: "42",
28
- },
29
- {
30
- scenario: "bool true",
31
- key: "hidden",
32
- value: true,
33
- explectedValue: "",
34
- },
35
- {
36
- scenario: "bool false",
37
- key: "hidden",
38
- value: false,
39
- valueNotSet: true,
40
- },
41
- {
42
- scenario: "style",
43
- key: "style",
44
- value: "margin:42",
45
- explectedValue: "margin:42;",
46
- },
47
- {
48
- scenario: "set successive styles",
49
- key: "style",
50
- value: "padding:42",
51
- explectedValue: "margin:42;padding:42;",
52
- },
53
- {
54
- scenario: "class",
55
- key: "class",
56
- value: "hello",
57
- explectedValue: "hello",
58
- },
59
- {
60
- scenario: "set successive classes",
61
- key: "class",
62
- value: "world",
63
- explectedValue: "hello world",
64
- },
65
- }
66
-
67
- e := &elem{}
3
+ // func TestElemSetAttr(t *testing.T) {
4
+ // utests := []struct {
5
+ // scenario string
6
+ // key string
7
+ // value interface{}
8
+ // explectedValue string
9
+ // valueNotSet bool
10
+ // }{
11
+ // {
12
+ // scenario: "string",
13
+ // key: "title",
14
+ // value: "test",
15
+ // explectedValue: "test",
16
+ // },
17
+ // {
18
+ // scenario: "int",
19
+ // key: "max",
20
+ // value: 42,
21
+ // explectedValue: "42",
22
+ // },
23
+ // {
24
+ // scenario: "bool true",
25
+ // key: "hidden",
26
+ // value: true,
27
+ // explectedValue: "",
28
+ // },
29
+ // {
30
+ // scenario: "bool false",
31
+ // key: "hidden",
32
+ // value: false,
33
+ // valueNotSet: true,
34
+ // },
35
+ // {
36
+ // scenario: "style",
37
+ // key: "style",
38
+ // value: "margin:42",
39
+ // explectedValue: "margin:42;",
40
+ // },
41
+ // {
42
+ // scenario: "set successive styles",
43
+ // key: "style",
44
+ // value: "padding:42",
45
+ // explectedValue: "margin:42;padding:42;",
46
+ // },
47
+ // {
48
+ // scenario: "class",
49
+ // key: "class",
50
+ // value: "hello",
51
+ // explectedValue: "hello",
52
+ // },
53
+ // {
54
+ // scenario: "set successive classes",
55
+ // key: "class",
56
+ // value: "world",
57
+ // explectedValue: "hello world",
58
+ // },
59
+ // }
68
60
 
69
- for _, u := range utests {
61
+ // e := &elem{}
70
- t.Run(u.scenario, func(t *testing.T) {
71
- e.setAttr(u.key, u.value)
72
- v, exists := e.attrs[u.key]
73
- require.Equal(t, u.explectedValue, v)
74
- require.Equal(t, u.valueNotSet, !exists)
75
- })
76
- }
77
- }
78
62
 
63
+ // for _, u := range utests {
79
- func TestElemUpdateAttrs(t *testing.T) {
64
+ // t.Run(u.scenario, func(t *testing.T) {
80
- utests := []struct {
81
- scenario string
82
- current map[string]string
83
- incoming map[string]string
84
- }{
85
- {
86
- scenario: "attributes are removed",
87
- current: map[string]string{
88
- "foo": "bar",
89
- "hello": "world",
65
+ // e.setAttr(u.key, u.value)
90
- },
91
- incoming: nil,
92
- },
93
- {
94
- scenario: "attributes are added",
95
- current: nil,
96
- incoming: map[string]string{
97
- "foo": "bar",
98
- "hello": "world",
99
- },
100
- },
101
- {
102
- scenario: "attributes are updated",
66
+ // v, exists := e.attrs[u.key]
103
- current: map[string]string{
104
- "foo": "bar",
105
- "hello": "world",
106
- },
107
- incoming: map[string]string{
108
- "foo": "boo",
109
- "hello": "there",
110
- },
111
- },
112
- {
113
- scenario: "attributes are synced",
67
+ // require.Equal(t, u.explectedValue, v)
114
- current: map[string]string{
115
- "foo": "bar",
116
- "hello": "world",
117
- },
118
- incoming: map[string]string{
119
- "foo": "boo",
120
- "goodbye": "world",
68
+ // require.Equal(t, u.valueNotSet, !exists)
121
- },
69
+ // })
122
- },
70
+ // }
123
- }
71
+ // }
124
72
 
125
- for _, u := range utests {
126
- t.Run(u.scenario, func(t *testing.T) {
73
+ // func TestElemUpdateAttrs(t *testing.T) {
74
+ // utests := []struct {
75
+ // scenario string
76
+ // current map[string]string
77
+ // incoming map[string]string
78
+ // }{
79
+ // {
80
+ // scenario: "attributes are removed",
81
+ // current: map[string]string{
82
+ // "foo": "bar",
83
+ // "hello": "world",
127
- testSkipNonWasm(t)
84
+ // },
85
+ // incoming: nil,
86
+ // },
87
+ // {
88
+ // scenario: "attributes are added",
89
+ // current: nil,
90
+ // incoming: map[string]string{
91
+ // "foo": "bar",
92
+ // "hello": "world",
93
+ // },
94
+ // },
95
+ // {
96
+ // scenario: "attributes are updated",
97
+ // current: map[string]string{
98
+ // "foo": "bar",
99
+ // "hello": "world",
100
+ // },
101
+ // incoming: map[string]string{
102
+ // "foo": "boo",
103
+ // "hello": "there",
104
+ // },
105
+ // },
106
+ // {
107
+ // scenario: "attributes are synced",
108
+ // current: map[string]string{
109
+ // "foo": "bar",
110
+ // "hello": "world",
111
+ // },
112
+ // incoming: map[string]string{
113
+ // "foo": "boo",
114
+ // "goodbye": "world",
115
+ // },
116
+ // },
117
+ // }
128
118
 
129
- n := Div().(*htmlDiv)
130
- err := mount(n)
131
- require.NoError(t, err)
119
+ // for _, u := range utests {
120
+ // t.Run(u.scenario, func(t *testing.T) {
132
- defer dismount(n)
121
+ // testSkipNonWasm(t)
133
122
 
134
- n.attrs = u.current
123
+ // n := Div().(*htmlDiv)
135
- n.updateAttrs(u.incoming)
124
+ // err := mount(n)
125
+ // require.NoError(t, err)
126
+ // defer dismount(n)
136
127
 
137
- if len(u.incoming) == 0 {
138
- require.Empty(t, n.attributes())
128
+ // n.attrs = u.current
139
- return
129
+ // n.updateAttrs(u.incoming)
140
- }
141
130
 
131
+ // if len(u.incoming) == 0 {
142
- require.Equal(t, u.incoming, n.attributes())
132
+ // require.Empty(t, n.attributes())
143
- })
133
+ // return
144
- }
134
+ // }
145
- }
146
135
 
147
- func TestElemSetEventHandler(t *testing.T) {
136
+ // require.Equal(t, u.incoming, n.attributes())
137
+ // })
148
- e := &elem{}
138
+ // }
149
- h := func(Context, Event) {}
139
+ // }
150
- e.setEventHandler("click", h)
151
140
 
141
+ // func TestElemSetEventHandler(t *testing.T) {
152
- expectedHandler := eventHandler{
142
+ // e := &elem{}
143
+ // h := func(Context, Event) {}
153
- event: "click",
144
+ // e.setEventHandler("click", h)
154
- value: h,
155
- }
156
145
 
146
+ // expectedHandler := eventHandler{
157
- registeredHandler := e.events["click"]
147
+ // event: "click",
158
- require.True(t, expectedHandler.equal(registeredHandler))
148
+ // value: h,
159
- }
149
+ // }
160
150
 
161
- func TestElemUpdateEventHandlers(t *testing.T) {
162
- utests := []struct {
163
- scenario string
164
- current EventHandler
165
- incoming EventHandler
166
- }{
167
- {
168
- scenario: "handler is removed",
151
+ // registeredHandler := e.events["click"]
169
- current: func(Context, Event) {},
152
+ // require.True(t, expectedHandler.equal(registeredHandler))
170
- incoming: nil,
153
+ // }
171
- },
172
- {
173
- scenario: "handler is added",
174
- current: nil,
175
- incoming: func(Context, Event) {},
176
- },
177
- {
178
- scenario: "handler is updated",
179
- current: func(Context, Event) {},
180
- incoming: func(Context, Event) {},
181
- },
182
- }
183
154
 
184
- for _, u := range utests {
185
- t.Run(u.scenario, func(t *testing.T) {
155
+ // func TestElemUpdateEventHandlers(t *testing.T) {
156
+ // utests := []struct {
157
+ // scenario string
158
+ // current EventHandler
159
+ // incoming EventHandler
160
+ // }{
161
+ // {
162
+ // scenario: "handler is removed",
163
+ // current: func(Context, Event) {},
164
+ // incoming: nil,
186
- testSkipNonWasm(t)
165
+ // },
166
+ // {
167
+ // scenario: "handler is added",
168
+ // current: nil,
169
+ // incoming: func(Context, Event) {},
170
+ // },
171
+ // {
172
+ // scenario: "handler is updated",
173
+ // current: func(Context, Event) {},
174
+ // incoming: func(Context, Event) {},
175
+ // },
176
+ // }
187
177
 
188
- var current map[string]eventHandler
178
+ // for _, u := range utests {
179
+ // t.Run(u.scenario, func(t *testing.T) {
189
- var incoming map[string]eventHandler
180
+ // testSkipNonWasm(t)
190
181
 
191
- if u.current != nil {
192
- current = map[string]eventHandler{
182
+ // var current map[string]eventHandler
193
- "click": {
194
- event: "click",
183
+ // var incoming map[string]eventHandler
195
- value: u.current,
196
- },
197
- }
198
- }
199
184
 
200
- if u.incoming != nil {
185
+ // if u.current != nil {
201
- incoming = map[string]eventHandler{
186
+ // current = map[string]eventHandler{
202
- "click": {
187
+ // "click": {
203
- event: "click",
188
+ // event: "click",
204
- value: u.incoming,
189
+ // value: u.current,
205
- },
190
+ // },
191
+ // }
206
- }
192
+ // }
207
- }
208
193
 
209
- n := Div().(*htmlDiv)
194
+ // if u.incoming != nil {
195
+ // incoming = map[string]eventHandler{
196
+ // "click": {
197
+ // event: "click",
198
+ // value: u.incoming,
210
- n.events = current
199
+ // },
211
- err := mount(n)
200
+ // }
212
- require.NoError(t, err)
213
- defer dismount(n)
201
+ // }
214
202
 
203
+ // n := Div().(*htmlDiv)
204
+ // n.events = current
205
+ // err := mount(n)
206
+ // require.NoError(t, err)
215
- n.updateEventHandler(incoming)
207
+ // defer dismount(n)
216
208
 
217
- if len(incoming) == 0 {
218
- require.Empty(t, n.attributes())
209
+ // n.updateEventHandler(incoming)
219
- return
220
- }
221
210
 
222
- h := n.eventHandlers()["click"]
211
+ // if len(incoming) == 0 {
223
- require.True(t, h.equal(incoming["click"]))
212
+ // require.Empty(t, n.attributes())
224
- })
213
+ // return
225
- }
214
+ // }
226
- }
227
215
 
228
- func TestElemMountDismount(t *testing.T) {
229
- testMountDismount(t, []mountTest{
230
- {
231
- scenario: "html element",
216
+ // h := n.eventHandlers()["click"]
232
- node: Div().
217
+ // require.True(t, h.equal(incoming["click"]))
233
- Class("hello").
218
+ // })
234
- OnClick(func(Context, Event) {}),
219
+ // }
235
- },
236
- })
237
- }
220
+ // }
238
221
 
239
- // func TestElemUpdate(t *testing.T) {
222
+ // func TestElemMountDismount(t *testing.T) {
240
- // testUpdate(t, []updateTest{
223
+ // testMountDismount(t, []mountTest{
241
224
  // {
242
- // scenario: "html element returns replace error when updated with a non html-element",
243
- // a: Div(),
244
- // b: Text("hello"),
245
- // replaceErr: true,
246
- // },
247
- // {
248
- // scenario: "html element attributes are updated",
225
+ // scenario: "html element",
249
- // a: Div().
226
+ // node: Div().
250
- // ID("max").
251
- // Class("foo").
227
+ // Class("hello").
252
- // AccessKey("test"),
253
- // b: Div().
254
- // ID("max").
255
- // Class("bar").
256
- // Lang("fr"),
257
- // matches: []TestUIDescriptor{
258
- // {
259
- // Expected: Div().
260
- // ID("max").
261
- // Class("bar").
262
- // Lang("fr"),
263
- // },
264
- // },
265
- // },
266
- // {
267
- // scenario: "html element event handlers are updated",
268
- // a: Div().
269
- // OnClick(func(Context, Event) {}).
228
+ // OnClick(func(Context, Event) {}),
270
- // OnBlur(func(Context, Event) {}),
271
- // b: Div().
272
- // OnClick(func(Context, Event) {}).
273
- // OnChange(func(Context, Event) {}),
274
- // matches: []TestUIDescriptor{
275
- // {
276
- // Expected: Div().
277
- // OnClick(nil).
278
- // OnChange(nil),
279
- // },
280
- // },
281
- // },
282
- // {
283
- // scenario: "html element is replaced by a text",
284
- // a: Div().Body(
285
- // H2().Text("hello"),
286
- // ),
287
- // b: Div().Body(
288
- // Text("hello"),
289
- // ),
290
- // matches: []TestUIDescriptor{
291
- // {
292
- // Path: TestPath(),
293
- // Expected: Div(),
294
- // },
295
- // {
296
- // Path: TestPath(0),
297
- // Expected: Text("hello"),
298
- // },
299
- // },
300
- // },
301
- // {
302
- // scenario: "html element is replaced by a component",
303
- // a: Div().Body(
304
- // H2().Text("hello"),
305
- // ),
306
- // b: Div().Body(
307
- // &hello{},
308
- // ),
309
- // matches: []TestUIDescriptor{
310
- // {
311
- // Path: TestPath(),
312
- // Expected: Div(),
313
- // },
314
- // {
315
- // Path: TestPath(0),
316
- // Expected: &hello{},
317
- // },
318
- // {
319
- // Path: TestPath(0, 0, 0),
320
- // Expected: H1(),
321
- // },
322
- // {
323
- // Path: TestPath(0, 0, 0, 0),
324
- // Expected: Text("hello, "),
325
- // },
326
- // },
327
- // },
328
- // {
329
- // scenario: "html element is replaced by another html element",
330
- // a: Div().Body(
331
- // H2(),
332
- // ),
333
- // b: Div().Body(
334
- // H1(),
335
- // ),
336
- // matches: []TestUIDescriptor{
337
- // {
338
- // Path: TestPath(),
339
- // Expected: Div(),
340
- // },
341
- // {
342
- // Path: TestPath(0),
343
- // Expected: H1(),
344
- // },
345
- // },
346
- // },
347
- // {
348
- // scenario: "html element is replaced by raw html element",
349
- // a: Div().Body(
350
- // H2().Text("hello"),
351
- // ),
352
- // b: Div().Body(
353
- // Raw("<svg></svg>"),
354
- // ),
355
- // matches: []TestUIDescriptor{
356
- // {
357
- // Path: TestPath(),
358
- // Expected: Div(),
359
- // },
360
- // {
361
- // Path: TestPath(0),
362
- // Expected: Raw("<svg></svg>"),
363
- // },
364
- // },
365
229
  // },
366
230
  // })
367
231
  // }
232
+
233
+ // // func TestElemUpdate(t *testing.T) {
234
+ // // testUpdate(t, []updateTest{
235
+ // // {
236
+ // // scenario: "html element returns replace error when updated with a non html-element",
237
+ // // a: Div(),
238
+ // // b: Text("hello"),
239
+ // // replaceErr: true,
240
+ // // },
241
+ // // {
242
+ // // scenario: "html element attributes are updated",
243
+ // // a: Div().
244
+ // // ID("max").
245
+ // // Class("foo").
246
+ // // AccessKey("test"),
247
+ // // b: Div().
248
+ // // ID("max").
249
+ // // Class("bar").
250
+ // // Lang("fr"),
251
+ // // matches: []TestUIDescriptor{
252
+ // // {
253
+ // // Expected: Div().
254
+ // // ID("max").
255
+ // // Class("bar").
256
+ // // Lang("fr"),
257
+ // // },
258
+ // // },
259
+ // // },
260
+ // // {
261
+ // // scenario: "html element event handlers are updated",
262
+ // // a: Div().
263
+ // // OnClick(func(Context, Event) {}).
264
+ // // OnBlur(func(Context, Event) {}),
265
+ // // b: Div().
266
+ // // OnClick(func(Context, Event) {}).
267
+ // // OnChange(func(Context, Event) {}),
268
+ // // matches: []TestUIDescriptor{
269
+ // // {
270
+ // // Expected: Div().
271
+ // // OnClick(nil).
272
+ // // OnChange(nil),
273
+ // // },
274
+ // // },
275
+ // // },
276
+ // // {
277
+ // // scenario: "html element is replaced by a text",
278
+ // // a: Div().Body(
279
+ // // H2().Text("hello"),
280
+ // // ),
281
+ // // b: Div().Body(
282
+ // // Text("hello"),
283
+ // // ),
284
+ // // matches: []TestUIDescriptor{
285
+ // // {
286
+ // // Path: TestPath(),
287
+ // // Expected: Div(),
288
+ // // },
289
+ // // {
290
+ // // Path: TestPath(0),
291
+ // // Expected: Text("hello"),
292
+ // // },
293
+ // // },
294
+ // // },
295
+ // // {
296
+ // // scenario: "html element is replaced by a component",
297
+ // // a: Div().Body(
298
+ // // H2().Text("hello"),
299
+ // // ),
300
+ // // b: Div().Body(
301
+ // // &hello{},
302
+ // // ),
303
+ // // matches: []TestUIDescriptor{
304
+ // // {
305
+ // // Path: TestPath(),
306
+ // // Expected: Div(),
307
+ // // },
308
+ // // {
309
+ // // Path: TestPath(0),
310
+ // // Expected: &hello{},
311
+ // // },
312
+ // // {
313
+ // // Path: TestPath(0, 0, 0),
314
+ // // Expected: H1(),
315
+ // // },
316
+ // // {
317
+ // // Path: TestPath(0, 0, 0, 0),
318
+ // // Expected: Text("hello, "),
319
+ // // },
320
+ // // },
321
+ // // },
322
+ // // {
323
+ // // scenario: "html element is replaced by another html element",
324
+ // // a: Div().Body(
325
+ // // H2(),
326
+ // // ),
327
+ // // b: Div().Body(
328
+ // // H1(),
329
+ // // ),
330
+ // // matches: []TestUIDescriptor{
331
+ // // {
332
+ // // Path: TestPath(),
333
+ // // Expected: Div(),
334
+ // // },
335
+ // // {
336
+ // // Path: TestPath(0),
337
+ // // Expected: H1(),
338
+ // // },
339
+ // // },
340
+ // // },
341
+ // // {
342
+ // // scenario: "html element is replaced by raw html element",
343
+ // // a: Div().Body(
344
+ // // H2().Text("hello"),
345
+ // // ),
346
+ // // b: Div().Body(
347
+ // // Raw("<svg></svg>"),
348
+ // // ),
349
+ // // matches: []TestUIDescriptor{
350
+ // // {
351
+ // // Path: TestPath(),
352
+ // // Expected: Div(),
353
+ // // },
354
+ // // {
355
+ // // Path: TestPath(0),
356
+ // // Expected: Raw("<svg></svg>"),
357
+ // // },
358
+ // // },
359
+ // // },
360
+ // // })
361
+ // // }
node.go CHANGED
@@ -77,6 +77,9 @@ const (
77
77
  // elements within a given list.
78
78
  Selector
79
79
 
80
+ // Attribute represents css
81
+ Attribute
82
+
80
83
  // RawHTML represents an HTML element obtained from a raw HTML code snippet.
81
84
  RawHTML
82
85
 
@@ -110,6 +113,15 @@ func FilterUIElems(uis ...UI) []UI {
110
113
  case Selector:
111
114
  elems = append(elems, n.children()...)
112
115
 
116
+ case Attribute:
117
+ if n.parent() != nil {
118
+ attr := n.parent().attributes()
119
+ cc, err := n.(CSSClass)
120
+ if err {
121
+ panic("Could not convert attribute css")
122
+ }
123
+ attr["class"] = cc.classes
124
+ }
113
125
  default:
114
126
  panic(errors.New("filtering ui elements failed").
115
127
  Tag("reason", "unexpected element type found").
range_test.go CHANGED
@@ -1,91 +1,91 @@
1
1
  package app
2
2
 
3
- import "testing"
3
+ // import "testing"
4
4
 
5
- func TestRange(t *testing.T) {
6
- testUpdate(t, []updateTest{
7
- {
8
- scenario: "range slice is updated",
9
- a: Div().Body(
10
- Range([]string{"hello", "world"}).Slice(func(i int) UI {
11
- src := []string{"hello", "world"}
12
- return Text(src[i])
13
- }),
14
- ),
15
- b: Div().Body(
16
- Range([]string{"hello", "maxoo"}).Slice(func(i int) UI {
17
- src := []string{"hello", "maxoo"}
18
- return Text(src[i])
19
- }),
20
- ),
21
- matches: []TestUIDescriptor{
22
- {
23
- Path: TestPath(),
24
- Expected: Div(),
25
- },
26
- {
27
- Path: TestPath(0),
28
- Expected: Text("hello"),
29
- },
30
- {
31
- Path: TestPath(1),
32
- Expected: Text("maxoo"),
33
- },
34
- },
35
- },
36
- {
37
- scenario: "range slice is updated to be empty",
38
- a: Div().Body(
39
- Range([]string{"hello", "world"}).Slice(func(i int) UI {
40
- src := []string{"hello", "world"}
41
- return Text(src[i])
42
- }),
43
- ),
44
- b: Div().Body(
45
- Range([]string{}).Slice(func(i int) UI {
46
- src := []string{"hello", "maxoo"}
47
- return Text(src[i])
48
- }),
49
- ),
50
- matches: []TestUIDescriptor{
51
- {
52
- Path: TestPath(),
53
- Expected: Div(),
54
- },
55
- {
56
- Path: TestPath(0),
57
- Expected: nil,
58
- },
59
- {
60
- Path: TestPath(1),
61
- Expected: nil,
62
- },
63
- },
64
- },
65
- {
66
- scenario: "range map is updated",
67
- a: Div().Body(
68
- Range(map[string]string{"key": "value"}).Map(func(k string) UI {
69
- src := map[string]string{"key": "value"}
70
- return Text(src[k])
71
- }),
72
- ),
73
- b: Div().Body(
74
- Range(map[string]string{"key": "value"}).Map(func(k string) UI {
75
- src := map[string]string{"key": "maxoo"}
76
- return Text(src[k])
77
- }),
78
- ),
79
- matches: []TestUIDescriptor{
80
- {
81
- Path: TestPath(),
82
- Expected: Div(),
83
- },
84
- {
85
- Path: TestPath(0),
86
- Expected: Text("maxoo"),
87
- },
88
- },
89
- },
90
- })
91
- }
5
+ // func TestRange(t *testing.T) {
6
+ // testUpdate(t, []updateTest{
7
+ // {
8
+ // scenario: "range slice is updated",
9
+ // a: Div().Body(
10
+ // Range([]string{"hello", "world"}).Slice(func(i int) UI {
11
+ // src := []string{"hello", "world"}
12
+ // return Text(src[i])
13
+ // }),
14
+ // ),
15
+ // b: Div().Body(
16
+ // Range([]string{"hello", "maxoo"}).Slice(func(i int) UI {
17
+ // src := []string{"hello", "maxoo"}
18
+ // return Text(src[i])
19
+ // }),
20
+ // ),
21
+ // matches: []TestUIDescriptor{
22
+ // {
23
+ // Path: TestPath(),
24
+ // Expected: Div(),
25
+ // },
26
+ // {
27
+ // Path: TestPath(0),
28
+ // Expected: Text("hello"),
29
+ // },
30
+ // {
31
+ // Path: TestPath(1),
32
+ // Expected: Text("maxoo"),
33
+ // },
34
+ // },
35
+ // },
36
+ // {
37
+ // scenario: "range slice is updated to be empty",
38
+ // a: Div().Body(
39
+ // Range([]string{"hello", "world"}).Slice(func(i int) UI {
40
+ // src := []string{"hello", "world"}
41
+ // return Text(src[i])
42
+ // }),
43
+ // ),
44
+ // b: Div().Body(
45
+ // Range([]string{}).Slice(func(i int) UI {
46
+ // src := []string{"hello", "maxoo"}
47
+ // return Text(src[i])
48
+ // }),
49
+ // ),
50
+ // matches: []TestUIDescriptor{
51
+ // {
52
+ // Path: TestPath(),
53
+ // Expected: Div(),
54
+ // },
55
+ // {
56
+ // Path: TestPath(0),
57
+ // Expected: nil,
58
+ // },
59
+ // {
60
+ // Path: TestPath(1),
61
+ // Expected: nil,
62
+ // },
63
+ // },
64
+ // },
65
+ // {
66
+ // scenario: "range map is updated",
67
+ // a: Div().Body(
68
+ // Range(map[string]string{"key": "value"}).Map(func(k string) UI {
69
+ // src := map[string]string{"key": "value"}
70
+ // return Text(src[k])
71
+ // }),
72
+ // ),
73
+ // b: Div().Body(
74
+ // Range(map[string]string{"key": "value"}).Map(func(k string) UI {
75
+ // src := map[string]string{"key": "maxoo"}
76
+ // return Text(src[k])
77
+ // }),
78
+ // ),
79
+ // matches: []TestUIDescriptor{
80
+ // {
81
+ // Path: TestPath(),
82
+ // Expected: Div(),
83
+ // },
84
+ // {
85
+ // Path: TestPath(0),
86
+ // Expected: Text("maxoo"),
87
+ // },
88
+ // },
89
+ // },
90
+ // })
91
+ // }
raw_test.go CHANGED
@@ -1,116 +1,116 @@
1
1
  package app
2
2
 
3
- import (
3
+ // import (
4
- "testing"
4
+ // "testing"
5
5
 
6
- "github.com/stretchr/testify/require"
6
+ // "github.com/stretchr/testify/require"
7
- )
7
+ // )
8
8
 
9
- func TestRawRootTagName(t *testing.T) {
9
+ // func TestRawRootTagName(t *testing.T) {
10
- tests := []struct {
10
+ // tests := []struct {
11
- scenario string
11
+ // scenario string
12
- raw string
12
+ // raw string
13
- expected string
13
+ // expected string
14
- }{
14
+ // }{
15
- {
15
+ // {
16
- scenario: "tag set",
16
+ // scenario: "tag set",
17
- raw: `
17
+ // raw: `
18
- <div>
18
+ // <div>
19
- <span></span>
19
+ // <span></span>
20
- </div>`,
20
+ // </div>`,
21
- expected: "div",
21
+ // expected: "div",
22
- },
22
+ // },
23
- {
23
+ // {
24
- scenario: "tag is empty",
24
+ // scenario: "tag is empty",
25
- },
25
+ // },
26
- {
26
+ // {
27
- scenario: "opening tag missing",
27
+ // scenario: "opening tag missing",
28
- raw: "</div>",
28
+ // raw: "</div>",
29
- },
29
+ // },
30
- {
30
+ // {
31
- scenario: "tag is not set",
31
+ // scenario: "tag is not set",
32
- raw: "div",
32
+ // raw: "div",
33
- },
33
+ // },
34
- {
34
+ // {
35
- scenario: "tag is not closing",
35
+ // scenario: "tag is not closing",
36
- raw: "<div",
36
+ // raw: "<div",
37
- },
37
+ // },
38
- {
38
+ // {
39
- scenario: "tag is not closing",
39
+ // scenario: "tag is not closing",
40
- raw: "<div",
40
+ // raw: "<div",
41
- },
41
+ // },
42
- {
42
+ // {
43
- scenario: "tag without value",
43
+ // scenario: "tag without value",
44
- raw: "<>",
44
+ // raw: "<>",
45
- },
45
+ // },
46
- }
46
+ // }
47
47
 
48
- for _, test := range tests {
48
+ // for _, test := range tests {
49
- t.Run(test.scenario, func(t *testing.T) {
49
+ // t.Run(test.scenario, func(t *testing.T) {
50
- tag := rawRootTagName(test.raw)
50
+ // tag := rawRootTagName(test.raw)
51
- require.Equal(t, test.expected, tag)
51
+ // require.Equal(t, test.expected, tag)
52
- })
52
+ // })
53
+ // }
53
- }
54
+ // }
54
- }
55
55
 
56
- func TestRawMountDismount(t *testing.T) {
56
+ // func TestRawMountDismount(t *testing.T) {
57
- testMountDismount(t, []mountTest{
57
+ // testMountDismount(t, []mountTest{
58
- {
58
+ // {
59
- scenario: "raw html element",
59
+ // scenario: "raw html element",
60
- node: Raw(`<h1>Hello</h1>`),
60
+ // node: Raw(`<h1>Hello</h1>`),
61
- },
61
+ // },
62
- {
62
+ // {
63
- scenario: "raw svg element",
63
+ // scenario: "raw svg element",
64
- node: Raw(`<svg></svg>`),
64
+ // node: Raw(`<svg></svg>`),
65
- },
65
+ // },
66
- })
66
+ // })
67
- }
67
+ // }
68
68
 
69
- func TestRawUpdate(t *testing.T) {
69
+ // func TestRawUpdate(t *testing.T) {
70
- testUpdate(t, []updateTest{
70
+ // testUpdate(t, []updateTest{
71
- {
71
+ // {
72
- scenario: "raw html element returns replace error when updated with a non text-element",
72
+ // scenario: "raw html element returns replace error when updated with a non text-element",
73
- a: Raw("<svg></svg>"),
73
+ // a: Raw("<svg></svg>"),
74
- b: Div(),
74
+ // b: Div(),
75
- replaceErr: true,
75
+ // replaceErr: true,
76
- },
76
+ // },
77
- {
77
+ // {
78
- scenario: "raw html element is replace by another raw html element",
78
+ // scenario: "raw html element is replace by another raw html element",
79
- a: Div().Body(
79
+ // a: Div().Body(
80
- Raw("<div></div>"),
80
+ // Raw("<div></div>"),
81
- ),
81
+ // ),
82
- b: Div().Body(
82
+ // b: Div().Body(
83
- Raw("<svg></svg>"),
84
- ),
85
- matches: []TestUIDescriptor{
86
- {
87
- Path: TestPath(),
88
- Expected: Div(),
89
- },
90
- {
91
- Path: TestPath(0),
92
- Expected: Raw("<svg></svg>"),
83
+ // Raw("<svg></svg>"),
84
+ // ),
85
+ // matches: []TestUIDescriptor{
86
+ // {
87
+ // Path: TestPath(),
88
+ // Expected: Div(),
89
+ // },
90
+ // {
91
+ // Path: TestPath(0),
92
+ // Expected: Raw("<svg></svg>"),
93
+ // },
94
+ // },
93
- },
95
+ // },
94
- },
95
- },
96
- {
96
+ // {
97
- scenario: "raw html element is replace by non-raw html element",
97
+ // scenario: "raw html element is replace by non-raw html element",
98
- a: Div().Body(
98
+ // a: Div().Body(
99
- Raw("<div></div>"),
99
+ // Raw("<div></div>"),
100
- ),
100
+ // ),
101
- b: Div().Body(
101
+ // b: Div().Body(
102
- Text("hello"),
103
- ),
104
- matches: []TestUIDescriptor{
105
- {
106
- Path: TestPath(),
107
- Expected: Div(),
108
- },
109
- {
110
- Path: TestPath(0),
111
- Expected: Text("hello"),
102
+ // Text("hello"),
103
+ // ),
104
+ // matches: []TestUIDescriptor{
105
+ // {
106
+ // Path: TestPath(),
107
+ // Expected: Div(),
108
+ // },
109
+ // {
110
+ // Path: TestPath(0),
111
+ // Expected: Text("hello"),
112
+ // },
113
+ // },
112
- },
114
+ // },
113
- },
114
- },
115
- })
115
+ // })
116
- }
116
+ // }
text_test.go CHANGED
@@ -1,103 +1,101 @@
1
1
  package app
2
2
 
3
+ // func TestTextMountDismout(t *testing.T) {
4
+ // testMountDismount(t, []mountTest{
5
+ // {
3
- import "testing"
6
+ // scenario: "text",
7
+ // node: Text("hello"),
8
+ // },
9
+ // })
10
+ // }
4
11
 
5
- func TestTextMountDismout(t *testing.T) {
12
+ // func TestTextUpdate(t *testing.T) {
6
- testMountDismount(t, []mountTest{
13
+ // testUpdate(t, []updateTest{
7
- {
14
+ // {
15
+ // scenario: "text element returns replace error when updated with a non text-element",
16
+ // a: Text("hello"),
17
+ // b: Div(),
18
+ // replaceErr: true,
19
+ // },
20
+ // {
8
- scenario: "text",
21
+ // scenario: "text element is updated",
9
- node: Text("hello"),
22
+ // a: Text("hello"),
23
+ // b: Text("world"),
24
+ // matches: []TestUIDescriptor{
25
+ // {
26
+ // Expected: Text("world"),
27
+ // },
28
+ // },
10
- },
29
+ // },
11
- })
12
- }
13
30
 
14
- func TestTextUpdate(t *testing.T) {
15
- testUpdate(t, []updateTest{
16
- {
17
- scenario: "text element returns replace error when updated with a non text-element",
18
- a: Text("hello"),
19
- b: Div(),
20
- replaceErr: true,
21
- },
22
- {
23
- scenario: "text element is updated",
24
- a: Text("hello"),
25
- b: Text("world"),
26
- matches: []TestUIDescriptor{
27
- {
28
- Expected: Text("world"),
29
- },
30
- },
31
- },
32
-
33
- {
34
- scenario: "text is replaced by a html elem",
35
- a: Div().Body(
36
- Text("hello"),
37
- ),
38
- b: Div().Body(
39
- H2().Text("hello"),
40
- ),
41
- matches: []TestUIDescriptor{
42
- {
43
- Path: TestPath(),
44
- Expected: Div(),
45
- },
46
- {
47
- Path: TestPath(0),
48
- Expected: H2(),
49
- },
50
- {
51
- Path: TestPath(0, 0),
52
- Expected: Text("hello"),
53
- },
54
- },
55
- },
56
- {
57
- scenario: "text is replaced by a component",
58
- a: Div().Body(
59
- Text("hello"),
60
- ),
61
- // b: Div().Body(
62
- // &hello{},
63
- // ),
64
- matches: []TestUIDescriptor{
65
- {
66
- Path: TestPath(),
67
- Expected: Div(),
68
- },
69
- // {
70
- // Path: TestPath(0),
71
- // Expected: &hello{},
72
- // },
73
- {
74
- Path: TestPath(0, 0, 0),
75
- Expected: H1(),
76
- },
77
- {
78
- Path: TestPath(0, 0, 0, 0),
79
- Expected: Text("hello, "),
80
- },
81
- },
82
- },
83
- {
84
- scenario: "text is replaced by a raw html element",
85
- a: Div().Body(
86
- Text("hello"),
87
- ),
88
- b: Div().Body(
89
- Raw("<svg></svg>"),
90
- ),
91
- matches: []TestUIDescriptor{
92
- {
93
- Path: TestPath(),
94
- Expected: Div(),
95
- },
96
- {
97
- Path: TestPath(0),
98
- Expected: Raw("<svg></svg>"),
99
- },
100
- },
101
- },
102
- })
103
- }
31
+ // {
32
+ // scenario: "text is replaced by a html elem",
33
+ // a: Div().Body(
34
+ // Text("hello"),
35
+ // ),
36
+ // b: Div().Body(
37
+ // H2().Text("hello"),
38
+ // ),
39
+ // matches: []TestUIDescriptor{
40
+ // {
41
+ // Path: TestPath(),
42
+ // Expected: Div(),
43
+ // },
44
+ // {
45
+ // Path: TestPath(0),
46
+ // Expected: H2(),
47
+ // },
48
+ // {
49
+ // Path: TestPath(0, 0),
50
+ // Expected: Text("hello"),
51
+ // },
52
+ // },
53
+ // },
54
+ // {
55
+ // scenario: "text is replaced by a component",
56
+ // a: Div().Body(
57
+ // Text("hello"),
58
+ // ),
59
+ // // b: Div().Body(
60
+ // // &hello{},
61
+ // // ),
62
+ // matches: []TestUIDescriptor{
63
+ // {
64
+ // Path: TestPath(),
65
+ // Expected: Div(),
66
+ // },
67
+ // // {
68
+ // // Path: TestPath(0),
69
+ // // Expected: &hello{},
70
+ // // },
71
+ // {
72
+ // Path: TestPath(0, 0, 0),
73
+ // Expected: H1(),
74
+ // },
75
+ // {
76
+ // Path: TestPath(0, 0, 0, 0),
77
+ // Expected: Text("hello, "),
78
+ // },
79
+ // },
80
+ // },
81
+ // {
82
+ // scenario: "text is replaced by a raw html element",
83
+ // a: Div().Body(
84
+ // Text("hello"),
85
+ // ),
86
+ // b: Div().Body(
87
+ // Raw("<svg></svg>"),
88
+ // ),
89
+ // matches: []TestUIDescriptor{
90
+ // {
91
+ // Path: TestPath(),
92
+ // Expected: Div(),
93
+ // },
94
+ // {
95
+ // Path: TestPath(0),
96
+ // Expected: Raw("<svg></svg>"),
97
+ // },
98
+ // },
99
+ // },
100
+ // })
101
+ // }