Update pc.form on_submit args (#1033)

This commit is contained in:
Nikhil Rao 2023-05-16 12:09:15 -07:00 committed by GitHub
parent 98f1b30735
commit e6a679d3a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 102 additions and 78 deletions

View File

@ -94,6 +94,13 @@ class Component(Base, ABC):
Raises:
TypeError: If an invalid prop is passed.
"""
# Set the id and children initially.
initial_kwargs = {
"id": kwargs.get("id"),
"children": kwargs.get("children", []),
}
super().__init__(**initial_kwargs)
# Get the component fields, triggers, and props.
fields = self.get_fields()
triggers = self.get_triggers()
@ -264,17 +271,15 @@ class Component(Base, ABC):
events=events, state_name=state_name, full_control=full_control
)
@classmethod
def get_triggers(cls) -> Set[str]:
def get_triggers(self) -> Set[str]:
"""Get the event triggers for the component.
Returns:
The event triggers.
"""
return EVENT_TRIGGERS | set(cls.get_controlled_triggers())
return EVENT_TRIGGERS | set(self.get_controlled_triggers())
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:
@ -488,7 +493,7 @@ class Component(Base, ABC):
# Add the hook code for the children.
for child in self.children:
code.update(child.get_hooks())
code |= child.get_hooks()
return code
@ -502,6 +507,20 @@ class Component(Base, ABC):
return None
return format.format_ref(self.id)
def get_refs(self) -> Set[str]:
"""Get the refs for the children of the component.
Returns:
The refs for the children.
"""
refs = set()
ref = self.get_ref()
if ref is not None:
refs.add(ref)
for child in self.children:
refs |= child.get_refs()
return refs
def get_custom_components(
self, seen: Optional[Set[str]] = None
) -> Set[CustomComponent]:
@ -565,7 +584,7 @@ class CustomComponent(Component):
library = f"/{constants.COMPONENTS_PATH}"
# The function that creates the component.
component_fn: Callable[..., Component]
component_fn: Callable[..., Component] = Component.create
# The props of the component.
props: Dict[str, Any] = {}

View File

@ -48,8 +48,7 @@ class Checkbox(ChakraComponent):
# The spacing between the checkbox and its label text (0.5rem)
spacing: Var[str]
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -16,8 +16,7 @@ class CopyToClipboard(Component):
# The text to copy when clicked.
text: Var[str]
@classmethod
def get_controlled_triggers(cls) -> Set[str]:
def get_controlled_triggers(self) -> Set[str]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -36,8 +36,7 @@ class Editable(ChakraComponent):
# The initial value of the Editable in both edit and preview mode.
default_value: Var[str]
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -1,6 +1,6 @@
"""Form components."""
from typing import Set
from typing import Dict
from pynecone.components.component import Component
from pynecone.components.libs.chakra import ChakraComponent
@ -14,14 +14,19 @@ class Form(ChakraComponent):
as_: Var[str] = "form" # type: ignore
@classmethod
def get_triggers(cls) -> Set[str]:
"""Get the event triggers for the component.
def get_controlled_triggers(self) -> Dict[str, Dict]:
"""Get the event triggers that pass the component's value to the handler.
Returns:
The event triggers.
A dict mapping the event trigger to the var that is passed to the handler.
"""
return super().get_triggers() | {"on_submit"}
# Send all the input refs to the handler.
return {
"on_submit": {
ref[4:]: Var.create(f"{ref}.current.value", is_local=False)
for ref in self.get_refs()
}
}
class FormControl(ChakraComponent):
@ -52,7 +57,7 @@ class FormControl(ChakraComponent):
input=None,
help_text=None,
error_message=None,
**props
**props,
) -> Component:
"""Create a form control component.

View File

@ -55,8 +55,7 @@ class Input(ChakraComponent):
{"/utils/state": {ImportVar(tag="set_val")}},
)
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -64,8 +64,7 @@ class NumberInput(ChakraComponent):
# "outline" | "filled" | "flushed" | "unstyled"
variant: Var[str]
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -55,8 +55,7 @@ class PinInput(ChakraComponent):
# "outline" | "flushed" | "filled" | "unstyled"
variant: Var[str]
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -23,8 +23,7 @@ class RadioGroup(ChakraComponent):
# The default value.
default_value: Var[Any]
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -43,8 +43,7 @@ class RangeSlider(ChakraComponent):
# The minimum distance between slider thumbs. Useful for preventing the thumbs from being too close together.
min_steps_between_thumbs: Var[int]
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -48,8 +48,7 @@ class Select(ChakraComponent):
# The size of the select.
size: Var[str]
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -61,8 +61,7 @@ class Slider(ChakraComponent):
# Maximum width of the slider.
max_w: Var[str]
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -41,8 +41,7 @@ class Switch(ChakraComponent):
# The color scheme of the switch (e.g. "blue", "green", "red", etc.)
color_scheme: Var[str]
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -42,8 +42,7 @@ class TextArea(ChakraComponent):
# "outline" | "filled" | "flushed" | "unstyled"
variant: Var[str]
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -80,8 +80,7 @@ class Upload(Component):
# Create the component.
return super().create(zone, on_drop=upload_file, **upload_props)
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Get the event triggers that pass the component's value to the handler.
Returns:

View File

@ -17,10 +17,10 @@ class Cond(Component):
cond: Var[Any]
# The component to render if the cond is true.
comp1: Component
comp1: Component = Fragment.create()
# The component to render if the cond is false.
comp2: Component
comp2: Component = Fragment.create()
@classmethod
def create(

View File

@ -4,6 +4,7 @@ from __future__ import annotations
from typing import Any, Callable, List
from pynecone.components.component import Component
from pynecone.components.layout.fragment import Fragment
from pynecone.components.tags import IterTag
from pynecone.vars import BaseVar, Var, get_unique_variable_name
@ -15,7 +16,7 @@ class Foreach(Component):
iterable: Var[List]
# A function from the render args to the component.
render_fn: Callable
render_fn: Callable = Fragment.create
@classmethod
def create(cls, iterable: Var[List], render_fn: Callable, **props) -> Foreach:

View File

@ -35,8 +35,7 @@ class Avatar(ChakraComponent):
# "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "full"
size: Var[str]
@classmethod
def get_triggers(cls) -> Set[str]:
def get_triggers(self) -> Set[str]:
"""Get the event triggers for the component.
Returns:

View File

@ -43,8 +43,7 @@ class Image(ChakraComponent):
# The image srcset attribute.
src_set: Var[str]
@classmethod
def get_triggers(cls) -> Set[str]:
def get_triggers(self) -> Set[str]:
"""Get the event triggers for the component.
Returns:

View File

@ -52,8 +52,7 @@ class AlertDialog(ChakraComponent):
# If true, the siblings of the modal will have `aria-hidden` set to true so that screen readers can only see the modal. This is commonly known as making the other elements **inert**
use_inert: Var[bool]
@classmethod
def get_triggers(cls) -> Set[str]:
def get_triggers(self) -> Set[str]:
"""Get the event triggers for the component.
Returns:

View File

@ -58,8 +58,7 @@ class Drawer(ChakraComponent):
# Variant of drawer
variant: Var[str]
@classmethod
def get_triggers(cls) -> Set[str]:
def get_triggers(self) -> Set[str]:
"""Get the event triggers for the component.
Returns:

View File

@ -60,8 +60,7 @@ class Menu(ChakraComponent):
# The CSS positioning strategy to use. ("fixed" | "absolute")
strategy: Var[str]
@classmethod
def get_triggers(cls) -> Set[str]:
def get_triggers(self) -> Set[str]:
"""Get the event triggers for the component.
Returns:

View File

@ -52,8 +52,7 @@ class Modal(ChakraComponent):
# A11y: If true, the siblings of the modal will have `aria-hidden` set to true so that screen readers can only see the modal. This is commonly known as making the other elements **inert**
use_inert: Var[bool]
@classmethod
def get_triggers(cls) -> Set[str]:
def get_triggers(self) -> Set[str]:
"""Get the event triggers for the component.
Returns:

View File

@ -75,8 +75,7 @@ class Popover(ChakraComponent):
# The interaction that triggers the popover. hover - means the popover will open when you hover with mouse or focus with keyboard on the popover trigger click - means the popover will open on click or press Enter to Space on keyboard ("click" | "hover")
trigger: Var[str]
@classmethod
def get_triggers(cls) -> Set[str]:
def get_triggers(self) -> Set[str]:
"""Get the event triggers for the component.
Returns:

View File

@ -62,8 +62,7 @@ class Tooltip(ChakraComponent):
# If true, the tooltip will wrap its children in a `<span/>` with `tabIndex=0`
should_wrap_children: Var[bool]
@classmethod
def get_triggers(cls) -> Set[str]:
def get_triggers(self) -> Set[str]:
"""Get the event triggers for the component.
Returns:

View File

@ -3,7 +3,6 @@
from __future__ import annotations
import json
import re
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple, Union
from plotly.graph_objects import Figure
@ -97,14 +96,10 @@ class Tag(Base):
prop = json.loads(to_json(prop))["data"] # type: ignore
# For dictionaries, convert any properties to strings.
else:
if isinstance(prop, dict):
# Convert any var keys to strings.
prop = {
key: str(val) if isinstance(val, Var) else val
for key, val in prop.items()
}
elif isinstance(prop, dict):
prop = format.format_dict(prop)
else:
# Dump the prop as JSON.
try:
prop = format.json_dumps(prop)
@ -113,11 +108,6 @@ class Tag(Base):
f"Could not format prop: {prop} of type {type(prop)}"
) from e
# This substitution is necessary to unwrap var values.
prop = re.sub('"{', "", prop)
prop = re.sub('}"', "", prop)
prop = re.sub('\\\\"', '"', prop)
# Wrap the variable in braces.
assert isinstance(prop, str), "The prop must be a string."
return format.wrap(prop, "{", check_first=False)

View File

@ -15,6 +15,7 @@ from pynecone import constants
from pynecone.utils import types
if TYPE_CHECKING:
from pynecone.components.component import ComponentStyle
from pynecone.event import EventChain, EventHandler, EventSpec
WRAP_MAP = {
@ -411,6 +412,33 @@ def format_ref(ref: str) -> str:
return f"ref_{clean_ref}"
def format_dict(prop: ComponentStyle) -> str:
"""Format a dict with vars potentially as values.
Args:
prop: The dict to format.
Returns:
The formatted dict.
"""
# Import here to avoid circular imports.
from pynecone.vars import Var
# Convert any var keys to strings.
prop = {key: str(val) if isinstance(val, Var) else val for key, val in prop.items()}
# Dump the dict to a string.
fprop = json_dumps(prop)
# This substitution is necessary to unwrap var values.
fprop = re.sub('"{', "", fprop)
fprop = re.sub('}"', "", fprop)
fprop = re.sub('\\\\"', '"', fprop)
# Return the formatted dict.
return fprop
def json_dumps(obj: Any) -> str:
"""Takes an object and returns a jsonified string.

View File

@ -101,6 +101,9 @@ class Var(ABC):
value = json.loads(to_json(value))["data"] # type: ignore
type_ = Figure
if isinstance(value, dict):
value = format.format_dict(value)
try:
name = value if isinstance(value, str) else json.dumps(value)
except TypeError as e:

View File

@ -62,8 +62,7 @@ def component2() -> Type[Component]:
# A test list prop.
arr: Var[List[str]]
@classmethod
def get_controlled_triggers(cls) -> Dict[str, Var]:
def get_controlled_triggers(self) -> Dict[str, Var]:
"""Test controlled triggers.
Returns:
@ -307,8 +306,8 @@ def test_get_controlled_triggers(component1, component2):
component1: A test component.
component2: A test component.
"""
assert component1.get_controlled_triggers() == dict()
assert set(component2.get_controlled_triggers()) == {"on_open", "on_close"}
assert component1().get_controlled_triggers() == dict()
assert set(component2().get_controlled_triggers()) == {"on_open", "on_close"}
def test_get_triggers(component1, component2):
@ -318,8 +317,8 @@ def test_get_triggers(component1, component2):
component1: A test component.
component2: A test component.
"""
assert component1.get_triggers() == EVENT_TRIGGERS
assert component2.get_triggers() == {"on_open", "on_close"} | EVENT_TRIGGERS
assert component1().get_triggers() == EVENT_TRIGGERS
assert component2().get_triggers() == {"on_open", "on_close"} | EVENT_TRIGGERS
def test_create_custom_component(my_component):