leptos-use-intersection-observer 0.20.0

Reactive [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver)
Documentation
#![cfg(target_arch = "wasm32")]

use any_spawner::Executor;
use gloo_timers::future::TimeoutFuture;
use leptos::prelude::*;
use leptos_use_intersection_observer::{
    UseIntersectionObserverOptions, UseIntersectionObserverReturn, use_intersection_observer,
    use_intersection_observer_with_options,
};
use std::cell::RefCell;
use std::rc::Rc;
use wasm_bindgen_test::*;

wasm_bindgen_test_configure!(run_in_browser);

fn append_box(parent: &web_sys::Element, style: &str) -> web_sys::Element {
    let el = document().create_element("div").unwrap();
    el.set_attribute("style", style).unwrap();
    parent.append_child(&el).unwrap();
    el
}

fn body() -> web_sys::Element {
    document().body().unwrap().into()
}

type Seen = Rc<RefCell<Vec<bool>>>;

fn recorder() -> (
    Seen,
    impl FnMut(Vec<web_sys::IntersectionObserverEntry>, web_sys::IntersectionObserver) + 'static,
) {
    let seen: Seen = Default::default();
    let cb = {
        let seen = Rc::clone(&seen);
        move |entries: Vec<web_sys::IntersectionObserverEntry>, _| {
            seen.borrow_mut()
                .extend(entries.iter().map(|e| e.is_intersecting()));
        }
    };
    (seen, cb)
}

#[wasm_bindgen_test]
async fn reports_intersection_changes() {
    let _ = Executor::init_wasm_bindgen();
    let owner = Owner::new();
    owner.set();

    let el = append_box(
        &body(),
        "position: fixed; left: 0; top: 0; width: 20px; height: 20px;",
    );
    let (seen, cb) = recorder();

    let UseIntersectionObserverReturn { is_active, .. } = use_intersection_observer(el.clone(), cb);
    TimeoutFuture::new(150).await;
    assert!(is_active.get_untracked());
    assert_eq!(*seen.borrow(), vec![true]);

    el.set_attribute(
        "style",
        "position: fixed; left: -100px; top: 0; width: 20px; height: 20px;",
    )
    .unwrap();
    TimeoutFuture::new(150).await;
    assert_eq!(*seen.borrow(), vec![true, false]);
}

#[wasm_bindgen_test]
async fn pause_and_resume_observing() {
    let _ = Executor::init_wasm_bindgen();
    let owner = Owner::new();
    owner.set();

    let el = append_box(
        &body(),
        "position: fixed; left: 0; top: 0; width: 20px; height: 20px;",
    );
    let (seen, cb) = recorder();

    let UseIntersectionObserverReturn {
        pause,
        resume,
        is_active,
        ..
    } = use_intersection_observer(el.clone(), cb);
    TimeoutFuture::new(150).await;
    assert_eq!(seen.borrow().len(), 1);

    pause();
    assert!(!is_active.get_untracked());
    el.set_attribute(
        "style",
        "position: fixed; left: -100px; top: 0; width: 20px; height: 20px;",
    )
    .unwrap();
    TimeoutFuture::new(150).await;
    assert_eq!(seen.borrow().len(), 1, "no callbacks while paused");

    resume();
    assert!(is_active.get_untracked());
    TimeoutFuture::new(150).await;
    assert_eq!(
        seen.borrow().last(),
        Some(&false),
        "resuming reports the current state"
    );
}

#[wasm_bindgen_test]
async fn observes_relative_to_a_root_element() {
    let _ = Executor::init_wasm_bindgen();
    let owner = Owner::new();
    owner.set();

    let root = append_box(&body(), "width: 100px; height: 100px; overflow: scroll;");
    let el = append_box(&root, "margin-top: 300px; width: 20px; height: 20px;");
    let (seen, cb) = recorder();

    let _ = use_intersection_observer_with_options(
        el.clone(),
        cb,
        UseIntersectionObserverOptions::default().root(Some(root.clone())),
    );
    TimeoutFuture::new(150).await;
    assert_eq!(
        *seen.borrow(),
        vec![false],
        "hidden below the root's scroll area"
    );

    root.set_scroll_top(300.0);
    TimeoutFuture::new(150).await;
    assert_eq!(*seen.borrow(), vec![false, true]);
}

#[wasm_bindgen_test]
async fn does_not_start_when_not_immediate() {
    let _ = Executor::init_wasm_bindgen();
    let owner = Owner::new();
    owner.set();

    let el = append_box(&body(), "width: 20px; height: 20px;");
    let (seen, cb) = recorder();

    let UseIntersectionObserverReturn {
        is_active, resume, ..
    } = use_intersection_observer_with_options(
        el,
        cb,
        UseIntersectionObserverOptions::<web_sys::Element, _>::default().immediate(false),
    );
    TimeoutFuture::new(150).await;
    assert!(!is_active.get_untracked());
    assert!(seen.borrow().is_empty());

    resume();
    TimeoutFuture::new(150).await;
    assert_eq!(*seen.borrow(), vec![true]);
}