diff --git a/assets/typeset-preview.js b/assets/typeset-preview.js index 85156ba6..a9e18a16 100644 --- a/assets/typeset-preview.js +++ b/assets/typeset-preview.js @@ -261,6 +261,15 @@ app = rx.App( } } + // On SPA re-navigation React remounts the selects at their defaults; pull + // the persisted state back into them before re-rendering. + function syncAll() { + if (!ensureDefaults()) return; + syncSelectsToState(); + applyAll(); + renderCodeBlocks(); + } + window.typesetPreview = { state: state, applyAll: applyAll, @@ -269,6 +278,7 @@ app = rx.App( getThemeTokensCode: getThemeTokensCode, getUsageCode: getUsageCode, renderCodeBlocks: renderCodeBlocks, + syncAll: syncAll, }; document.addEventListener("change", function (e) { diff --git a/native/pages/typeset.py b/native/pages/typeset.py index a06aeb20..dc0439af 100644 --- a/native/pages/typeset.py +++ b/native/pages/typeset.py @@ -243,8 +243,7 @@ def typeset_page(): flow: {json.dumps(FLOW_OPTIONS)}, }}; if (window.typesetPreview) {{ - window.typesetPreview.applyAll(); - window.typesetPreview.renderCodeBlocks(); + window.typesetPreview.syncAll(); }} """ ), diff --git a/tests/test_typeset_sync.py b/tests/test_typeset_sync.py new file mode 100644 index 00000000..edb46861 --- /dev/null +++ b/tests/test_typeset_sync.py @@ -0,0 +1,35 @@ +"""Regression: /typeset on_mount must sync remounted selects to persisted state.""" + +import pathlib +import re + +from native.pages.typeset import typeset_page + +JS = (pathlib.Path(__file__).parent.parent / "assets" / "typeset-preview.js").read_text( + encoding="utf-8", +) + + +def _on_mount_scripts() -> list[str]: + chain = typeset_page().event_triggers["on_mount"] + scripts = [] + for event in chain.events: + for name_var, val_var in event.args: + if getattr(name_var, "_js_expr", None) == "javascript_code": + scripts.append(val_var._var_value) + break + return scripts + + +def test_on_mount_calls_sync_all() -> None: + init = _on_mount_scripts()[0] + assert "typesetPreview.syncAll()" in init + assert "typesetPreview.applyAll()" not in init + + +def test_sync_all_exported_and_syncs_selects() -> None: + assert re.search(r"window\.typesetPreview\s*=\s*\{[^}]*\bsyncAll:\s*syncAll", JS) + body = re.search(r"function syncAll\(\)\s*\{(.*?)\n \}", JS, re.S) + assert body + b = body.group(1) + assert b.index("syncSelectsToState()") < b.index("applyAll()") < b.index("renderCodeBlocks()")