Allow pc.cond without else case (#453)
This commit is contained in:
parent
0b2f1369a6
commit
8958f14778
@ -1,7 +1,9 @@
|
|||||||
"""Import all the components."""
|
"""Import all the components."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from pynecone import utils
|
from pynecone import utils
|
||||||
from pynecone.propcond import PropCond
|
|
||||||
|
|
||||||
from .component import Component
|
from .component import Component
|
||||||
from .datadisplay import *
|
from .datadisplay import *
|
||||||
@ -15,6 +17,10 @@ from .navigation import *
|
|||||||
from .overlay import *
|
from .overlay import *
|
||||||
from .typography import *
|
from .typography import *
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Any
|
||||||
|
from pynecone.var import Var
|
||||||
|
|
||||||
# Add the convenience methods for all the components.
|
# Add the convenience methods for all the components.
|
||||||
locals().update(
|
locals().update(
|
||||||
{
|
{
|
||||||
@ -91,17 +97,32 @@ def mobile_and_tablet(*children, **props):
|
|||||||
return Box.create(*children, **props, display=["block", "block", "block", "none"])
|
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.
|
"""Create a conditional component or Prop.
|
||||||
|
|
||||||
Args:
|
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.
|
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.
|
c2: The component or prop to render if the cond_var is false.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The conditional component.
|
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)
|
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)
|
return PropCond.create(cond_var, c1, c2)
|
||||||
|
@ -12,7 +12,6 @@ from plotly.io import to_json
|
|||||||
|
|
||||||
from pynecone import utils
|
from pynecone import utils
|
||||||
from pynecone.base import Base
|
from pynecone.base import Base
|
||||||
from pynecone.propcond import PropCond
|
|
||||||
from pynecone.event import EventChain
|
from pynecone.event import EventChain
|
||||||
from pynecone.var import Var
|
from pynecone.var import Var
|
||||||
|
|
||||||
@ -191,3 +190,54 @@ class Tag(Base):
|
|||||||
Whether the prop is valid.
|
Whether the prop is valid.
|
||||||
"""
|
"""
|
||||||
return prop is not None and not (isinstance(prop, dict) and len(prop) == 0)
|
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,
|
||||||
|
)
|
||||||
|
@ -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,
|
|
||||||
)
|
|
@ -1,7 +1,12 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import pynecone as pc
|
import pynecone as pc
|
||||||
|
from pynecone.components import cond
|
||||||
from pynecone.components.layout.cond 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
|
from pynecone.components.typography.text import Text
|
||||||
|
|
||||||
|
|
||||||
@ -28,7 +33,7 @@ def test_validate_cond(cond_state: pc.Var):
|
|||||||
Args:
|
Args:
|
||||||
cond_state: A fixture.
|
cond_state: A fixture.
|
||||||
"""
|
"""
|
||||||
cond_component = Cond.create(
|
cond_component = cond(
|
||||||
cond_state.value,
|
cond_state.value,
|
||||||
Text.create("cond is True"),
|
Text.create("cond is True"),
|
||||||
Text.create("cond is False"),
|
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 True`}</Text> : "
|
||||||
"<Text>{`cond is False`}</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")
|
||||||
|
@ -5,9 +5,9 @@ import pytest
|
|||||||
|
|
||||||
from pynecone.components import Box
|
from pynecone.components import Box
|
||||||
from pynecone.components.tags import CondTag, IterTag, Tag
|
from pynecone.components.tags import CondTag, IterTag, Tag
|
||||||
|
from pynecone.components.tags.tag import PropCond
|
||||||
from pynecone.event import EventChain, EventHandler, EventSpec
|
from pynecone.event import EventChain, EventHandler, EventSpec
|
||||||
from pynecone.var import BaseVar, Var
|
from pynecone.var import BaseVar, Var
|
||||||
from pynecone.propcond import PropCond
|
|
||||||
|
|
||||||
|
|
||||||
def mock_event(arg):
|
def mock_event(arg):
|
||||||
|
@ -2,7 +2,7 @@ from typing import Any
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from pynecone.propcond import PropCond
|
from pynecone.components.tags.tag import PropCond
|
||||||
from pynecone.var import BaseVar, Var
|
from pynecone.var import BaseVar, Var
|
||||||
from pynecone.utils import wrap
|
from pynecone.utils import wrap
|
||||||
|
|
||||||
@ -21,7 +21,6 @@ def test_validate_propcond(prop1: Any, prop2: Any):
|
|||||||
Args:
|
Args:
|
||||||
prop1: truth condition value
|
prop1: truth condition value
|
||||||
prop2: false condition value
|
prop2: false condition value
|
||||||
|
|
||||||
"""
|
"""
|
||||||
prop_cond = PropCond.create(
|
prop_cond = PropCond.create(
|
||||||
cond=BaseVar(name="cond_state.value", type_=str), prop1=prop1, prop2=prop2
|
cond=BaseVar(name="cond_state.value", type_=str), prop1=prop1, prop2=prop2
|
||||||
|
Loading…
Reference in New Issue
Block a user