Skip to content

Commit 5c060e5

Browse files
authored
Merge branch 'main' into ahmad/react-flow-docs
2 parents b9bcd2f + 6ac4492 commit 5c060e5

File tree

3 files changed

+74
-8
lines changed

3 files changed

+74
-8
lines changed

docs/wrapping-react/props.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,49 @@ class EventHandlerComponent(MyBaseComponent):
154154

155155
```md alert info
156156
# Custom event specs have a few use case where they are particularly useful. If the event returns non-serializable data, you can filter them out so the event can be sent to the backend. You can also use them to transform the data before sending it to the backend.
157+
```
158+
159+
### Emulating Event Handler Behavior Outside a Component
160+
161+
In some instances, you may need to replicate the special behavior applied to
162+
event handlers from outside of a component context. For example if the component
163+
to be wrapped requires event callbacks passed in a dictionary, this can be
164+
achieved by directly instantiating an `EventChain`.
165+
166+
A real-world example of this is the `onEvents` prop of
167+
[`echarts-for-react`](https://www.npmjs.com/package/echarts-for-react) library,
168+
which, unlike a normal event handler, expects a mapping of event handlers like:
169+
170+
```javascript
171+
<ReactECharts
172+
option={this.getOption()}
173+
style={{ height: '300px', width: '100%' }}
174+
onEvents={{
175+
'click': this.onChartClick,
176+
'legendselectchanged': this.onChartLegendselectchanged
177+
}}
178+
/>
179+
```
180+
181+
To achieve this in Reflex, you can create an explicit `EventChain` for each
182+
event handler:
183+
184+
```python
185+
@classmethod
186+
def create(cls, *children, **props):
187+
on_events = props.pop("on_events", {})
188+
189+
event_chains = {}
190+
for event_name, handler in on_events.items():
191+
# Convert the EventHandler/EventSpec/lambda to an EventChain
192+
event_chains[event_name] = rx.EventChain.create(
193+
handler,
194+
args_spec=rx.event.no_args_event_spec,
195+
key=event_name,
196+
)
197+
if on_events:
198+
props["on_events"] = event_chains
199+
200+
# Create the component instance
201+
return super().create(*children, **props)
157202
```

pcweb/pages/docs/apiref.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
rx.Base,
1010
rx.Component,
1111
rx.ComponentState,
12-
rx.Config,
12+
(rx.Config, rx.config.BaseConfig),
1313
rx.event.Event,
1414
rx.event.EventHandler,
1515
rx.event.EventSpec,
@@ -26,9 +26,17 @@
2626

2727
pages = []
2828
for module in modules:
29+
if isinstance(module, tuple):
30+
module, *extra_modules = module
31+
extra_fields = []
32+
for extra_module in extra_modules:
33+
s_extra = Source(module=extra_module)
34+
extra_fields.extend(s_extra.get_fields())
35+
else:
36+
extra_fields = None
2937
s = Source(module=module)
3038
name = module.__name__.lower()
31-
docs = generate_docs(name, s)
39+
docs = generate_docs(name, s, extra_fields=extra_fields)
3240
title = name.replace("_", " ").title()
3341
page_data = docpage(f"/docs/api-reference/{name}/", title)(docs)
3442
page_data.title = page_data.title.split("·")[0].strip()

pcweb/pages/docs/source.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import dataclasses
12
import inspect
23
import re
34

45
# Get the comment for a specific field.
5-
from typing import Callable, Type
6+
from typing import Callable, ClassVar, Type, get_origin, get_type_hints
67

78
import reflex as rx
89
from pcweb.templates.docpage import h1_comp, h2_comp
@@ -54,9 +55,11 @@ def get_class_fields(self) -> list[dict]:
5455
return out
5556

5657
def get_fields(self) -> list[dict]:
57-
if not issubclass(self.module, rx.Base):
58-
return []
59-
return self.get_annotations(self.module.__fields__)
58+
if dataclasses.is_dataclass(self.module):
59+
return self.get_annotations({f.name: f for f in dataclasses.fields(self.module)})
60+
elif isinstance(self.module, type) and issubclass(self.module, rx.Base):
61+
return self.get_annotations(self.module.__fields__)
62+
return []
6063

6164
def get_methods(self):
6265
return [
@@ -170,8 +173,16 @@ def get_annotations(self, props) -> list[dict]:
170173

171174

172175
def format_field(field):
173-
type_ = field["prop"].type_
176+
prop = field["prop"]
177+
try:
178+
type_ = prop.type_
179+
except AttributeError:
180+
type_ = prop.type
174181
default = field["prop"].default
182+
if default is dataclasses.MISSING:
183+
default = None
184+
else:
185+
default = repr(default)
175186
type_str = type_.__name__ if hasattr(type_, "__name__") else str(type_)
176187
if default:
177188
type_str += f" = {default}"
@@ -217,8 +228,10 @@ def format_fields(headers, fields):
217228
)
218229

219230

220-
def generate_docs(title: str, s: Source):
231+
def generate_docs(title: str, s: Source, extra_fields: list[dict] | None = None):
221232
fields = s.get_fields()
233+
if extra_fields:
234+
fields.extend(extra_fields)
222235
class_fields = s.get_class_fields()
223236
return rx.box(
224237
h1_comp(text=title.title()),

0 commit comments

Comments
 (0)