From ecbe0af020269329a872b19a25d9512b57228505 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 26 Apr 2022 12:15:00 -0500 Subject: [PATCH] Add unit tests and fix a couple bugs --- shiny/ui/_navs.py | 26 ++++--- tests/test_navs.py | 175 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+), 11 deletions(-) create mode 100644 tests/test_navs.py diff --git a/shiny/ui/_navs.py b/shiny/ui/_navs.py index 62a5bf42dc..4c808812ef 100644 --- a/shiny/ui/_navs.py +++ b/shiny/ui/_navs.py @@ -45,34 +45,34 @@ def render( Add appropriate tag attributes to nav/content tags when linking to internal content. """ - x = copy.copy(self) - # Nothing to do for nav_item()/nav_spacer() - if x.content is None: - return x.nav, None + if self.content is None: + return self # At least currently, in the case where both nav and content are tags # (i.e., nav()), the nav always has a child tag...I'm not sure if # there's a way to statically type this - a_tag = cast(Tag, x.nav.children[0]) + nav = copy.deepcopy(self.nav) + a_tag = cast(Tag, nav.children[0]) if is_menu: a_tag.add_class("dropdown-item") else: a_tag.add_class("nav-link") - x.nav.add_class("nav-item") + nav.add_class("nav-item") # Hyperlink the nav to the content - x.content.attrs["id"] = id + content = copy.copy(self.content) + content.attrs["id"] = id a_tag.attrs["href"] = f"#{id}" # Mark the nav/content as active if it should be if isinstance(selected, str) and selected == self.get_value(): - x.content.add_class("active") + content.add_class("active") a_tag.add_class("active") - x.nav.children[0] = a_tag + nav.children[0] = a_tag - return x.nav, x.content + return nav, content def get_value(self) -> Optional[str]: if self.content is None: @@ -264,6 +264,10 @@ def render(self, selected: Optional[str], **kwargs: Any) -> Tuple[Tag, TagList]: ) def get_value(self) -> Optional[str]: + for x in self.nav_items: + val = x.get_value() + if val: + return val return None @@ -748,7 +752,7 @@ def navs_bar( nav = div(nav, id=collapse_id, class_="collapse navbar-collapse") nav_container.append(nav) - nav_final = tags.nav({"class": "navbar"}, nav_container) + nav_final = tags.nav({"class": "navbar navbar-expand-md"}, nav_container) if position != "static-top": nav_final.add_class(position) diff --git a/tests/test_navs.py b/tests/test_navs.py new file mode 100644 index 0000000000..8c3a6d0317 --- /dev/null +++ b/tests/test_navs.py @@ -0,0 +1,175 @@ +import pytest + +import random +import textwrap +from typing import Callable, Any, Union + +from shiny import ui +from shiny._utils import private_seed +from htmltools import Tag, TagList + + +# Fix the randomness of these functions to make the tests deterministic +def with_private_seed( + func: Callable[[], Union[Tag, TagList]], *args: Any, **kwargs: Any +): + with private_seed(): + random.seed(0) + return func(*args, **kwargs) + + +def test_nav_markup(): + a = ui.nav("a", "a") + b = ui.nav("b", "b") + c = ui.nav("c", "c") + menu = ui.nav_menu( + "Menu", + c, + "----", + "Plain text", + "----", + ui.nav_item("Other item"), + ) + + x = with_private_seed(ui.navs_tab, a, b, ui.nav_item("Some item"), menu) + + assert x.render()["html"] == textwrap.dedent( + """\ + +
+
a
+
b
+
c
+
""" + ) + + x = with_private_seed( + ui.navs_pill, + menu, + a, + id="navs_pill_id", + ) + + assert x.render()["html"] == textwrap.dedent( + """\ + +
+
c
+
a
+
""" + ) + + x = with_private_seed( + ui.navs_pill_card, + a, + ui.nav_menu("Menu", c), + b, + selected="c", + ) + + assert x.render()["html"] == textwrap.dedent( + """\ +
+
+ +
+
+
+
a
+
c
+
b
+
+
+
""" + ) + + x = with_private_seed( + ui.navs_bar, # type: ignore + ui.nav_menu("Menu", "Plain text", c), + title="Page title", + footer="Page footer", + header="Page header", + ) + + assert x.render()["html"] == textwrap.dedent( + """\ + +
+
Page header
+
+
c
+
+
Page footer
+
""" + )