Skip to content

Commit 32eb05d

Browse files
fix: include_css and include_js can use files in same dir (#2069)
Co-authored-by: Carson Sievert <[email protected]>
1 parent 61c23e5 commit 32eb05d

File tree

6 files changed

+80
-9
lines changed

6 files changed

+80
-9
lines changed

shiny/ui/_include_helpers.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,20 @@ def include_js(
3636
method
3737
One of the following:
3838
39-
* ``"link"`` is the link to the CSS file via a :func:`~shiny.ui.tags.link` tag. This
39+
* ``"link"`` is the link to the JS file via a :func:`~shiny.ui.tags.script` tag. This
4040
method is generally preferable to ``"inline"`` since it allows the browser to
4141
cache the file.
42-
* ``"link_files"`` is the same as ``"link"``, but also allow for the CSS file to
42+
* ``"link_files"`` is the same as ``"link"``, but also allow for the JS file to
4343
request other files within ``path``'s immediate parent directory (e.g.,
44-
``@import()`` another file). Note that this isn't the default behavior because
44+
``import`` another file). Note that this isn't the default behavior because
4545
you should **be careful not to include files in the same directory as ``path``
4646
that contain sensitive information**. A good general rule of thumb to follow
4747
is to have ``path`` be located in a subdirectory of the app directory. For
4848
example, if the app's source is located at ``/app/app.py``, then ``path``
49-
should be somewhere like ``/app/css/custom.css`` (and all the other relevant
49+
should be somewhere like ``/app/js/custom.js`` (and all the other relevant
5050
accompanying 'safe' files should be located under ``/app/css/``).
51-
* ``"inline"`` is the inline the CSS file contents within a
52-
:func:`~shiny.ui.tags.style` tag.
51+
* ``"inline"`` is the inline the JS file contents within a
52+
:func:`~shiny.ui.tags.script` tag.
5353
**kwargs
5454
Attributes which are passed on to `~shiny.ui.tags.script`.
5555
@@ -234,17 +234,17 @@ def maybe_copy_files(path: Path | str, include_files: bool) -> tuple[str, str]:
234234
else:
235235
os.makedirs(tmpdir, exist_ok=True)
236236
shutil.copy(path, path_dest)
237-
238237
return path_dest, hash
239238

240239

241240
def get_hash(path: Path | str, include_files: bool) -> str:
242241
if include_files:
243-
key = get_file_key(path)
244-
else:
245242
dir = os.path.dirname(path)
246243
files = glob.iglob(os.path.join(dir, "**"), recursive=True)
247244
key = "\n".join([get_file_key(x) for x in files])
245+
else:
246+
key = get_file_key(path)
247+
248248
return hash_deterministic(key)
249249

250250

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from pathlib import Path
2+
3+
from shiny import App, Inputs, Outputs, Session, ui
4+
5+
custom_css = ui.include_css(
6+
Path(__file__).parent / "css" / "style.css",
7+
method="link_files",
8+
)
9+
10+
custom_js = ui.include_js(
11+
Path(__file__).parent / "js" / "customjs.js",
12+
method="link_files",
13+
)
14+
15+
# path where the JS file's parent directory is mounted
16+
href = custom_js.get_dependencies()[0].source_path_map()["href"]
17+
18+
# Define the UI
19+
app_ui = ui.page_fluid(
20+
custom_css,
21+
custom_js,
22+
ui.tags.script(src=href + "/customjs2.js"),
23+
ui.h1("Simple Shiny App with External CSS"),
24+
ui.div(
25+
ui.p("This is a simple Shiny app that demonstrates ui.include_css()"),
26+
ui.p("The styling comes from an external CSS file!"),
27+
class_="content",
28+
),
29+
)
30+
31+
32+
# Define the server
33+
def server(input: Inputs, output: Outputs, session: Session):
34+
pass
35+
36+
37+
# Create and run the app
38+
app = App(app_ui, server)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
body {
2+
font-family: Arial, sans-serif;
3+
}
4+
5+
h1 {
6+
color: black;
7+
border-bottom: 2px solid #4682b4;
8+
padding-bottom: 10px;
9+
}
10+
11+
.content {
12+
margin: 20px;
13+
padding: 15px;
14+
background-color: white;
15+
border-radius: 5px;
16+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
17+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const newParagraph = document.createElement('p');
2+
newParagraph.textContent = 'Heyo!';
3+
document.body.appendChild(newParagraph);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const special = document.createElement('p');
2+
special.textContent = 'Also here!';
3+
document.body.appendChild(special);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from playwright.sync_api import Page, expect
2+
3+
from shiny.run import ShinyAppProc
4+
5+
6+
def test_inclusion(page: Page, local_app: ShinyAppProc) -> None:
7+
page.goto(local_app.url)
8+
9+
expect(page.locator("body > p").first).to_have_text("Heyo!")
10+
expect(page.locator("body > p").last).to_have_text("Also here!")

0 commit comments

Comments
 (0)