fix(web): style editor no longer drops layout-only fields it never rendered

CodeRabbit flagged elementKeys() in style-editor.js: render() claims the
whole customization.layout child as the widget's own (removing it from the
generic fallback renderer, since posting the same offset twice is worse),
but elementKeys() only listed keys that also have their own top-level style
block. A hand-written schema can put a key under layout that never got one
-- a logo, a timeout indicator, a possession arrow with a position but no
font or colour -- and that key's only control silently disappeared: no row
in the style editor's table (elementKeys never listed it) and no fallback
section either (layout was removed wholesale).

elementKeys() now appends any layout-declared key not already covered by a
style element, so table() renders a row for it (layout columns only, no
style columns) and the wholesale layout ownership claim stays truthful.

Verified against current code before fixing. New regression test
(test/js/unit/test_style_editor_element_keys.js, following this repo's
existing eval-extraction pattern for testing widget JS without a browser)
fails against the reverted function and passes with the fix; added to
run_all.js and the suite table in test/js/README.md.

Full pytest suite: 4887 passed, 62 skipped, 2 failed -- both the
pre-existing Europe/Kiev/Asia/Calcutta tzdata-alias gap on this sandbox,
identical on origin/main, unrelated to this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dpg3HLWohdCUdzz2QNHanm
This commit is contained in:
Claude
2026-09-13 23:38:37 +00:00
parent b8d344ae60
commit 75e6b2b20c
4 changed files with 124 additions and 2 deletions
@@ -159,7 +159,19 @@
});
// Nothing marked means the schema never went through expansion, so
// fall back to the shape test rather than rendering an empty table.
return managed.length ? managed : shaped;
var keys = managed.length ? managed : shaped;
// A hand-written layout block can offset something that never got
// a style block of its own -- a logo, a timeout indicator, a
// possession arrow with a position but no font, colour or size to
// set. render() claims the whole `layout` child as ours regardless
// (posting every covered offset twice from two controls is worse),
// so a key that only lives under layout still needs a row here, or
// it loses its only control the moment that fallback section is
// dropped.
var layoutKeys = Object.keys(ownObj(props, 'layout').properties || {});
var extra = layoutKeys.filter(function (k) { return keys.indexOf(k) === -1; });
return keys.concat(extra);
}
function titleOf(schema, key) {