Allow VNode props to be converted to Children. (#2749)

* Allow VNode props to be converted to Children.

* Also implement ChildrenWithProps as well.

* Update macro tests.
This commit is contained in:
Kaede Hoshikawa 2022-06-24 23:06:09 +09:00 committed by GitHub
parent 59beb2ad01
commit a5973bdca6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 175 additions and 8 deletions

View File

@ -349,7 +349,7 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfie
<&'static str as IntoPropValue<Classes>>
<&'static str as IntoPropValue<Option<AttrValue>>>
<&'static str as IntoPropValue<Option<String>>>
and 18 others
and 27 others
error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfied
--> tests/html_macro/component-fail.rs:79:34
@ -362,7 +362,7 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfie
<&'static str as IntoPropValue<Classes>>
<&'static str as IntoPropValue<Option<AttrValue>>>
<&'static str as IntoPropValue<Option<String>>>
and 18 others
and 27 others
error[E0308]: mismatched types
--> tests/html_macro/component-fail.rs:80:31

View File

@ -305,7 +305,7 @@ error[E0277]: the trait bound `Option<NotToString>: IntoPropValue<Option<AttrVal
<Option<&'static str> as IntoPropValue<Option<String>>>
<Option<F> as IntoPropValue<Option<yew::Callback<I, O>>>>
<Option<String> as IntoPropValue<Option<AttrValue>>>
<Option<std::rc::Rc<str>> as IntoPropValue<Option<AttrValue>>>
and 4 others
note: required by `into_prop_value`
--> $WORKSPACE/packages/yew/src/html/conversion.rs
|
@ -323,7 +323,7 @@ error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<AttrValue
<Option<&'static str> as IntoPropValue<Option<String>>>
<Option<F> as IntoPropValue<Option<yew::Callback<I, O>>>>
<Option<String> as IntoPropValue<Option<AttrValue>>>
<Option<std::rc::Rc<str>> as IntoPropValue<Option<AttrValue>>>
and 4 others
note: required by `into_prop_value`
--> $WORKSPACE/packages/yew/src/html/conversion.rs
|
@ -420,7 +420,7 @@ error[E0277]: the trait bound `Option<yew::NodeRef>: IntoPropValue<yew::NodeRef>
<Option<&'static str> as IntoPropValue<Option<String>>>
<Option<F> as IntoPropValue<Option<yew::Callback<I, O>>>>
<Option<String> as IntoPropValue<Option<AttrValue>>>
<Option<std::rc::Rc<str>> as IntoPropValue<Option<AttrValue>>>
and 4 others
note: required by `into_prop_value`
--> $WORKSPACE/packages/yew/src/html/conversion.rs
|

View File

@ -1,8 +1,8 @@
use std::rc::Rc;
use super::super::callback::Callback;
use super::{Component, NodeRef, Scope};
use crate::virtual_dom::AttrValue;
use super::{BaseComponent, Children, ChildrenRenderer, Component, NodeRef, Scope};
use crate::virtual_dom::{AttrValue, VChild, VNode};
/// Marker trait for types that the [`html!`](macro@crate::html) macro may clone implicitly.
pub trait ImplicitClone: Clone {}
@ -40,6 +40,7 @@ impl<T> IntoPropValue<T> for T {
self
}
}
impl<T> IntoPropValue<T> for &T
where
T: ImplicitClone,
@ -56,6 +57,7 @@ impl<T> IntoPropValue<Option<T>> for T {
Some(self)
}
}
impl<T> IntoPropValue<Option<T>> for &T
where
T: ImplicitClone,
@ -70,6 +72,7 @@ impl<I, O, F> IntoPropValue<Callback<I, O>> for F
where
F: 'static + Fn(I) -> O,
{
#[inline]
fn into_prop_value(self) -> Callback<I, O> {
Callback::from(self)
}
@ -79,6 +82,7 @@ impl<I, O, F> IntoPropValue<Option<Callback<I, O>>> for F
where
F: 'static + Fn(I) -> O,
{
#[inline]
fn into_prop_value(self) -> Option<Callback<I, O>> {
Some(Callback::from(self))
}
@ -88,8 +92,69 @@ impl<I, O, F> IntoPropValue<Option<Callback<I, O>>> for Option<F>
where
F: 'static + Fn(I) -> O,
{
#[inline]
fn into_prop_value(self) -> Option<Callback<I, O>> {
self.map(|f| Callback::from(f))
self.map(Callback::from)
}
}
impl<T> IntoPropValue<ChildrenRenderer<VChild<T>>> for VChild<T>
where
T: BaseComponent,
{
#[inline]
fn into_prop_value(self) -> ChildrenRenderer<VChild<T>> {
ChildrenRenderer::new(vec![self])
}
}
impl<T> IntoPropValue<Option<ChildrenRenderer<VChild<T>>>> for VChild<T>
where
T: BaseComponent,
{
#[inline]
fn into_prop_value(self) -> Option<ChildrenRenderer<VChild<T>>> {
Some(ChildrenRenderer::new(vec![self]))
}
}
impl<T> IntoPropValue<Option<ChildrenRenderer<VChild<T>>>> for Option<VChild<T>>
where
T: BaseComponent,
{
#[inline]
fn into_prop_value(self) -> Option<ChildrenRenderer<VChild<T>>> {
self.map(|m| ChildrenRenderer::new(vec![m]))
}
}
impl<T> IntoPropValue<ChildrenRenderer<VChild<T>>> for Vec<VChild<T>>
where
T: BaseComponent,
{
#[inline]
fn into_prop_value(self) -> ChildrenRenderer<VChild<T>> {
ChildrenRenderer::new(self)
}
}
impl<T> IntoPropValue<Option<ChildrenRenderer<VChild<T>>>> for Vec<VChild<T>>
where
T: BaseComponent,
{
#[inline]
fn into_prop_value(self) -> Option<ChildrenRenderer<VChild<T>>> {
Some(ChildrenRenderer::new(self))
}
}
impl<T> IntoPropValue<Option<ChildrenRenderer<VChild<T>>>> for Option<Vec<VChild<T>>>
where
T: BaseComponent,
{
#[inline]
fn into_prop_value(self) -> Option<ChildrenRenderer<VChild<T>>> {
self.map(ChildrenRenderer::new)
}
}
@ -127,6 +192,7 @@ impl_into_prop!(|value: &'static str| -> String { value.to_owned() });
impl_into_prop!(|value: &'static str| -> AttrValue { AttrValue::Static(value) });
impl_into_prop!(|value: String| -> AttrValue { AttrValue::Rc(Rc::from(value)) });
impl_into_prop!(|value: Rc<str>| -> AttrValue { AttrValue::Rc(value) });
impl_into_prop!(|value: VNode| -> Children { Children::new(vec![value]) });
#[cfg(test)]
mod test {
@ -150,4 +216,105 @@ mod test {
let _: Option<Callback<String, String>> = (|s: String| s).into_prop_value();
let _: Option<Callback<String, String>> = Some(|s: String| s).into_prop_value();
}
#[test]
fn test_html_to_children_compiles() {
use crate::prelude::*;
#[derive(Clone, Debug, PartialEq, Properties)]
pub struct Props {
#[prop_or_default]
pub header: Children,
#[prop_or_default]
pub children: Children,
#[prop_or_default]
pub footer: Children,
}
#[function_component]
pub fn App(props: &Props) -> Html {
let Props {
header,
children,
footer,
} = props.clone();
html! {
<div>
<header>
{header}
</header>
<main>
{children}
</main>
<footer>
{footer}
</footer>
</div>
}
}
let header = html! { <div>{"header"}</div> };
let footer = html! { <div>{"footer"}</div> };
let children = html! { <div>{"main"}</div> };
let _ = html! {
<App {header} {footer}>
{children}
</App>
};
}
#[test]
fn test_vchild_to_children_with_props_compiles() {
use crate::prelude::*;
#[function_component]
pub fn Comp() -> Html {
Html::default()
}
#[derive(Clone, Debug, PartialEq, Properties)]
pub struct Props {
#[prop_or_default]
pub header: ChildrenWithProps<Comp>,
#[prop_or_default]
pub children: Children,
#[prop_or_default]
pub footer: ChildrenWithProps<Comp>,
}
#[function_component]
pub fn App(props: &Props) -> Html {
let Props {
header,
children,
footer,
} = props.clone();
html! {
<div>
<header>
{header}
</header>
<main>
{children}
</main>
<footer>
{footer}
</footer>
</div>
}
}
let header = VChild::new((), NodeRef::default(), None);
let footer = html_nested! { <Comp /> };
let children = html! { <div>{"main"}</div> };
let _ = html! {
<App {header} {footer}>
{children}
</App>
};
}
}