Skip to content

Commit ecbe0af

Browse files
committed
Add unit tests and fix a couple bugs
1 parent 083573b commit ecbe0af

File tree

2 files changed

+190
-11
lines changed

2 files changed

+190
-11
lines changed

shiny/ui/_navs.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,34 +45,34 @@ def render(
4545
Add appropriate tag attributes to nav/content tags when linking to internal content.
4646
"""
4747

48-
x = copy.copy(self)
49-
5048
# Nothing to do for nav_item()/nav_spacer()
51-
if x.content is None:
52-
return x.nav, None
49+
if self.content is None:
50+
return self
5351

5452
# At least currently, in the case where both nav and content are tags
5553
# (i.e., nav()), the nav always has a child <a> tag...I'm not sure if
5654
# there's a way to statically type this
57-
a_tag = cast(Tag, x.nav.children[0])
55+
nav = copy.deepcopy(self.nav)
56+
a_tag = cast(Tag, nav.children[0])
5857
if is_menu:
5958
a_tag.add_class("dropdown-item")
6059
else:
6160
a_tag.add_class("nav-link")
62-
x.nav.add_class("nav-item")
61+
nav.add_class("nav-item")
6362

6463
# Hyperlink the nav to the content
65-
x.content.attrs["id"] = id
64+
content = copy.copy(self.content)
65+
content.attrs["id"] = id
6666
a_tag.attrs["href"] = f"#{id}"
6767

6868
# Mark the nav/content as active if it should be
6969
if isinstance(selected, str) and selected == self.get_value():
70-
x.content.add_class("active")
70+
content.add_class("active")
7171
a_tag.add_class("active")
7272

73-
x.nav.children[0] = a_tag
73+
nav.children[0] = a_tag
7474

75-
return x.nav, x.content
75+
return nav, content
7676

7777
def get_value(self) -> Optional[str]:
7878
if self.content is None:
@@ -264,6 +264,10 @@ def render(self, selected: Optional[str], **kwargs: Any) -> Tuple[Tag, TagList]:
264264
)
265265

266266
def get_value(self) -> Optional[str]:
267+
for x in self.nav_items:
268+
val = x.get_value()
269+
if val:
270+
return val
267271
return None
268272

269273

@@ -748,7 +752,7 @@ def navs_bar(
748752
nav = div(nav, id=collapse_id, class_="collapse navbar-collapse")
749753

750754
nav_container.append(nav)
751-
nav_final = tags.nav({"class": "navbar"}, nav_container)
755+
nav_final = tags.nav({"class": "navbar navbar-expand-md"}, nav_container)
752756

753757
if position != "static-top":
754758
nav_final.add_class(position)

tests/test_navs.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import pytest
2+
3+
import random
4+
import textwrap
5+
from typing import Callable, Any, Union
6+
7+
from shiny import ui
8+
from shiny._utils import private_seed
9+
from htmltools import Tag, TagList
10+
11+
12+
# Fix the randomness of these functions to make the tests deterministic
13+
def with_private_seed(
14+
func: Callable[[], Union[Tag, TagList]], *args: Any, **kwargs: Any
15+
):
16+
with private_seed():
17+
random.seed(0)
18+
return func(*args, **kwargs)
19+
20+
21+
def test_nav_markup():
22+
a = ui.nav("a", "a")
23+
b = ui.nav("b", "b")
24+
c = ui.nav("c", "c")
25+
menu = ui.nav_menu(
26+
"Menu",
27+
c,
28+
"----",
29+
"Plain text",
30+
"----",
31+
ui.nav_item("Other item"),
32+
)
33+
34+
x = with_private_seed(ui.navs_tab, a, b, ui.nav_item("Some item"), menu)
35+
36+
assert x.render()["html"] == textwrap.dedent(
37+
"""\
38+
<ul class="nav nav-tabs" data-tabsetid="7311">
39+
<li class="nav-item">
40+
<a data-bs-toggle="tab" data-value="a" role="tab" class="nav-link active" href="#tab-7311-0">a</a>
41+
</li>
42+
<li class="nav-item">
43+
<a data-bs-toggle="tab" data-value="b" role="tab" class="nav-link" href="#tab-7311-1">b</a>
44+
</li>
45+
<li>Some item</li>
46+
<li class="nav-item dropdown">
47+
<a class="nav-link dropdown-toggle " data-bs-toggle="dropdown" href="#" role="button">Menu</a>
48+
<ul class="dropdown-menu " data-tabsetid="7890">
49+
<li>
50+
<a data-bs-toggle="tab" data-value="c" role="tab" class="dropdown-item" href="#tab-7890-0">c</a>
51+
</li>
52+
<li class="dropdown-divider"></li>
53+
<li class="dropdown-header">Plain text</li>
54+
<li class="dropdown-divider"></li>
55+
<li>Other item</li>
56+
</ul>
57+
</li>
58+
</ul>
59+
<div class="tab-content" data-tabsetid="7311">
60+
<div class="tab-pane active" role="tabpanel" data-value="a" id="tab-7311-0">a</div>
61+
<div class="tab-pane" role="tabpanel" data-value="b" id="tab-7311-1">b</div>
62+
<div class="tab-pane" role="tabpanel" data-value="c" id="tab-7890-0">c</div>
63+
</div>"""
64+
)
65+
66+
x = with_private_seed(
67+
ui.navs_pill,
68+
menu,
69+
a,
70+
id="navs_pill_id",
71+
)
72+
73+
assert x.render()["html"] == textwrap.dedent(
74+
"""\
75+
<ul class="nav nav-pills shiny-tab-input" id="navs_pill_id" data-tabsetid="7311">
76+
<li class="nav-item dropdown">
77+
<a class="nav-link dropdown-toggle active" data-bs-toggle="dropdown" href="#" role="button">Menu</a>
78+
<ul class="dropdown-menu " data-tabsetid="7890">
79+
<li>
80+
<a data-bs-toggle="tab" data-value="c" role="tab" class="dropdown-item active" href="#tab-7890-0">c</a>
81+
</li>
82+
<li class="dropdown-divider"></li>
83+
<li class="dropdown-header">Plain text</li>
84+
<li class="dropdown-divider"></li>
85+
<li>Other item</li>
86+
</ul>
87+
</li>
88+
<li class="nav-item">
89+
<a data-bs-toggle="tab" data-value="a" role="tab" class="nav-link" href="#tab-7311-1">a</a>
90+
</li>
91+
</ul>
92+
<div class="tab-content" data-tabsetid="7311">
93+
<div class="tab-pane active" role="tabpanel" data-value="c" id="tab-7890-0">c</div>
94+
<div class="tab-pane" role="tabpanel" data-value="a" id="tab-7311-1">a</div>
95+
</div>"""
96+
)
97+
98+
x = with_private_seed(
99+
ui.navs_pill_card,
100+
a,
101+
ui.nav_menu("Menu", c),
102+
b,
103+
selected="c",
104+
)
105+
106+
assert x.render()["html"] == textwrap.dedent(
107+
"""\
108+
<div class="card">
109+
<div class="card-header">
110+
<ul class="nav nav-pills card-header-pills" data-tabsetid="7311">
111+
<li class="nav-item">
112+
<a data-bs-toggle="tab" data-value="a" role="tab" class="nav-link" href="#tab-7311-0">a</a>
113+
</li>
114+
<li class="nav-item dropdown">
115+
<a class="nav-link dropdown-toggle active" data-bs-toggle="dropdown" href="#" role="button">Menu</a>
116+
<ul class="dropdown-menu " data-tabsetid="7890">
117+
<li>
118+
<a data-bs-toggle="tab" data-value="c" role="tab" class="dropdown-item active" href="#tab-7890-0">c</a>
119+
</li>
120+
</ul>
121+
</li>
122+
<li class="nav-item">
123+
<a data-bs-toggle="tab" data-value="b" role="tab" class="nav-link" href="#tab-7311-2">b</a>
124+
</li>
125+
</ul>
126+
</div>
127+
<div class="card-body">
128+
<div class="tab-content" data-tabsetid="7311">
129+
<div class="tab-pane" role="tabpanel" data-value="a" id="tab-7311-0">a</div>
130+
<div class="tab-pane active" role="tabpanel" data-value="c" id="tab-7890-0">c</div>
131+
<div class="tab-pane" role="tabpanel" data-value="b" id="tab-7311-2">b</div>
132+
</div>
133+
</div>
134+
</div>"""
135+
)
136+
137+
x = with_private_seed(
138+
ui.navs_bar, # type: ignore
139+
ui.nav_menu("Menu", "Plain text", c),
140+
title="Page title",
141+
footer="Page footer",
142+
header="Page header",
143+
)
144+
145+
assert x.render()["html"] == textwrap.dedent(
146+
"""\
147+
<nav class="navbar navbar-expand-md">
148+
<div class="container-fluid">
149+
<a class="navbar-brand" href="#">Page title</a>
150+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-collapse-1663" aria-controls="navbar-collapse-1663" aria-expanded="false" aria-label="Toggle navigation">
151+
<span class="navbar-toggler-icon"></span>
152+
</button>
153+
<div id="navbar-collapse-1663" class="collapse navbar-collapse">
154+
<ul class="nav navbar-nav" data-tabsetid="7311">
155+
<li class="nav-item dropdown">
156+
<a class="nav-link dropdown-toggle active" data-bs-toggle="dropdown" href="#" role="button">Menu</a>
157+
<ul class="dropdown-menu " data-tabsetid="7890">
158+
<li class="dropdown-header">Plain text</li>
159+
<li>
160+
<a data-bs-toggle="tab" data-value="c" role="tab" class="dropdown-item active" href="#tab-7890-1">c</a>
161+
</li>
162+
</ul>
163+
</li>
164+
</ul>
165+
</div>
166+
</div>
167+
</nav>
168+
<div class="container-fluid">
169+
<div class="row">Page header</div>
170+
<div class="tab-content" data-tabsetid="7311">
171+
<div class="tab-pane active" role="tabpanel" data-value="c" id="tab-7890-1">c</div>
172+
</div>
173+
<div class="row">Page footer</div>
174+
</div>"""
175+
)

0 commit comments

Comments
 (0)