dca74ba5
—
Ash Guy 5 years ago
[Fix] Elements Ordering & Struct Field Visibility (#17)
- .gitignore +1 -0
- README.md +34 -7
- render_macros/src/children.rs +5 -2
- render_macros/src/function_component.rs +5 -1
- render_tests/src/lib.rs +83 -0
.gitignore
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
target
|
|
2
2
|
**/*.rs.bk
|
|
3
|
+
.DS_Store
|
README.md
CHANGED
|
@@ -11,9 +11,9 @@ XML rendering, but can work with other usages as well, like ReasonML's [`Pastel`
|
|
|
11
11
|
A renderable component is a struct that implements the `Render` trait. There
|
|
12
12
|
are multiple macros that provide a better experience implementing Renderable:
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
- `#[component]` for defining components using a function
|
|
15
|
-
|
|
15
|
+
- `rsx!` for composing elements with JSX ergonomics
|
|
16
|
-
|
|
16
|
+
- `html!` for composing elements and render them to a string
|
|
17
17
|
|
|
18
18
|
## Why is this different from...
|
|
19
19
|
|
|
@@ -113,10 +113,37 @@ assert_eq!(rendered_html, r#"<h1 class="title">Hello world!</h1>"#);
|
|
|
113
113
|
|
|
114
114
|
If you pay close attention, you see that the function `Heading` is:
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
- declared with an uppercase. Underneath, it generates a struct with the same name, and
|
|
117
|
-
implements the `Render` trait on it.
|
|
117
|
+
implements the `Render` trait on it.
|
|
118
|
-
|
|
118
|
+
- does not have a return type. This is because everything is written to a writer, for
|
|
119
|
-
performance reasons.
|
|
119
|
+
performance reasons.
|
|
120
|
+
|
|
121
|
+
### Visibility & Component Libraries
|
|
122
|
+
|
|
123
|
+
Often you're going to want to store your components somewhere else in your
|
|
124
|
+
project tree other than the module you're working on (if not in a different
|
|
125
|
+
module entirely!). In these cases, the visibility applied top the function that
|
|
126
|
+
defines your component will flow down into all fields of that struct.
|
|
127
|
+
|
|
128
|
+
For example, if we add "pub" to the front of our Heading component above:
|
|
129
|
+
|
|
130
|
+
```rust
|
|
131
|
+
#[component]
|
|
132
|
+
pub fn Heading<'title>(title: &'title str) {
|
|
133
|
+
rsx! { <h1 class={"title"}>{title}</h1> }
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
...the struct that is generated would look something like...
|
|
138
|
+
|
|
139
|
+
```rust
|
|
140
|
+
pub struct Heading {
|
|
141
|
+
pub title: &'title str
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
This is important to understand from a safety point of view when structuring
|
|
146
|
+
your libraries.
|
|
120
147
|
|
|
121
148
|
#### Full example
|
|
122
149
|
|
render_macros/src/children.rs
CHANGED
|
@@ -24,16 +24,19 @@ impl Children {
|
|
|
24
24
|
quote! { #child }
|
|
25
25
|
})
|
|
26
26
|
.collect();
|
|
27
|
+
|
|
27
28
|
match children_quotes.len() {
|
|
28
29
|
0 => quote! { Option::<()>::None },
|
|
29
|
-
1 => quote! { Some(#(#children_quotes)*) },
|
|
30
|
+
1 => quote! { Some(#(#children_quotes),*) },
|
|
30
31
|
_ => {
|
|
31
32
|
let mut iter = children_quotes.iter();
|
|
33
|
+
|
|
32
34
|
let first = iter.next().unwrap();
|
|
33
35
|
let second = iter.next().unwrap();
|
|
36
|
+
|
|
34
37
|
let tuple_of_tuples = iter.fold(
|
|
35
38
|
quote!((#first, #second)),
|
|
36
|
-
|renderable, current| quote!((#
|
|
39
|
+
|renderable, current| quote!((#renderable, #current)),
|
|
37
40
|
);
|
|
38
41
|
|
|
39
42
|
quote! { Some(#tuple_of_tuples) }
|
render_macros/src/function_component.rs
CHANGED
|
@@ -10,7 +10,11 @@ pub fn create_function_component(f: syn::ItemFn) -> TokenStream {
|
|
|
10
10
|
let vis = f.vis;
|
|
11
11
|
|
|
12
12
|
let inputs_block = if inputs.len() > 0 {
|
|
13
|
+
let input_names: Vec<_> = inputs
|
|
14
|
+
.iter()
|
|
15
|
+
.collect();
|
|
16
|
+
|
|
13
|
-
quote!({ #
|
|
17
|
+
quote!({ #(#vis #input_names),* })
|
|
14
18
|
} else {
|
|
15
19
|
quote!(;)
|
|
16
20
|
};
|
render_tests/src/lib.rs
CHANGED
|
@@ -20,6 +20,36 @@ pub fn works_with_raw() {
|
|
|
20
20
|
assert_eq!(actual, "<div><Hello /></div>");
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
#[test]
|
|
24
|
+
pub fn element_ordering() {
|
|
25
|
+
use pretty_assertions::assert_eq;
|
|
26
|
+
use render::{html, raw};
|
|
27
|
+
|
|
28
|
+
let actual = html! {
|
|
29
|
+
<ul>
|
|
30
|
+
<li>{"1"}</li>
|
|
31
|
+
<li>{"2"}</li>
|
|
32
|
+
<li>{"3"}</li>
|
|
33
|
+
</ul>
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
assert_eq!(actual, "<ul><li>1</li><li>2</li><li>3</li></ul>");
|
|
37
|
+
|
|
38
|
+
let deep = html! {
|
|
39
|
+
<div>
|
|
40
|
+
<h1>{"A list"}</h1>
|
|
41
|
+
<hr />
|
|
42
|
+
<ul>
|
|
43
|
+
<li>{"1"}</li>
|
|
44
|
+
<li>{"2"}</li>
|
|
45
|
+
<li>{"3"}</li>
|
|
46
|
+
</ul>
|
|
47
|
+
</div>
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
assert_eq!(deep, "<div><h1>A list</h1><hr/><ul><li>1</li><li>2</li><li>3</li></ul></div>");
|
|
51
|
+
}
|
|
52
|
+
|
|
23
53
|
mod kaki {
|
|
24
54
|
// A simple HTML 5 doctype declaration
|
|
25
55
|
use render::html::HTML5Doctype;
|
|
@@ -67,4 +97,57 @@ mod kaki {
|
|
|
67
97
|
);
|
|
68
98
|
assert_eq!(actual, expected);
|
|
69
99
|
}
|
|
100
|
+
|
|
101
|
+
#[test]
|
|
102
|
+
fn externals_test() {
|
|
103
|
+
use pretty_assertions::assert_eq;
|
|
104
|
+
use crate::other::ExternalPage;
|
|
105
|
+
|
|
106
|
+
let actual = render::html! {
|
|
107
|
+
<ExternalPage title={"Home"} subtitle={"Foo"}>
|
|
108
|
+
{format!("Welcome, {}", "Gal")}
|
|
109
|
+
</ExternalPage>
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
let expected = concat!(
|
|
113
|
+
"<!DOCTYPE html>",
|
|
114
|
+
"<html>",
|
|
115
|
+
"<head><title>Home</title></head>",
|
|
116
|
+
"<body>",
|
|
117
|
+
"<h1>Foo</h1>",
|
|
118
|
+
"Welcome, Gal",
|
|
119
|
+
"</body>",
|
|
120
|
+
"</html>"
|
|
121
|
+
);
|
|
122
|
+
assert_eq!(actual, expected);
|
|
123
|
+
}
|
|
70
124
|
}
|
|
125
|
+
|
|
126
|
+
/// ## Other
|
|
127
|
+
///
|
|
128
|
+
/// Module for testing component visibility when imported from other modules.
|
|
129
|
+
|
|
130
|
+
mod other {
|
|
131
|
+
use render::html::HTML5Doctype;
|
|
132
|
+
use render::{ component, rsx, Render };
|
|
133
|
+
|
|
134
|
+
#[component]
|
|
135
|
+
pub fn ExternalPage<'title, 'subtitle, Children: Render>(
|
|
136
|
+
title: &'title str,
|
|
137
|
+
subtitle: &'subtitle str,
|
|
138
|
+
children: Children
|
|
139
|
+
) {
|
|
140
|
+
rsx! {
|
|
141
|
+
<>
|
|
142
|
+
<HTML5Doctype />
|
|
143
|
+
<html>
|
|
144
|
+
<head><title>{title}</title></head>
|
|
145
|
+
<body>
|
|
146
|
+
<h1>{subtitle}</h1>
|
|
147
|
+
{children}
|
|
148
|
+
</body>
|
|
149
|
+
</html>
|
|
150
|
+
</>
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|