Skip to content

Commit 5790e11

Browse files
committed
Rename "jsExecutables" to "inlineJavascript"
1 parent d809007 commit 5790e11

File tree

9 files changed

+51
-44
lines changed

9 files changed

+51
-44
lines changed

Diff for: docs/source/about/changelog.rst

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Unreleased
3030
- :pull:`1281` - Added ``reactpy.Vdom`` primitive interface for creating VDOM dictionaries.
3131
- :pull:`1281` - Added type hints to ``reactpy.html`` attributes.
3232
- :pull:`1285` - Added support for nested components in web modules
33+
- :pull:`1289` - Added support for inline JavaScript as event handlers or other attributes that expect a callable
3334

3435
**Changed**
3536

Diff for: src/js/packages/@reactpy/client/src/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export type ReactPyVdom = {
5353
children?: (ReactPyVdom | string)[];
5454
error?: string;
5555
eventHandlers?: { [key: string]: ReactPyVdomEventHandler };
56-
jsExecutables?: { [key: string]: string };
56+
inlineJavascript?: { [key: string]: string };
5757
importSource?: ReactPyVdomImportSource;
5858
};
5959

Diff for: src/js/packages/@reactpy/client/src/vdom.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ export function createAttributes(
190190
),
191191
),
192192
...Object.fromEntries(
193-
Object.entries(model.jsExecutables || {}).map(([name, executable]) =>
194-
createJSExecutable(name, executable),
193+
Object.entries(model.inlineJavascript || {}).map(([name, inlineJavaScript]) =>
194+
createInlineJavascript(name, inlineJavaScript),
195195
),
196196
),
197197
}),
@@ -223,13 +223,13 @@ function createEventHandler(
223223
return [name, eventHandler];
224224
}
225225

226-
function createJSExecutable(
226+
function createInlineJavascript(
227227
name: string,
228-
executable: string,
228+
inlineJavaScript: string,
229229
): [string, () => void] {
230230
const wrappedExecutable = function (...args: any[]) {
231231
function handleExecution(...args: any[]) {
232-
const evalResult = eval(executable);
232+
const evalResult = eval(inlineJavaScript);
233233
if (typeof evalResult == "function") {
234234
return evalResult(...args);
235235
}

Diff for: src/reactpy/core/layout.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,9 @@ def _render_model_attributes(
262262
attrs = raw_model["attributes"].copy()
263263
new_state.model.current["attributes"] = attrs
264264

265-
if "jsExecutables" in raw_model:
266-
executables = raw_model["jsExecutables"].copy()
267-
new_state.model.current["jsExecutables"] = executables
265+
if "inlineJavascript" in raw_model:
266+
inline_javascript = raw_model["inlineJavascript"].copy()
267+
new_state.model.current["inlineJavascript"] = inline_javascript
268268

269269
if old_state is None:
270270
self._render_model_event_handlers_without_old_state(

Diff for: src/reactpy/core/vdom.py

+13-13
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@
2525
EventHandlerType,
2626
ImportSourceDict,
2727
JavaScript,
28-
JSExecutableDict,
28+
InlineJavascriptDict,
2929
VdomAttributes,
3030
VdomChildren,
3131
VdomDict,
3232
VdomJson,
3333
)
3434

35-
EVENT_ATTRIBUTE_PATTERN = re.compile(r"^on\w+")
35+
EVENT_ATTRIBUTE_PATTERN = re.compile(r"^on[A-Z]\w+")
3636

3737
VDOM_JSON_SCHEMA = {
3838
"$schema": "http://json-schema.org/draft-07/schema",
@@ -47,7 +47,7 @@
4747
"children": {"$ref": "#/definitions/elementChildren"},
4848
"attributes": {"type": "object"},
4949
"eventHandlers": {"$ref": "#/definitions/elementEventHandlers"},
50-
"jsExecutables": {"$ref": "#/definitions/elementJSExecutables"},
50+
"inlineJavascript": {"$ref": "#/definitions/elementInlineJavascripts"},
5151
"importSource": {"$ref": "#/definitions/importSource"},
5252
},
5353
# The 'tagName' is required because its presence is a useful indicator of
@@ -77,7 +77,7 @@
7777
},
7878
"required": ["target"],
7979
},
80-
"elementJSExecutables": {
80+
"elementInlineJavascripts": {
8181
"type": "object",
8282
"patternProperties": {
8383
".*": "str",
@@ -172,8 +172,8 @@ def __call__(
172172
"""The entry point for the VDOM API, for example reactpy.html(<WE_ARE_HERE>)."""
173173
attributes, children = separate_attributes_and_children(attributes_and_children)
174174
key = attributes.get("key", None)
175-
attributes, event_handlers, js_executables = (
176-
separate_attributes_handlers_and_executables(attributes)
175+
attributes, event_handlers, inline_javascript = (
176+
separate_attributes_handlers_and_inline_javascript(attributes)
177177
)
178178
if REACTPY_CHECK_JSON_ATTRS.current:
179179
json.dumps(attributes)
@@ -194,7 +194,7 @@ def __call__(
194194
**({"children": children} if children else {}),
195195
**({"attributes": attributes} if attributes else {}),
196196
**({"eventHandlers": event_handlers} if event_handlers else {}),
197-
**({"jsExecutables": js_executables} if js_executables else {}),
197+
**({"inlineJavascript": inline_javascript} if inline_javascript else {}),
198198
**({"importSource": self.import_source} if self.import_source else {}),
199199
}
200200

@@ -227,26 +227,26 @@ def separate_attributes_and_children(
227227
return _attributes, _children
228228

229229

230-
def separate_attributes_handlers_and_executables(
230+
def separate_attributes_handlers_and_inline_javascript(
231231
attributes: Mapping[str, Any],
232-
) -> tuple[VdomAttributes, EventHandlerDict, JSExecutableDict]:
232+
) -> tuple[VdomAttributes, EventHandlerDict, InlineJavascriptDict]:
233233
_attributes: VdomAttributes = {}
234234
_event_handlers: dict[str, EventHandlerType] = {}
235-
_js_executables: dict[str, JavaScript] = {}
235+
_inline_javascript: dict[str, JavaScript] = {}
236236

237237
for k, v in attributes.items():
238238
if callable(v):
239239
_event_handlers[k] = EventHandler(to_event_handler_function(v))
240240
elif isinstance(v, EventHandler):
241241
_event_handlers[k] = v
242242
elif EVENT_ATTRIBUTE_PATTERN.match(k) and isinstance(v, str):
243-
_js_executables[k] = JavaScript(v)
243+
_inline_javascript[k] = JavaScript(v)
244244
elif isinstance(v, JavaScript):
245-
_js_executables[k] = v
245+
_inline_javascript[k] = v
246246
else:
247247
_attributes[k] = v
248248

249-
return _attributes, _event_handlers, _js_executables
249+
return _attributes, _event_handlers, _inline_javascript
250250

251251

252252
def _flatten_children(children: Sequence[Any]) -> list[Any]:

Diff for: src/reactpy/transforms.py

+14-9
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,21 @@ def normalize_style_attributes(self, vdom: dict[str, Any]) -> None:
3535
if ":" in part
3636
)
3737
}
38-
38+
3939
@staticmethod
40-
def html_props_to_reactjs(vdom: VdomDict) -> None:
41-
"""Convert HTML prop names to their ReactJS equivalents."""
42-
if "attributes" in vdom:
43-
items = cast(VdomAttributes, vdom["attributes"].items())
44-
vdom["attributes"] = cast(
45-
VdomAttributes,
46-
{REACT_PROP_SUBSTITUTIONS.get(k, k): v for k, v in items},
47-
)
40+
def _attributes_to_reactjs(attributes: VdomAttributes):
41+
"""Convert HTML attribute names to their ReactJS equivalents.
42+
43+
This method is private because it is called prior to instantiating a
44+
Vdom class from a parsed html string, so it does not need to be called
45+
as part of this class's instantiation (see comments in __init__ above).
46+
"""
47+
attrs = cast(VdomAttributes, attributes.items())
48+
attrs = cast(
49+
VdomAttributes,
50+
{REACT_PROP_SUBSTITUTIONS.get(k, k): v for k, v in attrs},
51+
)
52+
return attrs
4853

4954
@staticmethod
5055
def textarea_children_to_prop(vdom: VdomDict) -> None:

Diff for: src/reactpy/types.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ class DangerouslySetInnerHTML(TypedDict):
768768
"children",
769769
"attributes",
770770
"eventHandlers",
771-
"jsExecutables",
771+
"inlineJavascript",
772772
"importSource",
773773
]
774774
ALLOWED_VDOM_KEYS = {
@@ -777,7 +777,7 @@ class DangerouslySetInnerHTML(TypedDict):
777777
"children",
778778
"attributes",
779779
"eventHandlers",
780-
"jsExecutables",
780+
"inlineJavascript",
781781
"importSource",
782782
}
783783

@@ -790,7 +790,7 @@ class VdomTypeDict(TypedDict):
790790
children: NotRequired[Sequence[ComponentType | VdomChild]]
791791
attributes: NotRequired[VdomAttributes]
792792
eventHandlers: NotRequired[EventHandlerDict]
793-
jsExecutables: NotRequired[JSExecutableDict]
793+
inlineJavascript: NotRequired[InlineJavascriptDict]
794794
importSource: NotRequired[ImportSourceDict]
795795

796796

@@ -821,7 +821,7 @@ def __getitem__(self, key: Literal["attributes"]) -> VdomAttributes: ...
821821
@overload
822822
def __getitem__(self, key: Literal["eventHandlers"]) -> EventHandlerDict: ...
823823
@overload
824-
def __getitem__(self, key: Literal["jsExecutables"]) -> JSExecutableDict: ...
824+
def __getitem__(self, key: Literal["inlineJavascript"]) -> InlineJavascriptDict: ...
825825
@overload
826826
def __getitem__(self, key: Literal["importSource"]) -> ImportSourceDict: ...
827827
def __getitem__(self, key: VdomDictKeys) -> Any:
@@ -845,7 +845,7 @@ def __setitem__(
845845
) -> None: ...
846846
@overload
847847
def __setitem__(
848-
self, key: Literal["jsExecutables"], value: JSExecutableDict
848+
self, key: Literal["inlineJavascript"], value: InlineJavascriptDict
849849
) -> None: ...
850850
@overload
851851
def __setitem__(
@@ -880,7 +880,7 @@ class VdomJson(TypedDict):
880880
children: NotRequired[list[Any]]
881881
attributes: NotRequired[VdomAttributes]
882882
eventHandlers: NotRequired[dict[str, JsonEventTarget]]
883-
jsExecutables: NotRequired[dict[str, JavaScript]]
883+
inlineJavascript: NotRequired[dict[str, JavaScript]]
884884
importSource: NotRequired[JsonImportSource]
885885

886886

@@ -938,11 +938,11 @@ class EventHandlerType(Protocol):
938938
EventHandlerDict: TypeAlias = dict[str, EventHandlerType]
939939
"""A dict mapping between event names to their handlers"""
940940

941-
JSExecutableMapping = Mapping[str, JavaScript]
942-
"""A generic mapping between attribute names to their javascript"""
941+
InlineJavascriptMapping = Mapping[str, JavaScript]
942+
"""A generic mapping between attribute names to their inline javascript"""
943943

944-
JSExecutableDict: TypeAlias = dict[str, JavaScript]
945-
"""A dict mapping between attribute names to their javascript"""
944+
InlineJavascriptDict: TypeAlias = dict[str, JavaScript]
945+
"""A dict mapping between attribute names to their inline javascript"""
946946

947947

948948
class VdomConstructor(Protocol):

Diff for: src/reactpy/utils.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,11 @@ def _etree_to_vdom(
147147

148148
# Recursively call _etree_to_vdom() on all children
149149
children = _generate_vdom_children(node, transforms, intercept_links)
150+
attributes = RequiredTransforms._attributes_to_reactjs(dict(node.items()))
150151

151152
# Convert the lxml node to a VDOM dict
152153
constructor = getattr(html, str(node.tag))
153-
el = constructor(dict(node.items()), children)
154+
el = constructor(attributes, children)
154155

155156
# Perform necessary transformations on the VDOM attributes to meet VDOM spec
156157
RequiredTransforms(el, intercept_links)

Diff for: tests/test_utils.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,12 @@ def test_string_to_reactpy(case):
194194
"key": "my-key",
195195
},
196196
},
197-
# 9: Includes `jsExecutables` attribue
197+
# 9: Includes `inlineJavascript` attribue
198198
{
199199
"source": """<button onclick="this.innerText = 'CLICKED'">Click Me</button>""",
200200
"model": {
201201
"tagName": "button",
202-
"jsExecutables": {"onclick": "this.innerText = 'CLICKED'"},
202+
"inlineJavascript": {"onClick": "this.innerText = 'CLICKED'"},
203203
"children": ["Click Me"],
204204
},
205205
},

0 commit comments

Comments
 (0)