Skip to content

Commit 0fde6cf

Browse files
fix: allow Option<T> and &T as VNode children in html! macro (#4005)
Fixed a regression that made it no longer possible to use types like `&AttrValue` and `Option<AttrValue>` as children
1 parent 1e0e1a8 commit 0fde6cf

6 files changed

Lines changed: 84 additions & 33 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/yew-macro/tests/html_macro/component-fail.stderr

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -488,14 +488,14 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfie
488488
| required by a bound introduced by this call
489489
|
490490
= help: the following other types implement trait `IntoPropValue<T>`:
491-
f32
492-
f64
493-
i128
494-
i16
495-
i32
496-
i64
497-
i8
498-
isize
491+
&f32
492+
&f64
493+
&i128
494+
&i16
495+
&i32
496+
&i64
497+
&i8
498+
&isize
499499
and $N others
500500
note: required by a bound in `ChildPropertiesBuilder::string`
501501
--> tests/html_macro/component-fail.rs:4:17
@@ -516,14 +516,14 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfie
516516
| required by a bound introduced by this call
517517
|
518518
= help: the following other types implement trait `IntoPropValue<T>`:
519-
f32
520-
f64
521-
i128
522-
i16
523-
i32
524-
i64
525-
i8
526-
isize
519+
&f32
520+
&f64
521+
&i128
522+
&i16
523+
&i32
524+
&i64
525+
&i8
526+
&isize
527527
and $N others
528528
note: required by a bound in `ChildPropertiesBuilder::string`
529529
--> tests/html_macro/component-fail.rs:4:17
@@ -560,9 +560,9 @@ error[E0277]: the trait bound `u32: IntoPropValue<i32>` is not satisfied
560560
| |
561561
| required by a bound introduced by this call
562562
|
563-
= help: the trait `IntoPropValue<i32>` is not implemented for `u32`
564-
but trait `IntoPropValue<VNode>` is implemented for it
565-
= help: for that trait implementation, expected `VNode`, found `i32`
563+
= help: the following other types implement trait `IntoPropValue<T>`:
564+
&u32
565+
u32
566566
note: required by a bound in `ChildPropertiesBuilder::int`
567567
--> tests/html_macro/component-fail.rs:4:17
568568
|

packages/yew-macro/tests/html_macro/element-fail.stderr

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -465,14 +465,14 @@ error[E0277]: the trait bound `NotToString: IntoPropValue<Option<implicit_clone:
465465
| ^^^^^^^^^^^ the trait `IntoPropValue<Option<implicit_clone::unsync::string::IString>>` is not implemented for `NotToString`
466466
|
467467
= help: the following other types implement trait `IntoPropValue<T>`:
468+
`&&String` implements `IntoPropValue<VNode>`
469+
`&&str` implements `IntoPropValue<VNode>`
468470
`&'static [(K, V)]` implements `IntoPropValue<implicit_clone::unsync::map::IMap<K, V>>`
469471
`&'static [T]` implements `IntoPropValue<implicit_clone::unsync::array::IArray<T>>`
470472
`&'static str` implements `IntoPropValue<Classes>`
471473
`&'static str` implements `IntoPropValue<Option<String>>`
472474
`&'static str` implements `IntoPropValue<Option<implicit_clone::unsync::string::IString>>`
473475
`&'static str` implements `IntoPropValue<String>`
474-
`&'static str` implements `IntoPropValue<implicit_clone::unsync::string::IString>`
475-
`&ChildrenRenderer<VNode>` implements `IntoPropValue<VNode>`
476476
and $N others
477477

478478
error[E0277]: the trait bound `Option<NotToString>: IntoPropValue<Option<implicit_clone::unsync::string::IString>>` is not satisfied
@@ -491,8 +491,8 @@ error[E0277]: the trait bound `Option<NotToString>: IntoPropValue<Option<implici
491491
`Option<F>` implements `IntoPropValue<Option<yew::Callback<I, O>>>`
492492
`Option<Rc<str>>` implements `IntoPropValue<Option<implicit_clone::unsync::string::IString>>`
493493
`Option<String>` implements `IntoPropValue<Option<implicit_clone::unsync::string::IString>>`
494+
`Option<T>` implements `IntoPropValue<VNode>`
494495
`Option<VChild<T>>` implements `IntoPropValue<Option<ChildrenRenderer<C>>>`
495-
`Option<VNode>` implements `IntoPropValue<VNode>`
496496

497497
error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<implicit_clone::unsync::string::IString>>` is not satisfied
498498
--> tests/html_macro/element-fail.rs:48:22
@@ -510,8 +510,8 @@ error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<implicit_
510510
`Option<F>` implements `IntoPropValue<Option<yew::Callback<I, O>>>`
511511
`Option<Rc<str>>` implements `IntoPropValue<Option<implicit_clone::unsync::string::IString>>`
512512
`Option<String>` implements `IntoPropValue<Option<implicit_clone::unsync::string::IString>>`
513+
`Option<T>` implements `IntoPropValue<VNode>`
513514
`Option<VChild<T>>` implements `IntoPropValue<Option<ChildrenRenderer<C>>>`
514-
`Option<VNode>` implements `IntoPropValue<VNode>`
515515

516516
error[E0277]: the trait bound `{integer}: IntoEventCallback<MouseEvent>` is not satisfied
517517
--> tests/html_macro/element-fail.rs:51:28
@@ -627,8 +627,8 @@ error[E0277]: the trait bound `Option<yew::NodeRef>: IntoPropValue<yew::NodeRef>
627627
`Option<F>` implements `IntoPropValue<Option<yew::Callback<I, O>>>`
628628
`Option<Rc<str>>` implements `IntoPropValue<Option<implicit_clone::unsync::string::IString>>`
629629
`Option<String>` implements `IntoPropValue<Option<implicit_clone::unsync::string::IString>>`
630+
`Option<T>` implements `IntoPropValue<VNode>`
630631
`Option<VChild<T>>` implements `IntoPropValue<Option<ChildrenRenderer<C>>>`
631-
`Option<VNode>` implements `IntoPropValue<VNode>`
632632

633633
error[E0277]: the trait bound `yew::Callback<String>: IntoEventCallback<MouseEvent>` is not satisfied
634634
--> tests/html_macro/element-fail.rs:58:29
@@ -665,14 +665,14 @@ error[E0277]: the trait bound `NotToString: IntoPropValue<Option<implicit_clone:
665665
| ^^^^^^^^^^^ the trait `IntoPropValue<Option<implicit_clone::unsync::string::IString>>` is not implemented for `NotToString`
666666
|
667667
= help: the following other types implement trait `IntoPropValue<T>`:
668+
`&&String` implements `IntoPropValue<VNode>`
669+
`&&str` implements `IntoPropValue<VNode>`
668670
`&'static [(K, V)]` implements `IntoPropValue<implicit_clone::unsync::map::IMap<K, V>>`
669671
`&'static [T]` implements `IntoPropValue<implicit_clone::unsync::array::IArray<T>>`
670672
`&'static str` implements `IntoPropValue<Classes>`
671673
`&'static str` implements `IntoPropValue<Option<String>>`
672674
`&'static str` implements `IntoPropValue<Option<implicit_clone::unsync::string::IString>>`
673675
`&'static str` implements `IntoPropValue<String>`
674-
`&'static str` implements `IntoPropValue<implicit_clone::unsync::string::IString>`
675-
`&ChildrenRenderer<VNode>` implements `IntoPropValue<VNode>`
676676
and $N others
677677

678678
error[E0277]: the trait bound `(): IntoPropValue<yew::NodeRef>` is not satisfied

packages/yew-macro/tests/html_macro/html-element-pass.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,26 @@ fn compile_pass() {
123123

124124
let option_vnode = ::std::option::Option::Some(::yew::html! {});
125125
_ = ::yew::html! { <div>{option_vnode}</div> };
126+
127+
let opt_string: ::std::option::Option<::std::string::String> =
128+
::std::option::Option::Some(::std::convert::From::from("hello"));
129+
_ = ::yew::html! { <div>{opt_string}</div> };
130+
131+
let opt_attr: ::std::option::Option<::yew::AttrValue> =
132+
::std::option::Option::Some(::yew::AttrValue::Static("hello"));
133+
_ = ::yew::html! { <div>{opt_attr}</div> };
134+
135+
let opt_none: ::std::option::Option<::std::string::String> = ::std::option::Option::None;
136+
_ = ::yew::html! { <div>{opt_none}</div> };
137+
138+
let num = 42i32;
139+
_ = ::yew::html! { <div>{&num}</div> };
140+
141+
let text = ::std::string::String::new();
142+
_ = ::yew::html! { <div>{&text}</div> };
143+
144+
let sr: &::core::primitive::str = "hello";
145+
_ = ::yew::html! { <div>{&sr}</div> };
126146
}
127147

128148
fn main() {}

packages/yew/src/html/conversion/into_prop_value.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,10 @@ impl IntoPropValue<VNode> for Vec<VNode> {
214214
}
215215
}
216216

217-
impl IntoPropValue<VNode> for Option<VNode> {
217+
impl<T: IntoPropValue<VNode>> IntoPropValue<VNode> for Option<T> {
218218
#[inline]
219219
fn into_prop_value(self) -> VNode {
220-
self.unwrap_or_default()
220+
self.map(IntoPropValue::into_prop_value).unwrap_or_default()
221221
}
222222
}
223223

@@ -292,6 +292,12 @@ macro_rules! impl_into_prop_value_via_display {
292292
VText::from(self).into()
293293
}
294294
}
295+
impl IntoPropValue<VNode> for &$from_ty {
296+
#[inline(always)]
297+
fn into_prop_value(self) -> VNode {
298+
self.clone().into_prop_value()
299+
}
300+
}
295301
};
296302
}
297303

@@ -350,6 +356,31 @@ mod test {
350356
let _: Option<AttrValue> = Cow::Borrowed("foo").into_prop_value();
351357
}
352358

359+
#[test]
360+
fn test_option_to_vnode() {
361+
let _: VNode = Some(String::from("hello")).into_prop_value();
362+
let _: VNode = Some(AttrValue::Static("hello")).into_prop_value();
363+
let _: VNode = Option::<String>::None.into_prop_value();
364+
let _: VNode = Option::<AttrValue>::None.into_prop_value();
365+
let _: VNode = Some(VNode::default()).into_prop_value();
366+
let _: VNode = Option::<VNode>::None.into_prop_value();
367+
let _: VNode = Some(42u32).into_prop_value();
368+
let _: VNode = Some(true).into_prop_value();
369+
}
370+
371+
#[test]
372+
fn test_ref_to_vnode() {
373+
let _: VNode = (&42i32).into_prop_value();
374+
let _: VNode = (&true).into_prop_value();
375+
let _: VNode = (&1.5f64).into_prop_value();
376+
let s = String::from("hello");
377+
let _: VNode = (&s).into_prop_value();
378+
let a = AttrValue::Static("hello");
379+
let _: VNode = (&a).into_prop_value();
380+
let sr: &str = "hello";
381+
let _: VNode = (&sr).into_prop_value();
382+
}
383+
353384
#[test]
354385
fn test_callback() {
355386
let _: Callback<String> = (|_: String| ()).into_prop_value();

website/docs/advanced-topics/children.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ impl Component for Page {
287287
fn view(&self, ctx: &Context<Self>) -> Html {
288288
html! {
289289
<div class="page">
290-
{ ctx.props().sidebar.clone().map(Html::from).unwrap_or_default() }
290+
{ ctx.props().sidebar.clone() }
291291
// ... page content
292292
</div>
293293
}

0 commit comments

Comments
 (0)