Allow pc.cond without else case (#453)

This commit is contained in:
Nikhil Rao 2023-02-05 17:50:34 -08:00 committed by GitHub
parent 0b2f1369a6
commit 8958f14778
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 126 additions and 60 deletions

View File

@ -1,7 +1,9 @@
"""Import all the components."""
from __future__ import annotations
from typing import TYPE_CHECKING
from pynecone import utils
from pynecone.propcond import PropCond
from .component import Component
from .datadisplay import *
@ -15,6 +17,10 @@ from .navigation import *
from .overlay import *
from .typography import *
if TYPE_CHECKING:
from typing import Any
from pynecone.var import Var
# Add the convenience methods for all the components.
locals().update(
{
@ -91,17 +97,32 @@ def mobile_and_tablet(*children, **props):
return Box.create(*children, **props, display=["block", "block", "block", "none"])
def cond(cond_var, c1, c2=None):
def cond(condition: Any, c1: Any, c2: Any = None):
"""Create a conditional component or Prop.
Args:
cond_var: The cond to determine which component to render.
condition: The cond to determine which component to render.
c1: The component or prop to render if the cond_var is true.
c2: The component or prop to render if the cond_var is false.
Returns:
The conditional component.
"""
if isinstance(c1, Component) and isinstance(c2, Component):
# Import here to avoid circular imports.
from .tags.tag import PropCond
from pynecone.var import Var
# Convert the condition to a Var.
cond_var = Var.create(condition)
assert cond_var is not None, "The condition must be set."
# If the first component is a component, create a Cond component.
if isinstance(c1, Component):
assert c2 is None or isinstance(
c2, Component
), "Both arguments must be components."
return Cond.create(cond_var, c1, c2)
# Otherwise, create a PropCond.
assert not isinstance(c2, Component), "Both arguments must be props."
return PropCond.create(cond_var, c1, c2)

View File

@ -12,7 +12,6 @@ from plotly.io import to_json
from pynecone import utils
from pynecone.base import Base
from pynecone.propcond import PropCond
from pynecone.event import EventChain
from pynecone.var import Var
@ -191,3 +190,54 @@ class Tag(Base):
Whether the prop is valid.
"""
return prop is not None and not (isinstance(prop, dict) and len(prop) == 0)
class PropCond(Base):
"""A conditional prop."""
# The condition to determine which prop to render.
cond: Var[Any]
# The prop to render if the condition is true.
prop1: Any
# The prop to render if the condition is false.
prop2: Any
@classmethod
def create(cls, cond: Var, prop1: Any, prop2: Any):
"""Create a conditional Prop.
Args:
cond: The cond to determine which prop to render.
prop1: The prop value to render if the cond is true.
prop2: The prop value to render if the cond is false.
Returns:
The conditional Prop.
Raises:
ValueError: If the condition or prop values are not set.
"""
if cond is None:
raise ValueError("The condition must be set.")
if prop1 is None or prop2 is None:
raise ValueError("Both prop values must be set.")
return cls(
cond=cond,
prop1=prop1,
prop2=prop2,
)
def __str__(self) -> str:
"""Render the prop as a React string.
Returns:
The React code to render the prop.
"""
return utils.format_cond(
cond=self.cond.full_name,
true_value=self.prop1,
false_value=self.prop2,
is_prop=True,
)

View File

@ -1,51 +0,0 @@
"""Create a Prop Condition."""
from typing import Any
from pynecone import utils
from pynecone.base import Base
from pynecone.var import Var
class PropCond(Base):
"""A conditional prop."""
# The condition to determine which prop to render.
cond: Var[Any]
# The prop to render if the condition is true.
prop1: Any
# The prop to render if the condition is false.
prop2: Any
@classmethod
def create(cls, cond: Var, prop1: Any, prop2: Any = None):
"""Create a conditional Prop.
Args:
cond: The cond to determine which prop to render.
prop1: The prop value to render if the cond is true.
prop2: The prop value to render if the cond is false.
Returns:
The conditional Prop.
"""
return cls(
cond=cond,
prop1=prop1,
prop2=prop2,
)
def __str__(self) -> str:
"""Render the prop as a React string.
Returns:
The React code to render the prop.
"""
assert self.cond is not None, "The condition must be set."
return utils.format_cond(
cond=self.cond.full_name,
true_value=self.prop1,
false_value=self.prop2,
is_prop=True,
)

View File

@ -1,7 +1,12 @@
from typing import Any
import pytest
import pynecone as pc
from pynecone.components import cond
from pynecone.components.layout.cond import Cond
from pynecone.components.layout.fragment import Fragment
from pynecone.components.tags.tag import PropCond
from pynecone.components.typography.text import Text
@ -28,7 +33,7 @@ def test_validate_cond(cond_state: pc.Var):
Args:
cond_state: A fixture.
"""
cond_component = Cond.create(
cond_component = cond(
cond_state.value,
Text.create("cond is True"),
Text.create("cond is False"),
@ -39,3 +44,45 @@ def test_validate_cond(cond_state: pc.Var):
"<Text>{`cond is True`}</Text> : "
"<Text>{`cond is False`}</Text>}"
)
@pytest.mark.parametrize(
"c1, c2",
[
(True, False),
(32, 0),
("hello", ""),
(2.3, 0.0),
],
)
def test_prop_cond(c1: Any, c2: Any):
"""Test if cond can be a prop.
Args:
c1: truth condition value
c2: false condition value
"""
prop_cond = cond(
True,
c1,
c2,
)
assert isinstance(prop_cond, PropCond)
assert prop_cond.prop1 == c1
assert prop_cond.prop2 == c2
assert prop_cond.cond == True
def test_cond_no_else():
"""Test if cond can be used without else"""
# Components should support the use of cond without else
comp = cond(True, Text.create("hello"))
assert isinstance(comp, Cond)
assert comp.cond == True
assert comp.comp1 == Text.create("hello")
assert comp.comp2 == Fragment.create()
# Props do not support the use of cond without else
with pytest.raises(ValueError):
prop_cond = cond(True, "hello")

View File

@ -5,9 +5,9 @@ import pytest
from pynecone.components import Box
from pynecone.components.tags import CondTag, IterTag, Tag
from pynecone.components.tags.tag import PropCond
from pynecone.event import EventChain, EventHandler, EventSpec
from pynecone.var import BaseVar, Var
from pynecone.propcond import PropCond
def mock_event(arg):

View File

@ -2,7 +2,7 @@ from typing import Any
import pytest
from pynecone.propcond import PropCond
from pynecone.components.tags.tag import PropCond
from pynecone.var import BaseVar, Var
from pynecone.utils import wrap
@ -21,7 +21,6 @@ def test_validate_propcond(prop1: Any, prop2: Any):
Args:
prop1: truth condition value
prop2: false condition value
"""
prop_cond = PropCond.create(
cond=BaseVar(name="cond_state.value", type_=str), prop1=prop1, prop2=prop2