Skip to content

Commit d6156f0

Browse files
committed
progress towards #143
1 parent 7c67774 commit d6156f0

File tree

8 files changed

+180
-47
lines changed

8 files changed

+180
-47
lines changed

idom/client/__init__.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,17 @@
1414
WEB_MODULES = CLIENT_DIR / "web_modules"
1515

1616

17-
def core_module(name: str) -> str:
18-
path = f"../{CORE_MODULES.name}/{name}.js"
19-
if not core_module_exists(name):
20-
raise ValueError(f"Module '{path}' does not exist.")
21-
return path
22-
23-
24-
def core_module_exists(name: str) -> bool:
25-
return _find_module_os_path(CORE_MODULES, name) is not None
26-
27-
2817
def web_module(name: str) -> str:
2918
path = f"../{WEB_MODULES.name}/{name}.js"
3019
if not web_module_exists(name):
3120
raise ValueError(f"Module '{path}' does not exist.")
3221
return path
3322

3423

24+
def web_module_path(name: str) -> Optional[Path]:
25+
return _find_module_os_path(WEB_MODULES, name)
26+
27+
3528
def web_module_exists(name: str) -> bool:
3629
return _find_module_os_path(WEB_MODULES, name) is not None
3730

idom/core/events.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def __iter__(self) -> Iterator[str]:
129129
def __getitem__(self, key: str) -> "EventHandler":
130130
return self._handlers[key]
131131

132-
def __repr__(self) -> str:
132+
def __repr__(self) -> str: # pragma: no cover
133133
return repr(self._handlers)
134134

135135

@@ -193,11 +193,6 @@ def remove(self, function: EventHandlerFunction) -> None:
193193
"""
194194
self._handlers.remove(function)
195195

196-
async def __call__(self, data: List[Any]) -> Any:
197-
"""Trigger all callbacks in the event handler."""
198-
for handler in self._handlers:
199-
await handler(*data)
200-
201196
def serialize(self) -> Dict[str, Any]:
202197
"""Serialize the event handler."""
203198
return {
@@ -206,5 +201,13 @@ def serialize(self) -> Dict[str, Any]:
206201
"stopPropagation": self._stop_propogation,
207202
}
208203

209-
def __repr__(self) -> str:
204+
async def __call__(self, data: List[Any]) -> Any:
205+
"""Trigger all callbacks in the event handler."""
206+
for handler in self._handlers:
207+
await handler(*data)
208+
209+
def __contains__(self, function: Any) -> bool:
210+
return function in self._handlers
211+
212+
def __repr__(self) -> str: # pragma: no cover
210213
return f"{type(self).__name__}({self.serialize()})"

idom/widgets/input.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ class Input(Generic[_InputType], AbstractElement):
3333
"""
3434

3535
__slots__ = (
36+
"_type",
3637
"_value",
3738
"_cast",
3839
"_display_value",
39-
"_label",
4040
"_ignore_empty",
4141
"_events",
4242
"_attributes",
@@ -48,26 +48,23 @@ def __init__(
4848
value: _InputType = "", # type: ignore
4949
attributes: Optional[Dict[str, Any]] = None,
5050
cast: Callable[[str], _InputType] = _pass_through,
51-
label: Optional[str] = None,
5251
ignore_empty: bool = True,
5352
) -> None:
5453
super().__init__()
54+
self._type = type
5555
self._value = value
5656
self._display_value = str(value)
5757
self._cast = cast
58-
self._label = label
5958
self._ignore_empty = ignore_empty
6059
self._events = Events()
6160
self._attributes = attributes or {}
62-
self._attributes["type"] = type
6361
self_ref = ref(self)
6462

6563
@self._events.on("change")
6664
async def on_change(event: Dict[str, Any]) -> None:
6765
self_deref = self_ref()
6866
if self_deref is not None:
69-
value = self_deref._cast(event["value"])
70-
self_deref.update(value)
67+
self_deref._set_str_value(event["value"])
7168

7269
@property
7370
def value(self) -> _InputType:
@@ -86,22 +83,27 @@ def attributes(self) -> Dict[str, Any]:
8683
def update(self, value: _InputType) -> None:
8784
"""Update the current value of the input."""
8885
self._set_value(value)
89-
super().update()
9086

9187
async def render(self) -> VdomDict:
9288
input_element = html.input(
93-
self.attributes, {"value": self._display_value}, event_handlers=self.events,
89+
self.attributes,
90+
{"type": self._type, "value": self._display_value},
91+
event_handlers=self.events,
9492
)
95-
if self._label is not None:
96-
return html.label([self._label, input_element])
97-
else:
98-
return input_element
93+
return input_element
94+
95+
def _set_str_value(self, value: str) -> None:
96+
self._display_value = value
97+
super().update()
98+
print(value)
99+
if not value and self._ignore_empty:
100+
return
101+
self._value = self._cast(value)
99102

100103
def _set_value(self, value: _InputType) -> None:
101104
self._display_value = str(value)
102-
if self._ignore_empty and not value:
103-
return
104105
self._value = value
106+
super().update()
105107

106-
def __repr__(self) -> str:
108+
def __repr__(self) -> str: # pragma: no cover
107109
return f"{type(self).__name__}({self.value!r})"

idom/widgets/utils.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class Module:
2525
An :class:`Import` element for the newly defined module.
2626
"""
2727

28-
__slots__ = "_module"
28+
__slots__ = ("_module", "_name", "_installed")
2929

3030
def __init__(
3131
self,
@@ -34,28 +34,52 @@ def __init__(
3434
source: Optional[IO] = None,
3535
replace: bool = False,
3636
) -> None:
37+
self._installed = False
3738
if install and source:
3839
raise ValueError("Both 'install' and 'source' were given.")
3940
elif (install or source) and not replace and client.web_module_exists(name):
4041
self._module = client.web_module(name)
42+
self._installed = True
43+
self._name = name
4144
elif source is not None:
4245
client.define_web_module(name, source.read())
4346
self._module = client.web_module(name)
47+
self._installed = True
48+
self._name = name
4449
elif isinstance(install, str):
4550
client.install({install: name})
4651
self._module = client.web_module(name)
52+
self._installed = True
53+
self._name = name
4754
elif install is True:
4855
client.install({name: name})
4956
self._module = client.web_module(name)
57+
self._installed = True
58+
self._name = name
5059
else:
5160
self._module = name
5261

62+
@property
63+
def name(self) -> str:
64+
if not self._installed:
65+
raise ValueError("Module is not installed locally")
66+
return self._name
67+
68+
@property
69+
def url(self) -> str:
70+
return self._module
71+
5372
def Import(self, name: str, *args, **kwargs) -> "Import":
5473
return Import(self._module, name, *args, **kwargs)
5574

5675
def delete(self) -> None:
76+
if not self._installed:
77+
raise ValueError("Module is not installed locally")
5778
client.delete_web_module(self._module)
5879

80+
def __repr__(self) -> str: # pragma: no cover
81+
return f"{type(self).__name__}({self._module!r})"
82+
5983

6084
class Import:
6185
"""Import a react module
@@ -87,7 +111,7 @@ def __init__(
87111
def __call__(self, *args: Any, **kwargs: Any,) -> VdomDict:
88112
return self._constructor(import_source=self._import_source, *args, **kwargs)
89113

90-
def __repr__(self) -> str:
114+
def __repr__(self) -> str: # pragma: no cover
91115
items = ", ".join(f"{k}={v!r}" for k, v in self._import_source.items())
92116
return f"{type(self).__name__}({items})"
93117

setup.cfg

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ exclude = idom/client/node_modules/*
2727
testpaths = tests
2828
xfail_strict = True
2929
addopts = --cov=idom
30+
markers =
31+
slow: marks tests as slow (deselect with '-m "not slow"')
3032

3133
[coverage:report]
32-
fail_under = 93
34+
fail_under = 95
3335
show_missing = True
3436
skip_covered = True
3537
sort = Miss

tests/driver_utils.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from selenium.webdriver.remote.webelement import WebElement
33

44

5-
def send_keys(element: WebElement, *values: Any) -> None:
6-
for keys in values:
7-
for char in keys:
8-
element.send_keys(char)
5+
def send_keys(element: WebElement, keys: Any) -> None:
6+
for char in keys:
7+
element.send_keys(char)

tests/test_core/test_events.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ async def key_press_handler():
1515

1616
assert isinstance(events["onClick"], EventHandler)
1717
assert isinstance(events["onKeyPress"], EventHandler)
18+
assert "onClick" in events
19+
assert "onKeyPress" in events
20+
assert len(events) == 2
1821

1922

2023
def test_event_handler_serialization():
@@ -51,3 +54,14 @@ async def callback_2(event):
5154
await event_handler([{}])
5255

5356
assert calls == [1, 2]
57+
58+
59+
def test_remove_event_handlers():
60+
def my_callback(event):
61+
...
62+
63+
events = EventHandler()
64+
events.add(my_callback)
65+
assert my_callback in events
66+
events.remove(my_callback)
67+
assert my_callback not in events

0 commit comments

Comments
 (0)