From ca32832e7d5ff6357753add6ca2b917eccb024fd Mon Sep 17 00:00:00 2001 From: rmorshea Date: Wed, 26 Jan 2022 18:43:43 -0800 Subject: [PATCH 1/4] implement script element --- .../idom-client-react/src/components.js | 8 ++++++++ src/idom/html.py | 19 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/client/packages/idom-client-react/src/components.js b/src/client/packages/idom-client-react/src/components.js index 9383e1f5f..5caed180e 100644 --- a/src/client/packages/idom-client-react/src/components.js +++ b/src/client/packages/idom-client-react/src/components.js @@ -34,6 +34,8 @@ export function Element({ model }) { } else { return null; } + } else if (model.tagName == "script") { + return html`<${ScriptElement} script=${model.children[0]} />`; } else if (model.importSource) { return html`<${ImportedElement} model=${model} />`; } else { @@ -56,6 +58,12 @@ function StandardElement({ model }) { ); } +function ScriptElement({ script }) { + const el = React.useRef(); + React.useEffect(eval(script), [script]); + return null; +} + function ImportedElement({ model }) { const layoutContext = React.useContext(LayoutContext); diff --git a/src/idom/html.py b/src/idom/html.py index 2dc9a22e6..49e734c96 100644 --- a/src/idom/html.py +++ b/src/idom/html.py @@ -150,7 +150,7 @@ - :func:`template` """ -from .core.vdom import make_vdom_constructor +from .core.vdom import VdomDict, make_vdom_constructor # Dcument metadata @@ -253,6 +253,23 @@ del_ = make_vdom_constructor("del") ins = make_vdom_constructor("ins") +# Scripting + + +def script(content: str) -> VdomDict: + """Create a new `<{script}> `__ element. + + Parameters: + content: + The text of the script should evaluate to a function. This function will be + called when the script is initially created or when the content of the + script changes. The function may optionally return a teardown function that + is called when the script element is removed from the tree, or when the + script content changes. + """ + return {"tagName": "script", "children": [content]} + + # Table content caption = make_vdom_constructor("caption") col = make_vdom_constructor("col") From 102b74e1ac6163b72e7341021761a8955601fc20 Mon Sep 17 00:00:00 2001 From: rmorshea Date: Wed, 26 Jan 2022 22:30:24 -0800 Subject: [PATCH 2/4] test script --- tests/test_html.py | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 tests/test_html.py diff --git a/tests/test_html.py b/tests/test_html.py new file mode 100644 index 000000000..c4b1e8d6b --- /dev/null +++ b/tests/test_html.py @@ -0,0 +1,92 @@ +from idom import component, html, use_state +from idom.utils import Ref + + +def test_script_mount_unmount(driver, driver_wait, display): + toggle_is_mounted = Ref() + + def use_toggle(): + state, set_state = use_state(True) + return state, lambda: set_state(not state) + + @component + def Root(): + is_mounted, toggle_is_mounted.current = use_toggle() + if is_mounted: + el = HasScript() + else: + el = html.div() + + return html.div( + html.div({"id": "mount-state", "data-value": False}), + el, + ) + + @component + def HasScript(): + return html.script( + """() => { + const mapping = {"false": false, "true": true}; + const mountStateEl = document.getElementById("mount-state"); + mountStateEl.setAttribute( + "data-value", !mapping[mountStateEl.getAttribute("data-value")]); + return () => mountStateEl.setAttribute( + "data-value", !mapping[mountStateEl.getAttribute("data-value")]); + }""" + ) + + display(Root) + + mount_state = driver.find_element("id", "mount-state") + + driver_wait.until(lambda d: mount_state.get_attribute("data-value") == "true") + + toggle_is_mounted.current() + + driver_wait.until(lambda d: mount_state.get_attribute("data-value") == "false") + + toggle_is_mounted.current() + + driver_wait.until(lambda d: mount_state.get_attribute("data-value") == "true") + + +def test_script_re_run_on_content_change(driver, driver_wait, display): + incr_count = Ref() + + def use_counter(): + state, set_state = use_state(1) + return state, lambda: set_state(state + 1) + + @component + def HasScript(): + count, incr_count.current = use_counter() + return html.div( + html.div({"id": "mount-count", "data-value": 0}), + html.div({"id": "unmount-count", "data-value": 0}), + html.script( + f"""() => {{ + const mountCountEl = document.getElementById("mount-count"); + const unmountCountEl = document.getElementById("unmount-count"); + mountCountEl.setAttribute("data-value", {count}); + return () => unmountCountEl.setAttribute("data-value", {count});; + }}""" + ), + ) + + display(HasScript) + + mount_count = driver.find_element("id", "mount-count") + unmount_count = driver.find_element("id", "unmount-count") + + driver_wait.until(lambda d: mount_count.get_attribute("data-value") == "1") + driver_wait.until(lambda d: unmount_count.get_attribute("data-value") == "0") + + incr_count.current() + + driver_wait.until(lambda d: mount_count.get_attribute("data-value") == "2") + driver_wait.until(lambda d: unmount_count.get_attribute("data-value") == "1") + + incr_count.current() + + driver_wait.until(lambda d: mount_count.get_attribute("data-value") == "3") + driver_wait.until(lambda d: unmount_count.get_attribute("data-value") == "2") From ba029fa54940ba6cb13bb8112e232bc73b9473d7 Mon Sep 17 00:00:00 2001 From: rmorshea Date: Wed, 26 Jan 2022 23:44:00 -0800 Subject: [PATCH 3/4] fix redef --- src/idom/html.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/idom/html.py b/src/idom/html.py index 49e734c96..64536595e 100644 --- a/src/idom/html.py +++ b/src/idom/html.py @@ -150,7 +150,8 @@ - :func:`template` """ -from .core.vdom import VdomDict, make_vdom_constructor +from .core.proto import VdomDict +from .core.vdom import make_vdom_constructor # Dcument metadata @@ -247,13 +248,6 @@ # Scripting canvas = make_vdom_constructor("canvas") noscript = make_vdom_constructor("noscript") -script = make_vdom_constructor("script") - -# Demarcating edits -del_ = make_vdom_constructor("del") -ins = make_vdom_constructor("ins") - -# Scripting def script(content: str) -> VdomDict: @@ -270,6 +264,10 @@ def script(content: str) -> VdomDict: return {"tagName": "script", "children": [content]} +# Demarcating edits +del_ = make_vdom_constructor("del") +ins = make_vdom_constructor("ins") + # Table content caption = make_vdom_constructor("caption") col = make_vdom_constructor("col") From 13a095ba84e7f5135493248c55d6b209df6de807 Mon Sep 17 00:00:00 2001 From: rmorshea Date: Thu, 27 Jan 2022 07:41:51 -0800 Subject: [PATCH 4/4] move hooks out of test def --- tests/test_html.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/test_html.py b/tests/test_html.py index c4b1e8d6b..d62e7a954 100644 --- a/tests/test_html.py +++ b/tests/test_html.py @@ -2,13 +2,19 @@ from idom.utils import Ref +def use_toggle(): + state, set_state = use_state(True) + return state, lambda: set_state(not state) + + +def use_counter(): + state, set_state = use_state(1) + return state, lambda: set_state(state + 1) + + def test_script_mount_unmount(driver, driver_wait, display): toggle_is_mounted = Ref() - def use_toggle(): - state, set_state = use_state(True) - return state, lambda: set_state(not state) - @component def Root(): is_mounted, toggle_is_mounted.current = use_toggle() @@ -53,10 +59,6 @@ def HasScript(): def test_script_re_run_on_content_change(driver, driver_wait, display): incr_count = Ref() - def use_counter(): - state, set_state = use_state(1) - return state, lambda: set_state(state + 1) - @component def HasScript(): count, incr_count.current = use_counter()