Popovers
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [viewer]
#| viewerHeight: 220
from shiny import App, render, ui
app_ui = ui.page_fluid(
ui.popover(
ui.input_action_button("btn", "A button with a popover"),
"A message inside the popover.",
title="A popover",
id="btn_popover",
placement="right",
),
ui.output_code("popover_state"),
class_="pt-3",
)
def server(input, output, session):
@render.code
def popover_state():
return f"Popover state: {input.btn_popover()}"
app = App(app_ui, server)from shiny import App, render, ui
app_ui = ui.page_fluid(
ui.popover(
ui.input_action_button("btn", "A button with a popover"),
"A message inside the popover.",
title="A popover",
id="btn_popover",
placement="right",
),
ui.output_code("popover_state"),
)
def server(input, output, session):
@render.code
def popover_state():
return f"Popover state: {input.btn_popover()}"
app = App(app_ui, server)Relevant Functions
-
ui.popover
ui.popover(trigger, *args, title=None, id=None, placement='auto', options=None, **kwargs) -
ui.update_popover
ui.update_popover(id, *args, title=None, show=None, session=None)
Details
A popover is a container that appears next to a trigger element when the user clicks the element. Unlike a tooltip, which shows a short message on hover, a popover opens on click and stays open until it is dismissed, so it can hold richer content such as formatted text or even input controls.
To add a popover in Shiny Core, wrap the trigger element in ui.popover() and pass the popover contents as additional arguments. In Shiny Express, use with ui.popover(): as a context manager, placing the trigger element first inside the block, followed by the popover contents.
Use the title argument to add a header to the popover. Control where the popover appears relative to its trigger with the placement argument, which defaults to 'auto' but can be set to 'top', 'bottom', 'left', or 'right'. placement is a preference rather than a guarantee: the popover is repositioned automatically when there isn’t room where you asked for it.
Optionally assign the popover an id to trigger reactions when the popover opens or closes, or to update the popover from the server. A boolean that describes whether or not the popover is visible will be accessible as a reactive variable within the server function as input.<id>().
To show or hide a popover programmatically, or to replace its contents or title, call ui.update_popover() from the server with the popover’s id.
For finer control, options takes a dictionary of Bootstrap popover options passed straight through to Bootstrap. Note that content, title, and placement cannot be set this way — use the dedicated arguments above instead.
Dismissing a popover
Besides clicking the close button, a popover can be closed by clicking its trigger again, or by pressing Esc or Space while the popover or its trigger has focus.
Accessibility of popover triggers
Because the user has to interact with the trigger to see the popover, prefer an element that is already keyboard accessible, such as a button or a link. If you use a non-interactive element such as a span or bare text, ui.popover() adds tabindex="0" for you so the trigger can still be reached with the keyboard.
The case that needs care is an icon used as the trigger with no accompanying text. Icons are often generated on the assumption that they are decorative, which hides them from assistive technologies. When an icon is the trigger, make sure it does not carry aria-hidden="true" or role="presentation", and give it a short title describing what the trigger does.
See Also: Tooltips show a short message on hover. Modal messages and notifications provide alternative ways to display information to the user.
Variations
Put input controls in a popover
Because a popover opens on click and stays open, it can hold live input controls rather than just text. That makes it a good home for settings that would otherwise clutter the page. Place the trigger first, then the inputs; they stay reactive while the popover is open.
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [viewer]
#| viewerHeight: 300
from shiny import App, render, ui
app_ui = ui.page_fluid(
ui.popover(
ui.input_action_button("settings", "Settings", class_="mt-3"),
ui.input_slider("n", "Number of points", 1, 100, 50),
ui.input_select("color", "Color", ["red", "green", "blue"]),
title="Plot settings",
id="settings_popover",
),
ui.output_code("summary"),
)
def server(input, output, session):
@render.code
def summary():
return f"{input.n()} points in {input.color()}"
app = App(app_ui, server)from shiny.express import input, render, ui
with ui.popover(id="settings_popover", title="Plot settings"):
ui.input_action_button("settings", "Settings", class_="mt-3")
ui.input_slider("n", "Number of points", 1, 100, 50)
ui.input_select("color", "Color", ["red", "green", "blue"])
@render.code
def summary():
return f"{input.n()} points in {input.color()}"from shiny import App, render, ui
app_ui = ui.page_fluid(
ui.popover(
ui.input_action_button("settings", "Settings", class_="mt-3"),
ui.input_slider("n", "Number of points", 1, 100, 50),
ui.input_select("color", "Color", ["red", "green", "blue"]),
title="Plot settings",
id="settings_popover",
),
ui.output_code("summary"),
)
def server(input, output, session):
@render.code
def summary():
return f"{input.n()} points in {input.color()}"
app = App(app_ui, server)Update a popover
Call ui.update_popover() to programmatically show or hide a popover with a given id, or to update its contents and title.
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [viewer]
#| viewerHeight: 250
from shiny import App, reactive, ui
app_ui = ui.page_fluid(
ui.input_action_button("btn_show", "Show popover", class_="mt-3 me-3"),
ui.input_action_button("btn_close", "Close popover", class_="mt-3 me-3"),
ui.input_action_button("btn_update", "Replace contents", class_="mt-3 me-3"),
ui.br(),
ui.popover(
ui.input_action_button(
"btn_w_popover", "A button with a popover", class_="mt-3"
),
"A message inside the popover.",
title="A popover",
id="btn_popover",
),
)
def server(input, output, session):
@reactive.effect
@reactive.event(input.btn_show)
def show_popover():
ui.update_popover("btn_popover", show=True)
@reactive.effect
@reactive.event(input.btn_close)
def close_popover():
ui.update_popover("btn_popover", show=False)
@reactive.effect
@reactive.event(input.btn_update)
def replace_popover():
# Positional arguments replace the body; `title` replaces the header.
ui.update_popover(
"btn_popover", "An updated message.", title="An updated popover"
)
app = App(app_ui, server)from shiny import reactive
from shiny.express import input, ui
ui.input_action_button("btn_show", "Show popover", class_="mt-3 me-3")
ui.input_action_button("btn_close", "Close popover", class_="mt-3 me-3")
ui.input_action_button("btn_update", "Replace contents", class_="mt-3 me-3")
with ui.popover(id="btn_popover", title="A popover"):
ui.input_action_button("btn_w_popover", "A button with a popover", class_="mt-3")
"A message inside the popover."
@reactive.effect
@reactive.event(input.btn_show)
def show_popover():
ui.update_popover("btn_popover", show=True)
@reactive.effect
@reactive.event(input.btn_close)
def close_popover():
ui.update_popover("btn_popover", show=False)
@reactive.effect
@reactive.event(input.btn_update)
def replace_popover():
# Positional arguments replace the body; `title` replaces the header.
ui.update_popover("btn_popover", "An updated message.", title="An updated popover") from shiny import App, reactive, ui
app_ui = ui.page_fluid(
ui.input_action_button("btn_show", "Show popover", class_="mt-3 me-3"),
ui.input_action_button("btn_close", "Close popover", class_="mt-3 me-3"),
ui.input_action_button("btn_update", "Replace contents", class_="mt-3 me-3"),
ui.br(),
ui.popover(
ui.input_action_button(
"btn_w_popover", "A button with a popover", class_="mt-3"
),
"A message inside the popover.",
title="A popover",
id="btn_popover",
),
)
def server(input, output, session):
@reactive.effect
@reactive.event(input.btn_show)
def show_popover():
ui.update_popover("btn_popover", show=True)
@reactive.effect
@reactive.event(input.btn_close)
def close_popover():
ui.update_popover("btn_popover", show=False)
@reactive.effect
@reactive.event(input.btn_update)
def replace_popover():
# Positional arguments replace the body; `title` replaces the header.
ui.update_popover(
"btn_popover", "An updated message.", title="An updated popover"
)
app = App(app_ui, server)