瀏覽代碼

Tabs validate parent is proper tab container (#2463)

Tianze 1 年之前
父節點
當前提交
209c5fab7b

+ 8 - 0
reflex/components/chakra/disclosure/tabs.py

@@ -90,20 +90,28 @@ class Tab(ChakraComponent):
     # The id of the panel.
     panel_id: Var[str]
 
+    _valid_parents: List[str] = ["TabList"]
+
 
 class TabList(ChakraComponent):
     """Wrapper for the Tab components."""
 
     tag = "TabList"
 
+    _valid_parents: List[str] = ["Tabs"]
+
 
 class TabPanels(ChakraComponent):
     """Wrapper for the Tab components."""
 
     tag = "TabPanels"
 
+    _valid_parents: List[str] = ["Tabs"]
+
 
 class TabPanel(ChakraComponent):
     """An element that contains the content associated with a tab."""
 
     tag = "TabPanel"
+
+    _valid_parents: List[str] = ["TabPanels"]

+ 17 - 1
reflex/components/component.py

@@ -155,6 +155,9 @@ class Component(BaseComponent, ABC):
     # only components that are allowed as children
     _valid_children: List[str] = []
 
+    # only components that are allowed as parent
+    _valid_parents: List[str] = []
+
     # custom attribute
     custom_attrs: Dict[str, Union[Var, str]] = {}
 
@@ -651,7 +654,8 @@ class Component(BaseComponent, ABC):
             children: The children of the component.
 
         """
-        if not self._invalid_children and not self._valid_children:
+        skip_parentable = all(child._valid_parents == [] for child in children)
+        if not self._invalid_children and not self._valid_children and skip_parentable:
             return
 
         comp_name = type(self).__name__
@@ -671,6 +675,15 @@ class Component(BaseComponent, ABC):
                     f"The component `{comp_name}` only allows the components: {valid_child_list} as children. Got `{child_name}` instead."
                 )
 
+        def validate_vaild_parent(child_name, valid_parents):
+            if comp_name not in valid_parents:
+                valid_parent_list = ", ".join(
+                    [f"`{v_parent}`" for v_parent in valid_parents]
+                )
+                raise ValueError(
+                    f"The component `{child_name}` can only be a child of the components: {valid_parent_list}. Got `{comp_name}` instead."
+                )
+
         for child in children:
             name = type(child).__name__
 
@@ -680,6 +693,9 @@ class Component(BaseComponent, ABC):
             if self._valid_children:
                 validate_valid_child(name)
 
+            if child._valid_parents:
+                validate_vaild_parent(name, child._valid_parents)
+
     @staticmethod
     def _get_vars_from_event_triggers(
         event_triggers: dict[str, EventChain | Var],

+ 1 - 0
scripts/pyi_generator.py

@@ -55,6 +55,7 @@ EXCLUDED_PROPS = [
     "_invalid_children",
     "_memoization_mode",
     "_valid_children",
+    "_valid_parents",
 ]
 
 DEFAULT_TYPING_IMPORTS = {

+ 16 - 0
tests/components/test_component.py

@@ -137,6 +137,8 @@ def component5() -> Type[Component]:
 
         _valid_children: List[str] = ["Text"]
 
+        _valid_parents: List[str] = ["Text"]
+
     return TestComponent5
 
 
@@ -569,6 +571,20 @@ def test_unsupported_child_components(fixture, request):
     )
 
 
+def test_unsupported_parent_components(component5):
+    """Test that a value error is raised when an component is not in _valid_parents of one of its children.
+
+    Args:
+        component5: component with valid parent of "Text" only
+    """
+    with pytest.raises(ValueError) as err:
+        rx.Box(children=[component5.create()])
+    assert (
+        err.value.args[0]
+        == f"The component `{component5.__name__}` can only be a child of the components: `{component5._valid_parents[0]}`. Got `Box` instead."
+    )
+
+
 @pytest.mark.parametrize("fixture", ["component5", "component7"])
 def test_component_with_only_valid_children(fixture, request):
     """Test that a value error is raised when an unsupported component (a child component not found in the