Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Express: Cannot create a navbar page with an id and a sidebar #1308

Closed
gadenbuie opened this issue Apr 11, 2024 · 1 comment · Fixed by #1314
Closed

Express: Cannot create a navbar page with an id and a sidebar #1308

gadenbuie opened this issue Apr 11, 2024 · 1 comment · Fixed by #1314

Comments

@gadenbuie
Copy link
Collaborator

gadenbuie commented Apr 11, 2024

It's common to want to add a global sidebar to a navbar page and to have some content in that sidebar be displayed conditionally. With Shiny Core, you can create an app with this layout using the following code:

from shiny import App, ui

ui = ui.page_navbar(
    ui.nav_panel("A", "Page A content"),
    ui.nav_panel("B", "Page B content"),  # <2>
    title="App with navbar",
    fillable=True,
    id="page",  # <1>
    sidebar=ui.sidebar(
        ui.input_select("data", "Data", ("A", "B", "C")),
        ui.panel_conditional(
            "input.page === 'B'",  # <3>
            ui.input_select("subset", "Subset for B", ("e", "f", "g")),
        ),
    ),
)


def server(input):
    pass


app = App(ui, server)

shinylive.io example

There are three components:

  1. The ui.page_navbar() needs the id argument.
  2. When id is provided, the current page title is reported at that input.{id}.
  3. ui.panel_conditional() can use the value of the input, e.g. input.page === 'B', to display content only when nav_panel("B", ...) is displayed.

In Shiny Express, this pattern is not possible because users cannot set id via ui.page_opts(). In theory, they would need to provide a custom function to ui.page_opts(page_fn=) that passes id to shiny.ui.page_navbar().

from shiny.express import input, ui
from functools import partial
from shiny.ui import page_navbar

def page_navbar_id(*args, **kwargs):
    return page_navbar(*args, **kwargs, id="page")

## Doesn't work
ui.page_opts(title="App with navbar", fillable=True, page_fn=page_navbar_id)
## Does work, but can't set the page `id`
# ui.page_opts(title="App with navbar", fillable=True)

with ui.sidebar(title="Settings"):
    ui.input_select("data", "Data", ("A", "B", "C"))

    with ui.panel_conditional("input.page === 'Settings B'"):
        ui.input_select("subset", "Subset for B", ("e", "f", "g"))

with ui.nav_panel("A"):
    "Page A content"

with ui.nav_panel("B"):
    "Page B content"

shinylive.io example

This doesn't work, however, because the special handling for the top-level with ui.sidebar() only happens when page_fn is None.

py-shiny/shiny/ui/_page.py

Lines 543 to 545 in 340858a

if page_fn is None:
if nNavs == 0:
if nSidebars == 0:

elif nSidebars == 1:

Attempting to run the example above using ui.page_opts(page_fn) gives the following error

Traceback (most recent call last):
  File "/Users/garrick/work/posit-dev/py-shiny/shiny/express/_run.py", line 60, in wrap_express_app
    app_ui = run_express(file).tagify()
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/garrick/work/posit-dev/py-shiny/venv/lib/python3.12/site-packages/htmltools/_core.py", line 735, in tagify
    cp.children = cp.children.tagify()
                  ^^^^^^^^^^^^^^^^^^^^
  File "/Users/garrick/work/posit-dev/py-shiny/venv/lib/python3.12/site-packages/htmltools/_core.py", line 222, in tagify
    tagified_child = child.tagify()
                     ^^^^^^^^^^^^^^
  File "/Users/garrick/work/posit-dev/py-shiny/venv/lib/python3.12/site-packages/htmltools/_core.py", line 735, in tagify
    cp.children = cp.children.tagify()
                  ^^^^^^^^^^^^^^^^^^^^
  File "/Users/garrick/work/posit-dev/py-shiny/venv/lib/python3.12/site-packages/htmltools/_core.py", line 222, in tagify
    tagified_child = child.tagify()
                     ^^^^^^^^^^^^^^
  File "/Users/garrick/work/posit-dev/py-shiny/shiny/ui/_navs.py", line 396, in tagify
    nav, content = render_navset(
                   ^^^^^^^^^^^^^^
  File "/Users/garrick/work/posit-dev/py-shiny/shiny/ui/_navs.py", line 1314, in render_navset
    selected = x.get_value()
               ^^^^^^^^^^^
AttributeError: 'Sidebar' object has no attribute 'get_value'
@gadenbuie
Copy link
Collaborator Author

We're going to move forward with passing kwargs (#1314), but we also discussed that this situation would be improved by resolving #939.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant