openpencil/crates/op-pen-loader/tests/fixed_size_layout.rs

90 lines
4.3 KiB
Rust

//! Regression for jian `layout::resolve` fixed-size handling. The fix: a
//! node whose width AND height are both `Number` gets `flex_shrink=0` (in
//! both `node_to_style` leaves and `container_to_style` frames), matching
//! TS (fixed sizes are summed; only fill_container shares the remainder).
//! - a fixed square (image / pin / filter background) keeps its size in a
//! tight row instead of taffy's default `flex_shrink:1` squeezing it;
//! - a `width=fill, height=Number` tile (quick-action cards) is NOT pinned
//! (the rule is AND, not OR), so N fill tiles still share the row.
use op_pen_loader::payload::NodePayload;
fn find<'a>(nodes: &'a [NodePayload], name: &str) -> Option<&'a NodePayload> {
for n in nodes {
if n.name == name {
return Some(n);
}
if let Some(f) = find(&n.children, name) {
return Some(f);
}
}
None
}
#[test]
fn fixed_square_image_not_shrunk_in_tight_row() {
// 160-wide horizontal row holding a fill image + a fixed 80x80 image.
// The fixed one must stay 80x80 (the fill image absorbs the squeeze).
let doc = r#"{"version":"1.0.0","children":[{"type":"frame","id":"row","width":160,"height":100,"layout":"horizontal","gap":8,"children":[{"type":"image","id":"fill","name":"Fill","width":"fill_container","height":"fill_container","src":""},{"type":"image","id":"sq","name":"Sq","width":80,"height":80,"src":""}]}]}"#;
let loaded = op_pen_loader::load_canonical(doc).expect("loads");
let d = op_pen_loader::pen_document_to_payload(&loaded.value);
let sq = d
.payload
.pages
.iter()
.find_map(|p| find(&p.children, "Sq"))
.expect("fixed image present");
assert!(
(sq.w - 80.0).abs() < 1.0 && (sq.h - 80.0).abs() < 1.0,
"fixed 80x80 image must stay square in a tight row, got {}x{}",
sq.w,
sq.h
);
}
#[test]
fn fill_width_tiles_with_fixed_height_still_share_the_row() {
// Quick-action tiles are `width=fill_container, height=78` (only the
// cross axis is fixed). They must still flex-share the row. A `width OR
// height is Number` rule wrongly pinned them (flex_shrink=0) and the
// fill tiles overflowed, showing only the first; the `AND` rule frees
// them to share the row again.
let doc = r#"{"version":"1.0.0","children":[{"type":"frame","id":"row","width":300,"height":100,"layout":"horizontal","gap":0,"children":[{"type":"frame","id":"a","name":"A","width":"fill_container","height":78},{"type":"frame","id":"b","name":"B","width":"fill_container","height":78},{"type":"frame","id":"c","name":"C","width":"fill_container","height":78}]}]}"#;
let loaded = op_pen_loader::load_canonical(doc).expect("loads");
let d = op_pen_loader::pen_document_to_payload(&loaded.value);
let a = d
.payload
.pages
.iter()
.find_map(|p| find(&p.children, "A"))
.expect("tile A present");
assert!(
a.w < 150.0,
"fill+fixed-height tiles must share the 300px row (~100 each), not occupy it whole; got w={}",
a.w
);
}
#[test]
fn fixed_sibling_stays_inside_card_when_fill_column_has_wide_content() {
// Banner-shaped: a horizontal card holding [fill column whose content is
// wider than the remaining space, fixed 80x80 image]. The fill column
// must shrink below its content's min-width (TS gives fill the remaining
// space, content wraps) so the fixed image stays inside the card instead
// of being pushed out (needs `min_size: 0` on the fill axis).
let doc = r#"{"version":"1.0.0","children":[{"type":"frame","id":"card","width":200,"height":100,"layout":"horizontal","gap":0,"children":[{"type":"frame","id":"col","name":"Col","width":"fill_container","height":"fill_container","layout":"vertical","children":[{"type":"frame","id":"wide","width":150,"height":20}]},{"type":"image","id":"img","name":"Img","width":80,"height":80,"src":""}]}]}"#;
let loaded = op_pen_loader::load_canonical(doc).expect("loads");
let d = op_pen_loader::pen_document_to_payload(&loaded.value);
let img = d
.payload
.pages
.iter()
.find_map(|p| find(&p.children, "Img"))
.expect("img present");
assert!(
img.x + img.w <= 201.0,
"fixed image must stay inside the 200px card (x+w<=200), got x={} w={}",
img.x,
img.w
);
}