Allow fstrings as component children (#890)

This commit is contained in:
Nikhil Rao 2023-04-26 19:32:51 -07:00 committed by GitHub
parent e03e5f8033
commit f0346506d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 75 additions and 2 deletions

View File

@ -263,6 +263,7 @@ class App(Base):
# Generate the component if it is a callable. # Generate the component if it is a callable.
try: try:
component = component if isinstance(component, Component) else component() component = component if isinstance(component, Component) else component()
component.set_state(self.state)
except TypeError as e: except TypeError as e:
message = str(e) message = str(e)
if "BaseVar" in message or "ComputedVar" in message: if "BaseVar" in message or "ComputedVar" in message:

View File

@ -27,4 +27,8 @@ class Bare(Component):
return cls(contents=str(contents)) # type: ignore return cls(contents=str(contents)) # type: ignore
def _render(self) -> Tag: def _render(self) -> Tag:
return Tagless(contents=str(self.contents)) contents = str(self.contents)
if self.state is not None:
check = f"{{{self.state.get_name()}"
contents = str(self.contents).replace(check, f"${check}")
return Tagless(contents=contents)

View File

@ -24,6 +24,9 @@ from pynecone.style import Style
from pynecone.utils import format, imports, path_ops, types from pynecone.utils import format, imports, path_ops, types
from pynecone.var import BaseVar, Var from pynecone.var import BaseVar, Var
if typing.TYPE_CHECKING:
from pynecone.state import State
class Component(Base, ABC): class Component(Base, ABC):
"""The base class for all Pynecone components.""" """The base class for all Pynecone components."""
@ -34,6 +37,9 @@ class Component(Base, ABC):
# The style of the component. # The style of the component.
style: Style = Style() style: Style = Style()
# The app state the component is connected to.
state: Optional[Type[State]] = None
# A mapping from event triggers to event chains. # A mapping from event triggers to event chains.
event_triggers: Dict[str, Union[EventChain, Var]] = {} event_triggers: Dict[str, Union[EventChain, Var]] = {}
@ -393,6 +399,19 @@ class Component(Base, ABC):
child.add_style(style) child.add_style(style)
return self return self
def set_state(self, state: Type[State]):
"""Set the state of the component and its children.
Args:
state: The state to set.
"""
# Set the state of the component.
self.state = state
# Set the state of the children.
for child in self.children:
child.set_state(state)
def render(self) -> str: def render(self) -> str:
"""Render the component. """Render the component.

View File

View File

@ -0,0 +1,25 @@
import pytest
from pynecone.components.base.bare import Bare
from pynecone.state import DefaultState
@pytest.mark.parametrize(
"contents,expected",
[
("hello", "hello"),
("{}", "{}"),
("{default_state.name}", "${default_state.name}"),
("{state.name}", "{state.name}"),
],
)
def test_fstrings(contents, expected):
"""Test that fstrings are rendered correctly.
Args:
contents: The contents of the component.
expected: The expected output.
"""
comp = Bare.create(contents)
comp.set_state(DefaultState)
assert str(comp) == expected

View File

@ -6,7 +6,7 @@ import pynecone as pc
from pynecone.components.component import Component, CustomComponent, custom_component from pynecone.components.component import Component, CustomComponent, custom_component
from pynecone.components.layout.box import Box from pynecone.components.layout.box import Box
from pynecone.event import EVENT_ARG, EVENT_TRIGGERS, EventHandler from pynecone.event import EVENT_ARG, EVENT_TRIGGERS, EventHandler
from pynecone.state import State from pynecone.state import DefaultState, State
from pynecone.style import Style from pynecone.style import Style
from pynecone.utils import imports from pynecone.utils import imports
from pynecone.var import Var from pynecone.var import Var
@ -434,3 +434,27 @@ def test_get_hooks_nested2(component3, component4):
).get_hooks() ).get_hooks()
== exp_hooks == exp_hooks
) )
def test_set_state(component1, component2, component3):
"""Test that setting the state of a component works.
Args:
component1: test component.
component2: another component.
component3: component with hooks defined.
"""
c2 = component2.create()
c3 = component3.create()
c1 = component1.create(c2, c3)
# State should be None by default.
assert c1.state is None
assert c2.state is None
assert c3.state is None
# Setting the parent state should set the child state.
c1.set_state(DefaultState)
assert c1.state == DefaultState
assert c2.state == DefaultState
assert c3.state == DefaultState