chore: update tests and doctests

This commit is contained in:
Greg Johnston 2024-08-01 12:04:14 -04:00
parent 05cd1bc6f0
commit bb63a1521e
9 changed files with 203 additions and 282 deletions

View file

@ -34,6 +34,7 @@ log = "0.4"
typed-builder = "0.18"
trybuild = "1"
leptos = { path = "../leptos" }
server_fn = { path = "../server_fn", features = ["cbor"] }
insta = "1.29"
serde = "1"

View file

@ -27,48 +27,41 @@ mod slot;
///
/// 1. Text content should be provided as a Rust string, i.e., double-quoted:
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// view! { <p>"Heres some text"</p> };
/// # use leptos::prelude::*;
/// # fn test() -> impl IntoView {
/// view! { <p>"Heres some text"</p> }
/// # }
/// # runtime.dispose();
/// ```
///
/// 2. Self-closing tags need an explicit `/` as in XML/XHTML
/// ```rust,compile_fail
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
///
/// # fn test() -> impl IntoView {
/// // ❌ not like this
/// view! { <input type="text" name="name"> }
/// # ;
/// # }
/// # runtime.dispose();
/// ```
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
/// # fn test() -> impl IntoView {
/// // ✅ add that slash
/// view! { <input type="text" name="name" /> }
/// # ;
/// # }
/// # runtime.dispose();
/// ```
///
/// 3. Components (functions annotated with `#[component]`) can be inserted as camel-cased tags. (Generics
/// on components are specified as `<Component<T>/>`, not the turbofish `<Component::<T>/>`.)
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # use leptos::prelude::*;
///
/// # #[component]
/// # fn Counter(initial_value: i32) -> impl IntoView { view! { <p></p>} }
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # fn test() -> impl IntoView {
/// view! { <div><Counter initial_value=3 /></div> }
/// # ;
/// # }
/// # runtime.dispose();
/// ```
///
/// 4. Dynamic content can be wrapped in curly braces (`{ }`) to insert text nodes, elements, or set attributes.
@ -80,9 +73,9 @@ mod slot;
/// take an `Option`, in which case `Some` sets the attribute and `None` removes the attribute.
///
/// ```rust,ignore
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
///
/// # fn test() -> impl IntoView {
/// let (count, set_count) = create_signal(0);
///
/// view! {
@ -95,15 +88,13 @@ mod slot;
/// }
/// # ;
/// # };
/// # runtime.dispose();
/// ```
///
/// 5. Event handlers can be added with `on:` attributes. In most cases, the events are given the correct type
/// based on the event name.
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
/// # fn test() -> impl IntoView {
/// view! {
/// <button on:click=|ev| {
/// log::debug!("click event: {ev:#?}");
@ -111,18 +102,15 @@ mod slot;
/// "Click me"
/// </button>
/// }
/// # ;
/// # };
/// # runtime.dispose();
/// # }
/// ```
///
/// 6. DOM properties can be set with `prop:` attributes, which take any primitive type or `JsValue` (or a signal
/// that returns a primitive or JsValue). They can also take an `Option`, in which case `Some` sets the property
/// and `None` deletes the property.
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
/// # fn test() -> impl IntoView {
/// let (name, set_name) = create_signal("Alice".to_string());
///
/// view! {
@ -134,53 +122,41 @@ mod slot;
/// on:click=move |ev| set_name.set(event_target_value(&ev)) // `event_target_value` is a useful little Leptos helper
/// />
/// }
/// # ;
/// # };
/// # runtime.dispose();
/// # }
/// ```
///
/// 7. Classes can be toggled with `class:` attributes, which take a `bool` (or a signal that returns a `bool`).
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
/// # fn test() -> impl IntoView {
/// let (count, set_count) = create_signal(2);
/// view! { <div class:hidden-div={move || count.get() < 3}>"Now you see me, now you dont."</div> }
/// # ;
/// # }
/// # runtime.dispose();
/// ```
///
/// Class names can include dashes, and since v0.5.0 can include a dash-separated segment of only numbers.
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
/// # fn test() -> impl IntoView {
/// let (count, set_count) = create_signal(2);
/// view! { <div class:hidden-div-25={move || count.get() < 3}>"Now you see me, now you dont."</div> }
/// # ;
/// # };
/// # runtime.dispose();
/// # }
/// ```
///
/// Class names cannot include special symbols.
/// ```rust,compile_fail
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
/// # fn test() -> impl IntoView {
/// let (count, set_count) = create_signal(2);
/// // class:hidden-[div]-25 is invalid attribute name
/// view! { <div class:hidden-[div]-25={move || count.get() < 3}>"Now you see me, now you dont."</div> }
/// # ;
/// # };
/// # runtime.dispose();
/// # }
/// ```
///
/// However, you can pass arbitrary class names using the syntax `class=("name", value)`.
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
/// # fn test() -> impl IntoView {
/// let (count, set_count) = create_signal(2);
/// // this allows you to use CSS frameworks that include complex class names
/// view! {
@ -190,16 +166,14 @@ mod slot;
/// "Now you see me, now you dont."
/// </div>
/// }
/// # ;
/// # };
/// # runtime.dispose();
/// # }
/// ```
///
/// 8. Individual styles can also be set with `style:` or `style=("property-name", value)` syntax.
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
///
/// # fn test() -> impl IntoView {
/// let (x, set_x) = create_signal(0);
/// let (y, set_y) = create_signal(0);
/// view! {
@ -212,67 +186,57 @@ mod slot;
/// "Moves when coordinates change"
/// </div>
/// }
/// # ;
/// # };
/// # runtime.dispose();
/// # }
/// ```
///
/// 9. You can use the `node_ref` or `_ref` attribute to store a reference to its DOM element in a
/// [NodeRef](https://docs.rs/leptos/latest/leptos/struct.NodeRef.html) to use later.
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
///
/// # fn test() -> impl IntoView {
/// use leptos::html::Input;
///
/// let (value, set_value) = create_signal(0);
/// let my_input = create_node_ref::<Input>();
/// view! { <input type="text" _ref=my_input/> }
/// let (value, set_value) = signal(0);
/// let my_input = NodeRef::<Input>::new();
/// view! { <input type="text" node_ref=my_input/> }
/// // `my_input` now contains an `Element` that we can use anywhere
/// # ;
/// # };
/// # runtime.dispose();
/// ```
///
/// 10. You can add the same class to every element in the view by passing in a special
/// `class = {/* ... */},` argument after ``. This is useful for injecting a class
/// provided by a scoped styling library.
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
///
/// # fn test() -> impl IntoView {
/// let class = "mycustomclass";
/// view! { class = class,
/// <div> // will have class="mycustomclass"
/// <p>"Some text"</p> // will also have class "mycustomclass"
/// </div>
/// }
/// # ;
/// # };
/// # runtime.dispose();
/// # }
/// ```
///
/// 11. You can set any HTML elements `innerHTML` with the `inner_html` attribute on an
/// element. Be careful: this HTML will not be escaped, so you should ensure that it
/// only contains trusted input.
/// ```rust
/// # use leptos::*;
/// # let runtime = create_runtime();
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
/// # fn test() -> impl IntoView {
/// let html = "<p>This HTML will be injected.</p>";
/// view! {
/// <div inner_html=html/>
/// }
/// # ;
/// # };
/// # runtime.dispose();
/// # }
/// ```
///
/// Heres a simple example that shows off several of these features, put together
/// ```rust
/// # use leptos::*;
///
/// # if !cfg!(any(feature = "csr", feature = "hydrate")) {
/// # use leptos::prelude::*;
/// pub fn SimpleCounter() -> impl IntoView {
/// // create a reactive signal with the initial value
/// let (value, set_value) = create_signal(0);
@ -292,8 +256,6 @@ mod slot;
/// </div>
/// }
/// }
/// # ;
/// # }
/// ```
#[proc_macro_error::proc_macro_error]
#[proc_macro]
@ -364,7 +326,7 @@ pub fn view(tokens: TokenStream) -> TokenStream {
///
/// Heres how you would define and use a simple Leptos component which can accept custom properties for a name and age:
/// ```rust
/// # use leptos::*;
/// # use leptos::prelude::*;
/// use std::time::Duration;
///
/// #[component]
@ -411,7 +373,7 @@ pub fn view(tokens: TokenStream) -> TokenStream {
/// a particular tag is a component, not an HTML element.
///
/// ```
/// # use leptos::*;
/// # use leptos::prelude::*;
///
/// // PascalCase: Generated component will be called MyComponent
/// #[component]
@ -422,48 +384,15 @@ pub fn view(tokens: TokenStream) -> TokenStream {
/// fn my_snake_case_component() -> impl IntoView {}
/// ```
///
/// * You can pass generic arguments, and they can either be defined in a `where` clause
/// or inline in the generic block, but not in an `impl` in function argument position.
///
/// ```compile_error
/// // ❌ This won't work.
/// # use leptos::*;
/// use leptos::html::Div;
///
/// #[component]
/// fn MyComponent(render_prop: impl Fn() -> HtmlElement<Div>) -> impl IntoView {
/// }
/// ```
///
/// ```
/// // ✅ Do this instead
/// # use leptos::*;
/// use leptos::html::Div;
///
/// #[component]
/// fn MyComponent<T>(render_prop: T) -> impl IntoView
/// where
/// T: Fn() -> HtmlElement<Div>,
/// {
/// }
///
/// // or
/// #[component]
/// fn MyComponent2<T: Fn() -> HtmlElement<Div>>(
/// render_prop: T,
/// ) -> impl IntoView {
/// }
/// ```
///
/// 5. You can access the children passed into the component with the `children` property, which takes
/// an argument of the type `Children`. This is an alias for `Box<dyn FnOnce() -> Fragment>`.
/// an argument of the type `Children`. This is an alias for `Box<dyn FnOnce() -> AnyView<_>>`.
/// If you need `children` to be a `Fn` or `FnMut`, you can use the `ChildrenFn` or `ChildrenFnMut`
/// type aliases.
/// type aliases. If you want to iterate over the children, you can take `ChildrenFragment`.
///
/// ```
/// # use leptos::*;
/// # use leptos::prelude::*;
/// #[component]
/// fn ComponentWithChildren(children: Children) -> impl IntoView {
/// fn ComponentWithChildren(children: ChildrenFragment) -> impl IntoView {
/// view! {
/// <ul>
/// {children()
@ -502,7 +431,7 @@ pub fn view(tokens: TokenStream) -> TokenStream {
/// `Some(T)` explicitly. This means that the optional property can be omitted (and be `None`), or explicitly
/// specified as either `None` or `Some(T)`.
/// ```rust
/// # use leptos::*;
/// # use leptos::prelude::*;
///
/// #[component]
/// pub fn MyComponent(
@ -570,7 +499,7 @@ pub fn component(
///
/// ## Example
/// ```rust,ignore
/// use leptos::*;
/// use leptos::prelude::*;
///
/// #[component]
/// pub fn App() -> impl IntoView {
@ -656,7 +585,7 @@ fn component_macro(s: TokenStream, island: bool) -> TokenStream {
///
/// Heres how you would define and use a simple Leptos component which can accept a custom slot:
/// ```rust
/// # use leptos::*;
/// # use leptos::prelude::*;
/// use std::time::Duration;
///
/// #[slot]
@ -668,16 +597,10 @@ fn component_macro(s: TokenStream, island: bool) -> TokenStream {
///
/// #[component]
/// fn HelloComponent(
///
/// /// Component slot, should be passed through the <HelloSlot slot> syntax.
/// hello_slot: HelloSlot,
/// ) -> impl IntoView {
/// // mirror the children from the slot, if any were passed
/// if let Some(children) = hello_slot.children {
/// (children)().into_view()
/// } else {
/// ().into_view()
/// }
/// hello_slot.children.map(|children| children())
/// }
///
/// #[component]
@ -702,7 +625,7 @@ fn component_macro(s: TokenStream, island: bool) -> TokenStream {
///
/// ```compile_error
/// // ❌ This won't work
/// # use leptos::*;
/// # use leptos::prelude::*;
///
/// #[slot]
/// struct SlotWithChildren {
@ -728,7 +651,7 @@ fn component_macro(s: TokenStream, island: bool) -> TokenStream {
///
/// ```
/// // ✅ Do this instead
/// # use leptos::*;
/// # use leptos::prelude::*;
///
/// #[slot]
/// struct SlotWithChildren {
@ -908,9 +831,8 @@ pub fn params_derive(
/// Can be used to access deeply nested fields within a global state object.
///
/// ```rust
/// # use leptos::{create_runtime, create_rw_signal};
/// # use leptos::prelude::*;
/// # use leptos_macro::slice;
/// # let runtime = create_runtime();
///
/// #[derive(Default)]
/// pub struct Outer {
@ -924,7 +846,7 @@ pub fn params_derive(
/// inner_name: String,
/// }
///
/// let outer_signal = create_rw_signal(Outer::default());
/// let outer_signal = RwSignal::new(Outer::default());
///
/// let (count, set_count) = slice!(outer_signal.count);
///

View file

@ -40,7 +40,7 @@ impl ToTokens for SliceMacroInput {
let path = &self.path;
tokens.extend(quote! {
::leptos::create_slice(
::leptos::reactive_graph::computed::create_slice(
#root,
|st: &_| st.#path.clone(),
|st: &mut _, n| st.#path = n

View file

@ -1,9 +1,7 @@
#[cfg(test)]
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(not(feature = "ssr"))] {
use leptos::{server, server_fn::{codec, ServerFn}, ServerFnError};
use leptos::{
server,
server_fn::{codec, ServerFn, ServerFnError},
};
use std::any::TypeId;
#[test]
@ -16,7 +14,10 @@ cfg_if! {
<MyServerAction as ServerFn>::PATH.trim_end_matches(char::is_numeric),
"/api/my_server_action"
);
assert_eq!(TypeId::of::<<MyServerAction as ServerFn>::InputEncoding>(), TypeId::of::<codec::PostUrl>());
assert_eq!(
TypeId::of::<<MyServerAction as ServerFn>::InputEncoding>(),
TypeId::of::<codec::PostUrl>()
);
}
#[test]
@ -26,7 +27,10 @@ cfg_if! {
Ok(())
}
assert_eq!(<FooBar as ServerFn>::PATH, "/foo/bar/my_path");
assert_eq!(TypeId::of::<<FooBar as ServerFn>::InputEncoding>(), TypeId::of::<codec::Cbor>());
assert_eq!(
TypeId::of::<<FooBar as ServerFn>::InputEncoding>(),
TypeId::of::<codec::Cbor>()
);
}
#[test]
@ -36,7 +40,10 @@ cfg_if! {
Ok(())
}
assert_eq!(<FooBar as ServerFn>::PATH, "/foo/bar/my_path");
assert_eq!(TypeId::of::<<FooBar as ServerFn>::InputEncoding>(), TypeId::of::<codec::Cbor>());
assert_eq!(
TypeId::of::<<FooBar as ServerFn>::InputEncoding>(),
TypeId::of::<codec::Cbor>()
);
}
#[test]
@ -46,7 +53,10 @@ cfg_if! {
Ok(())
}
assert_eq!(<FooBar as ServerFn>::PATH, "/api/my_path");
assert_eq!(TypeId::of::<<FooBar as ServerFn>::InputEncoding>(), TypeId::of::<codec::PostUrl>());
assert_eq!(
TypeId::of::<<FooBar as ServerFn>::InputEncoding>(),
TypeId::of::<codec::PostUrl>()
);
}
#[test]
@ -59,7 +69,10 @@ cfg_if! {
<FooBar as ServerFn>::PATH.trim_end_matches(char::is_numeric),
"/api/my_server_action"
);
assert_eq!(TypeId::of::<<FooBar as ServerFn>::InputEncoding>(), TypeId::of::<codec::PostUrl>());
assert_eq!(
TypeId::of::<<FooBar as ServerFn>::InputEncoding>(),
TypeId::of::<codec::PostUrl>()
);
}
#[test]
@ -68,8 +81,14 @@ cfg_if! {
pub async fn my_server_action() -> Result<(), ServerFnError> {
Ok(())
}
assert_eq!(<MyServerAction as ServerFn>::PATH.trim_end_matches(char::is_numeric), "/foo/bar/my_server_action");
assert_eq!(TypeId::of::<<MyServerAction as ServerFn>::InputEncoding>(), TypeId::of::<codec::PostUrl>());
assert_eq!(
<MyServerAction as ServerFn>::PATH.trim_end_matches(char::is_numeric),
"/foo/bar/my_server_action"
);
assert_eq!(
TypeId::of::<<MyServerAction as ServerFn>::InputEncoding>(),
TypeId::of::<codec::PostUrl>()
);
}
#[test]
@ -82,7 +101,10 @@ cfg_if! {
<MyServerAction as ServerFn>::PATH.trim_end_matches(char::is_numeric),
"/api/my_server_action"
);
assert_eq!(TypeId::of::<<MyServerAction as ServerFn>::InputEncoding>(), TypeId::of::<codec::GetUrl>());
assert_eq!(
TypeId::of::<<MyServerAction as ServerFn>::InputEncoding>(),
TypeId::of::<codec::GetUrl>()
);
}
#[test]
@ -91,8 +113,12 @@ cfg_if! {
pub async fn my_server_action() -> Result<(), ServerFnError> {
Ok(())
}
assert_eq!(<MyServerAction as ServerFn>::PATH, "/api/path/to/my/endpoint");
assert_eq!(TypeId::of::<<MyServerAction as ServerFn>::InputEncoding>(), TypeId::of::<codec::PostUrl>());
}
}
assert_eq!(
<MyServerAction as ServerFn>::PATH,
"/api/path/to/my/endpoint"
);
assert_eq!(
TypeId::of::<<MyServerAction as ServerFn>::InputEncoding>(),
TypeId::of::<codec::PostUrl>()
);
}

View file

@ -1,4 +1,4 @@
use leptos::{create_runtime, create_rw_signal};
use leptos::prelude::RwSignal;
use leptos_macro::slice;
#[derive(Default)]
@ -18,9 +18,7 @@ pub struct InnerTuple(String);
#[test]
fn green() {
let _ = create_runtime();
let outer_signal = create_rw_signal(OuterState::default());
let outer_signal = RwSignal::new(OuterState::default());
let (_, _) = slice!(outer_signal.count);

View file

@ -1,5 +1,5 @@
use leptos::{create_runtime, create_rw_signal};
use leptos_macro::slice;
use leptos::prelude::RwSignal;
#[derive(Default, PartialEq)]
pub struct OuterState {
@ -14,9 +14,7 @@ pub struct InnerState {
}
fn main() {
let _ = create_runtime();
let outer_signal = create_rw_signal(OuterState::default());
let outer_signal = RwSignal::new(OuterState::default());
let (_, _) = slice!();

View file

@ -1,31 +1,31 @@
error: unexpected end of input, expected identifier
--> tests/slice/red.rs:21:18
--> tests/slice/red.rs:19:18
|
21 | let (_, _) = slice!();
19 | let (_, _) = slice!();
| ^^^^^^^^
|
= note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)
error: expected `.`
--> tests/slice/red.rs:21:18
|
21 | let (_, _) = slice!(outer_signal);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unexpected end of input, expected identifier or integer
--> tests/slice/red.rs:23:18
|
23 | let (_, _) = slice!(outer_signal);
| ^^^^^^^^^^^^^^^^^^^^
23 | let (_, _) = slice!(outer_signal.);
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unexpected end of input, expected identifier or integer
--> tests/slice/red.rs:25:18
|
25 | let (_, _) = slice!(outer_signal.);
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unexpected end of input, expected identifier or integer
--> tests/slice/red.rs:27:18
|
27 | let (_, _) = slice!(outer_signal.inner.);
25 | let (_, _) = slice!(outer_signal.inner.);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `slice` (in Nightly builds, run with -Z macro-backtrace for more info)

View file

@ -1,19 +1,3 @@
error: return type is incorrect
--> tests/ui/component.rs:4:1
|
4 | fn missing_scope() {}
| ^^^^^^^^^^^^^^^^^^
|
= help: return signature must be `-> impl IntoView`
error: return type is incorrect
--> tests/ui/component.rs:7:1
|
7 | fn missing_return_type() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: return signature must be `-> impl IntoView`
error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default`, `into` and `attrs`
--> tests/ui/component.rs:10:31
|

View file

@ -1,11 +1,3 @@
error: return type is incorrect
--> tests/ui/component_absolute.rs:2:1
|
2 | fn missing_return_type() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: return signature must be `-> impl IntoView`
error: supported fields are `optional`, `optional_no_strip`, `strip_option`, `default`, `into` and `attrs`
--> tests/ui/component_absolute.rs:5:31
|