fission-core 0.14.1

Core runtime, state, actions, effects, resources, input, and UI model for Fission
Documentation
use std::collections::HashSet;

use fission_core::ui::{Container, Scroll};
use fission_core::{Runtime, ScrollStateMap, WidgetIdExt};
use fission_ir::{CoreIR, FlexDirection, LayoutOp, Op, WidgetId};

#[test]
fn scroll_state_discards_unmounted_scroll_nodes() {
    let active = WidgetId::explicit("active-scroll");
    let inactive = WidgetId::explicit("inactive-scroll");
    let mut scroll = ScrollStateMap::default();
    scroll.set_offset(active, 40.0);
    scroll.set_offset(inactive, 120.0);

    scroll.retain_active(&HashSet::from([active]));

    assert_eq!(scroll.get_offset(active), 40.0);
    assert_eq!(scroll.get_offset(inactive), 0.0);
}

#[test]
fn runtime_reconciles_replacement_scroll_state_before_layout() {
    let review = WidgetId::explicit("flow.review.scroll");
    let installing = WidgetId::explicit("flow.installing.scroll");
    let mut runtime = Runtime::default();

    runtime.runtime_state.scroll.set_offset(review, 640.0);
    runtime.reconcile_ir(&scroll_ir(review));
    assert_eq!(runtime.runtime_state.scroll.get_offset(review), 640.0);

    runtime.reconcile_ir(&scroll_ir(installing));

    assert_eq!(runtime.runtime_state.scroll.get_offset(review), 0.0);
    assert_eq!(runtime.runtime_state.scroll.get_offset(installing), 0.0);
}

#[test]
fn runtime_preserves_scroll_state_for_the_same_mounted_identity() {
    let scroll = WidgetId::explicit("flow.review.scroll");
    let mut runtime = Runtime::default();
    runtime.runtime_state.scroll.set_offset(scroll, 320.0);

    runtime.reconcile_ir(&scroll_ir(scroll));
    runtime.reconcile_ir(&scroll_ir(scroll));

    assert_eq!(runtime.runtime_state.scroll.get_offset(scroll), 320.0);
}

#[test]
fn explicit_screen_scope_changes_implicit_descendant_scroll_identity() {
    let review = scoped_scroll_id("flow.review");
    let installing = scoped_scroll_id("flow.installing");

    assert_ne!(review, installing);
}

fn scoped_scroll_id(scope: &str) -> WidgetId {
    let widget = Container::new(Scroll::default()).id(WidgetId::explicit(scope));
    let ir = fission_core::internal::lower_widget_to_ir(&widget);
    ir.nodes
        .iter()
        .find_map(|(id, node)| {
            matches!(node.op, Op::Layout(LayoutOp::Scroll { .. })).then_some(*id)
        })
        .expect("scoped screen should contain a scroll node")
}

fn scroll_ir(scroll: WidgetId) -> CoreIR {
    let mut ir = CoreIR::new();
    ir.add_node(
        scroll,
        Op::Layout(LayoutOp::Scroll {
            direction: FlexDirection::Column,
            show_scrollbar: false,
            width: None,
            height: None,
            min_width: None,
            max_width: None,
            min_height: None,
            max_height: None,
            padding: [0.0; 4],
            flex_grow: 1.0,
            flex_shrink: 0.0,
        }),
        Vec::new(),
    );
    ir.set_root(scroll);
    ir
}