Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement script element #617

Merged
merged 6 commits into from
Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/client/packages/idom-client-react/src/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);

Expand Down
17 changes: 16 additions & 1 deletion src/idom/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
- :func:`template`
"""

from .core.proto import VdomDict
from .core.vdom import make_vdom_constructor


Expand Down Expand Up @@ -247,7 +248,21 @@
# Scripting
canvas = make_vdom_constructor("canvas")
noscript = make_vdom_constructor("noscript")
script = make_vdom_constructor("script")


def script(content: str) -> VdomDict:
"""Create a new `<{script}> <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/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]}


# Demarcating edits
del_ = make_vdom_constructor("del")
Expand Down
94 changes: 94 additions & 0 deletions tests/test_html.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from idom import component, html, use_state
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()

@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()

@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")