posit-dev / py-shiny

Shiny for Python
https://shiny.posit.co/py/
MIT License
1.3k stars 78 forks source link

How do I set the `<title>` of the page? #531

Open nealrichardson opened 1 year ago

nealrichardson commented 1 year ago

I guess I could probably do ui.tags.title()? But I don't see any of the examples doing this. Seems like this is something standard I should want.

Not to hammer on the markdown document topic too much (#527) but could be a great way to leverage the "front matter" used by quarto et al., for this and for things like meta tags that would give nice URL previews.

cpsievert commented 1 year ago

There is a shiny.ui.panel_title() for this (which mirrors R's shiny::titlePanel()) that not only sets the <title>, but also includes an <h2>

Page functions also have a title argument, which the majority of the time is just for the <title> (but for page_navbar() it also goes in the navbar)

nealrichardson commented 1 year ago

Thanks, I figured it was possible. Maybe we should use that in some examples (I'm thinking of the ones in the "gallery"), both for the completeness of the apps and so that we demonstrate how to do it?

gshotwell commented 1 year ago

Good thought, maybe we should just have the shiny create . app use a title instead of ui.h2

from shiny import App, render, ui

app_ui = ui.page_fluid(
    ui.panel_title("Hello Shiny!"),
    ui.input_slider("n", "N", 0, 100, 20),
    ui.output_text_verbatim("txt"),
)

def server(input, output, session):
    @output
    @render.text
    def txt():
        return f"n*2 is {input.n() * 2}"

app = App(app_ui, server)
nealrichardson commented 1 year ago

Maybe so, but I also think it's worthwhile for the gallery examples to show the most polished end product.

wch commented 1 year ago

If you just want to set the <title> of the page without displaying it on the page (as with panel_title()), you can do ui.head_content(ui.tags.title()):

ui.page_fluid(
    ui.head_content(ui.tags.title("Hello Shiny!")),
    ui.input_slider("n", "N", 0, 100, 20),
    ui.output_text_verbatim("txt"),
)

I've done this in a few apps, and I think this is too complicated and awkward. It would be nice to have a single function for this, maybe with a name like doc_title().

Also note that the <title> won't (at present) show in shinylive applications because the app runs in an iframe. (It would be possible to hoist the title from the iframe to the outer page, but it doesn't do that now.)

jcheng5 commented 1 year ago

@wch Do you prefer doc_title() to ui.page_fluid(..., title="Hello Shiny!")?

It is, as usual, unfortunate that the named arguments come after the unnamed arguments, but other than that, I think the page functions are the natural place I'd look for this. Or maybe on the App() constructor, I suppose.

wch commented 1 year ago

Oh wow, I had forgotten that we already support ui.page_fluid(..., title="Hello Shiny!").

https://github.com/rstudio/py-shiny/blob/befb5f3c7b5b2cbc390590884aa87c98d46172ba/shiny/ui/_page.py#L122-L127

Ideally, we'd have ui.page_fluid(title="Hello Shiny!", ...) but of course we can't do that in Python.

One other possibility is for the page functions to accept a dictionary (like tag functions) and pull out special attributes like title. For example:

page_fluid({"title": "My app", "lang": "en"}, ...)
# Or equivalently:
page_fluid(dict(title="My app", lang="en"), ...)

I don't love that either, since it breaks with how we handle dictionaries in other UI functions.

So perhaps we should just stick with the status quo?

nealrichardson commented 1 year ago

At the risk of derailing the discussion, from an OOP perspective I'd think of Page being a class with meaningful attributes like title and other meta information that's not just generic to Tags. FluidPage et al. being subclasses of that. And you'd __init__ Page with body = [Tags], title, etc.

nealrichardson commented 1 year ago

Regardless of how, I think it would be good if our gallery examples set title and ideally meta tags so that if I share a link, you get a nice preview, and so that the browser tab doesn't show a bare URL because there's no <title>. Just as a way of showcasing what a polished app could/should look like.