fix(lint): guard active nav pill against sibling corner-radius flattening
A 100%-rounded active tab nested in a nav pill was flattened to a sharp square by the sibling corner-radius consistency detectors. Skip the fix when a group's radius is pill-scale (>= 40) so the active pill survives validation.
This commit is contained in:
parent
169e6abed2
commit
4ac5b5fa69
|
|
@ -203,10 +203,33 @@ fn check_consistency(
|
|||
|
||||
let majority_value = values[majority_idx].1.value.clone();
|
||||
let property_fix = fix_property_for(property);
|
||||
// A PILL cornerRadius (>= 40) is an INTENTIONAL design choice — a
|
||||
// fully-rounded active nav tab / toggle highlight — NOT a sibling
|
||||
// inconsistency. Skip cornerRadius normalization when the outlier OR the
|
||||
// majority is a pill, so this lint never flattens a rounded active nav tab
|
||||
// back to its square siblings (the active-nav-tab-reset-to-square bug) nor
|
||||
// rounds square siblings up to a pill. Card-scale radii (8–16) still
|
||||
// normalize. (Mirrors the guard in `detect_mixed_sibling_corner_radius`.)
|
||||
const PILL_RADIUS: f64 = 40.0;
|
||||
let is_corner = property == "cornerRadius";
|
||||
let majority_is_pill = is_corner
|
||||
&& majority_value
|
||||
.as_f64()
|
||||
.map(|v| v >= PILL_RADIUS)
|
||||
.unwrap_or(false);
|
||||
for (idx, (_, group)) in values.iter().enumerate() {
|
||||
if idx == majority_idx {
|
||||
continue;
|
||||
}
|
||||
let group_is_pill = is_corner
|
||||
&& group
|
||||
.value
|
||||
.as_f64()
|
||||
.map(|v| v >= PILL_RADIUS)
|
||||
.unwrap_or(false);
|
||||
if is_corner && (group_is_pill || majority_is_pill) {
|
||||
continue;
|
||||
}
|
||||
for node_id_str in &group.nodes {
|
||||
issues.push(Issue {
|
||||
node_id: node_id_str.clone(),
|
||||
|
|
@ -278,6 +301,17 @@ fn walk_mixed_corner_radius(node: &PenNode, issues: &mut Vec<Issue>) {
|
|||
for sibling in &siblings {
|
||||
let num = corner_radius_numeric(sibling);
|
||||
if num != modal {
|
||||
// A PILL radius (>= 40) is an INTENTIONAL design choice — a
|
||||
// fully-rounded active nav tab / toggle highlight — not a
|
||||
// sibling inconsistency to flatten. Skip when either the
|
||||
// sibling OR the modal is a pill, so the validator never
|
||||
// resets a rounded active nav tab back to a square (the
|
||||
// round-then-lint-flattens bug) nor forces square siblings
|
||||
// into pills. Card-scale radii (8–16) still normalize.
|
||||
const PILL_RADIUS: f64 = 40.0;
|
||||
if num >= PILL_RADIUS || modal >= PILL_RADIUS {
|
||||
continue;
|
||||
}
|
||||
issues.push(Issue {
|
||||
node_id: node_id(sibling).to_string(),
|
||||
category: IssueCategory::MixedSiblingCornerRadius,
|
||||
|
|
|
|||
|
|
@ -187,6 +187,29 @@ mod mixed_corner_radius_tests {
|
|||
assert_eq!(issues[0].suggested_value, json!(8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn active_nav_pill_not_flattened_to_square_siblings() {
|
||||
// The desktop's pre-validator runs this lint AFTER apply_tree_heuristics
|
||||
// rounds the active nav tab to a 999 pill. Among its square (cr=0)
|
||||
// inactive siblings the modal is 0, so without a pill guard the lint
|
||||
// would "fix" the 999 back to 0 — exactly the active-nav-tab-reset-to-
|
||||
// square bug. A pill radius (>= 40) is intentional and must be left alone.
|
||||
let root = node(json!({
|
||||
"type": "frame", "id": "row", "layout": "horizontal",
|
||||
"children": [
|
||||
{"type": "frame", "id": "home", "cornerRadius": 999,
|
||||
"fill": [{"type": "solid", "color": "#F97316"}]},
|
||||
{"type": "frame", "id": "orders", "cornerRadius": 0},
|
||||
{"type": "frame", "id": "profile", "cornerRadius": 0}
|
||||
]
|
||||
}));
|
||||
let issues = detect_mixed_sibling_corner_radius(&root);
|
||||
assert!(
|
||||
!issues.iter().any(|i| i.node_id == "home"),
|
||||
"active pill nav tab (999) must NOT be flagged to flatten to its square siblings, got {issues:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_three_way_split_without_modal() {
|
||||
let root = node(json!({
|
||||
|
|
|
|||
|
|
@ -340,6 +340,40 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
/// End-to-end of what the desktop's `LintPreValidator` runs: `detect_and_plan`
|
||||
/// on a nav AFTER `apply_tree_heuristics` rounded the active Home tab to a 999
|
||||
/// pill. The mixed-sibling-corner-radius detector must NOT plan flattening it
|
||||
/// back to its square (cr=0) siblings — the active-nav-tab-reset-to-square bug
|
||||
/// that left op-smoke (no validation) rounded but the desktop (validation)
|
||||
/// square.
|
||||
#[test]
|
||||
fn detect_and_plan_does_not_flatten_active_nav_pill() {
|
||||
let raw = r##"{
|
||||
"version":"1.0",
|
||||
"children":[{
|
||||
"type":"frame","id":"bar","role":"bottom-tab-bar","layout":"vertical",
|
||||
"children":[{
|
||||
"type":"frame","id":"pill","cornerRadius":100,"layout":"horizontal",
|
||||
"children":[
|
||||
{"type":"frame","id":"home","cornerRadius":999,"fill":[{"type":"solid","color":"#F97316"}]},
|
||||
{"type":"frame","id":"search","role":"search-bar","cornerRadius":26},
|
||||
{"type":"frame","id":"orders","cornerRadius":0},
|
||||
{"type":"frame","id":"profile","cornerRadius":0}
|
||||
]
|
||||
}]
|
||||
}]
|
||||
}"##;
|
||||
let doc: PenDocument = serde_json::from_str(raw).expect("parse nav doc");
|
||||
let plan = detect_and_plan(&doc);
|
||||
let home_corner_fix = plan
|
||||
.iter()
|
||||
.any(|f| f.node_id == "home" && matches!(f.action, PlannedAction::SetCornerRadius(_)));
|
||||
assert!(
|
||||
!home_corner_fix,
|
||||
"active nav pill (home cr=999) must NOT be planned for a cornerRadius flatten, plan={plan:?}"
|
||||
);
|
||||
}
|
||||
|
||||
/// Load the `invisible-container-with-var` fixture (doc declares
|
||||
/// `color-border` variable) and assert equivalence — exercises the
|
||||
/// `$color-border` design-token reference path.
|
||||
|
|
|
|||
Loading…
Reference in a new issue