diff --git a/pynecone/components/__init__.py b/pynecone/components/__init__.py
index b20e71462..b3c0f15d0 100644
--- a/pynecone/components/__init__.py
+++ b/pynecone/components/__init__.py
@@ -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)
diff --git a/pynecone/components/tags/tag.py b/pynecone/components/tags/tag.py
index 513cef436..e20a62b49 100644
--- a/pynecone/components/tags/tag.py
+++ b/pynecone/components/tags/tag.py
@@ -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,
+ )
diff --git a/pynecone/propcond.py b/pynecone/propcond.py
deleted file mode 100644
index 1352dcae4..000000000
--- a/pynecone/propcond.py
+++ /dev/null
@@ -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,
- )
diff --git a/tests/components/layout/test_cond.py b/tests/components/layout/test_cond.py
index 8c956c94a..4a9e5b280 100644
--- a/tests/components/layout/test_cond.py
+++ b/tests/components/layout/test_cond.py
@@ -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):
"{`cond is True`} : "
"{`cond is False`}}"
)
+
+
+@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")
diff --git a/tests/components/test_tag.py b/tests/components/test_tag.py
index eb3229643..8593025ee 100644
--- a/tests/components/test_tag.py
+++ b/tests/components/test_tag.py
@@ -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):
diff --git a/tests/test_propcond.py b/tests/test_propcond.py
index 3c90653a2..2019870bf 100644
--- a/tests/test_propcond.py
+++ b/tests/test_propcond.py
@@ -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