From e1d4b6bcd01316a04c5f0f7fcfee45cce90bed1c Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 17 Mar 2026 18:58:47 -0500 Subject: [PATCH] fix: remove invalid `stack` property from secondary position channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `build_layer_encoding` was emitting `"stack": null` on both primary (y/radius) and secondary (y2/radius2) position channels. The Vega-Lite v6 schema does not allow `stack` on secondary channels, causing Altair to reject the spec with "Only chart objects can be used in LayerChart" (validate=False) or a schema validation error (validate=True). Only primary position channels need `stack: null` to disable Vega-Lite's automatic stacking — secondary channels inherit from the primary. Also strengthens `test_secondary_channels_have_no_disallowed_properties` to assert `stack` is absent and to run full Vega-Lite schema validation. Co-Authored-By: Claude Opus 4.6 --- src/writer/vegalite/mod.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/writer/vegalite/mod.rs b/src/writer/vegalite/mod.rs index 9d7c2def..81b66921 100644 --- a/src/writer/vegalite/mod.rs +++ b/src/writer/vegalite/mod.rs @@ -349,27 +349,19 @@ fn build_layer_encoding( ); } - // Disable Vega-Lite's automatic stacking - we handle position adjustments ourselves - // This prevents Vega-Lite from applying its own stack/dodge logic on top of ours - // Set stack: null on both y and y2 channels (pos2 and pos2end in our terminology) + // Disable Vega-Lite's automatic stacking - we handle position adjustments ourselves. + // This prevents Vega-Lite from applying its own stack/dodge logic on top of ours. + // Only set stack: null on primary position channels (y/radius) — Vega-Lite does + // not support 'stack' on secondary channels (y2/radius2) and Altair rejects it. let y_channel = match coord_kind { CoordKind::Cartesian => "y", CoordKind::Polar => "radius", }; - let y2_channel = match coord_kind { - CoordKind::Cartesian => "y2", - CoordKind::Polar => "radius2", - }; if let Some(y_enc) = encoding.get_mut(y_channel) { if let Some(obj) = y_enc.as_object_mut() { obj.insert("stack".to_string(), Value::Null); } } - if let Some(y2_enc) = encoding.get_mut(y2_channel) { - if let Some(obj) = y2_enc.as_object_mut() { - obj.insert("stack".to_string(), Value::Null); - } - } // Apply geom-specific encoding modifications via renderer let renderer = get_renderer(&layer.geom); @@ -2703,8 +2695,15 @@ mod tests { enc.get("axis").is_none(), "{channel} should not have 'axis': {enc}" ); + assert!( + enc.get("stack").is_none(), + "{channel} should not have 'stack': {enc}" + ); } } } + + // The spec must also pass Vega-Lite schema validation + assert_valid_vegalite(&json_str); } }