Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/_quartodoc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ quartodoc:
- ui.output_table
- ui.output_data_frame
- ui.output_text
- ui.output_code
- ui.output_text_verbatim
- ui.output_ui
- render.plot
Expand Down
2 changes: 2 additions & 0 deletions shiny/render/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
display,
)
from ._render import (
code,
image,
plot,
table,
Expand All @@ -28,6 +29,7 @@
"data_frame",
"display",
"text",
"code",
"plot",
"image",
"table",
Expand Down
97 changes: 94 additions & 3 deletions shiny/render/_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# Can use `dict` in python >= 3.9
from typing import (
TYPE_CHECKING,
Any,
Callable,
Literal,
Optional,
Expand Down Expand Up @@ -46,6 +47,7 @@

__all__ = (
"text",
"code",
"plot",
"image",
"table",
Expand All @@ -61,6 +63,17 @@ class text(Renderer[str]):
"""
Reactively render text.

When used in Shiny Express applications, this defaults to displaying the text as
normal text on the web page. When used in Shiny Core applications, this should be
paired with :func:`~shiny.ui.output_text` in the UI.


Parameters
----------
inline
Used in Shiny Express only. If ``True``, the result is displayed inline. (This
argument is passed to :func:`~shiny.ui.output_text`.)

Returns
-------
:
Expand All @@ -74,13 +87,91 @@ class text(Renderer[str]):

See Also
--------
~shiny.render.code
~shiny.ui.output_text
"""

def default_ui(self, id: str, placeholder: bool | MISSING_TYPE = MISSING) -> Tag:
def default_ui(
self,
id: str,
*,
inline: bool | MISSING_TYPE = MISSING,
) -> Tag:
kwargs: dict[str, Any] = {}
set_kwargs_value(kwargs, "inline", inline, self.inline)

return _ui.output_text(id, **kwargs)

def __init__(
self,
_fn: Optional[ValueFn[str]] = None,
*,
inline: bool = False,
) -> None:
super().__init__(_fn)
self.inline = inline

async def transform(self, value: str) -> Jsonifiable:
return str(value)


# ======================================================================================
# RenderCode
# ======================================================================================


class code(Renderer[str]):
"""
Reactively render text as code (monospaced).

When used in Shiny Express applications, this defaults to displaying the text in a
monospace font in a code block. When used in Shiny Core applications, this should be
paired with :func:`~shiny.ui.output_code` in the UI.

Parameters
----------
placeholder
Used in Shiny Express only. If the output is empty or ``None``, should an empty
rectangle be displayed to serve as a placeholder? This does not affect behavior
when the output is nonempty. (This argument is passed to
:func:`~shiny.ui.output_code`.)


Returns
-------
:
A decorator for a function that returns a string.

Tip
----
The name of the decorated function (or ``@output(id=...)``) should match the ``id``
of a :func:`~shiny.ui.output_code` container (see :func:`~shiny.ui.output_code` for
example usage).

See Also
--------
~shiny.render.code
~shiny.ui.output_code
"""

def default_ui(
self,
id: str,
*,
placeholder: bool | MISSING_TYPE = MISSING,
) -> Tag:
kwargs: dict[str, bool] = {}
set_kwargs_value(kwargs, "placeholder", placeholder, None)
return _ui.output_text_verbatim(id, **kwargs)
set_kwargs_value(kwargs, "placeholder", placeholder, self.placeholder)
return _ui.output_code(id, **kwargs)

def __init__(
self,
_fn: Optional[ValueFn[str]] = None,
*,
placeholder: bool = True,
) -> None:
super().__init__(_fn)
self.placeholder = placeholder

async def transform(self, value: str) -> Jsonifiable:
return str(value)
Expand Down
2 changes: 2 additions & 0 deletions shiny/ui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
output_plot,
output_image,
output_text,
output_code,
output_text_verbatim,
output_table,
output_ui,
Expand Down Expand Up @@ -296,6 +297,7 @@
"output_plot",
"output_image",
"output_text",
"output_code",
"output_text_verbatim",
"output_table",
"output_ui",
Expand Down
42 changes: 42 additions & 0 deletions shiny/ui/_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"output_plot",
"output_image",
"output_text",
"output_code",
"output_text_verbatim",
"output_table",
"output_ui",
Expand Down Expand Up @@ -270,6 +271,47 @@ def output_text(
return container(id=resolve_id(id), class_="shiny-text-output")


def output_code(id: str, placeholder: bool = True) -> Tag:
"""
Create a output container for code (monospaced text).

This is similar to :func:`~shiny.ui.output_text`, except that it displays the text
in a fixed-width container with a gray-ish background color and border.

Parameters
----------
id
An output id.
placeholder
If the output is empty or ``None``, should an empty rectangle be displayed to
serve as a placeholder? (This does not affect behavior when the output is
nonempty.)

Returns
-------
:
A UI element

Note
----
This function is currently the same as :func:`~shiny.ui.output_text_verbatim`, but
this may change in future versions of Shiny.

See Also
--------
* :func:`~shiny.render.text`
* :func:`~shiny.ui.output_text`
* :func:`~shiny.ui.output_text_verbatim`

Example
-------
See :func:`~shiny.ui.output_text`
"""

cls = "shiny-text-output" + (" noplaceholder" if not placeholder else "")
return tags.pre(id=resolve_id(id), class_=cls)


def output_text_verbatim(id: str, placeholder: bool = False) -> Tag:
"""
Create a output container for some text.
Expand Down
4 changes: 2 additions & 2 deletions tests/pytest/test_display_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,15 @@ def annotated(x: int, y: int) -> int:
def test_implicit_output():
@display_body()
def has_implicit_outputs():
@render.text
@render.code
def foo():
return "hello"

with capture_display() as d:
has_implicit_outputs()
assert len(d) == 1
d0 = cast(Tagifiable, d[0])
assert d0.tagify() == ui.output_text_verbatim("foo")
assert d0.tagify() == ui.output_code("foo")


def test_no_nested_transform_unless_explicit():
Expand Down
16 changes: 8 additions & 8 deletions tests/pytest/test_express_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def text1():

assert (
ui.TagList(text1.tagify()).get_html_string()
== ui.output_text_verbatim("text1").get_html_string()
== ui.output_text("text1").get_html_string()
)

@suspend_display
Expand All @@ -61,22 +61,22 @@ def text2():
assert ui.TagList(text2.tagify()).get_html_string() == ""

@ui_kwargs(placeholder=True)
@render.text
def text3():
@render.code
def code1():
return "text"

assert (
ui.TagList(text3.tagify()).get_html_string()
== ui.output_text_verbatim("text3", placeholder=True).get_html_string()
ui.TagList(code1.tagify()).get_html_string()
== ui.output_code("code1", placeholder=True).get_html_string()
)

@ui_kwargs(width=100)
@render.text
def text4():
@render.code
def code2():
return "text"

with pytest.raises(TypeError, match="width"):
text4.tagify()
code2.tagify()


def test_suspend_display():
Expand Down