mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-12 16:38:06 -05:00
Compare commits
252 Commits
jeff/integ
...
jeff-skele
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
337f4c9c40 | ||
|
|
e86c0c4be4 | ||
|
|
44a485b07a | ||
|
|
9138adf8a1 | ||
|
|
e588fc5a4a | ||
|
|
19965c9eb7 | ||
|
|
98e390bc1b | ||
|
|
3994055056 | ||
|
|
374f7c2aa2 | ||
|
|
27ad5d6110 | ||
|
|
d374f1dc88 | ||
|
|
38349f354d | ||
|
|
1433439215 | ||
|
|
4c8dc09f67 | ||
|
|
80b43942b0 | ||
|
|
b709b53b6a | ||
|
|
f4e3e5b618 | ||
|
|
bac7299359 | ||
|
|
fc6f535edd | ||
|
|
7e2ffab62c | ||
|
|
214d721380 | ||
|
|
2f8227e652 | ||
|
|
c0c02d290f | ||
|
|
bc2aa71888 | ||
|
|
7f187d1553 | ||
|
|
81b1f4fdc1 | ||
|
|
15f088f10a | ||
|
|
286f12522b | ||
|
|
9d8a6d0142 | ||
|
|
a2dd97cc74 | ||
|
|
1d9a6ea3c0 | ||
|
|
3ca8b1017b | ||
|
|
ecd7c76aee | ||
|
|
70edcd62b9 | ||
|
|
90f531888c | ||
|
|
953de733e7 | ||
|
|
e0ed443319 | ||
|
|
1487720fd8 | ||
|
|
828567e0ce | ||
|
|
78da4c7fce | ||
|
|
7f80bfd2cb | ||
|
|
7e3deb5e3f | ||
|
|
5475ec4f0c | ||
|
|
58b4585b57 | ||
|
|
cf9ab1c47b | ||
|
|
65233cdd5c | ||
|
|
9d13cb644d | ||
|
|
dd9e0343e8 | ||
|
|
bb4aaa2a78 | ||
|
|
0023418b94 | ||
|
|
ec2c9ecea0 | ||
|
|
59759398a6 | ||
|
|
c4852cb451 | ||
|
|
99880d6e8a | ||
|
|
b005799d92 | ||
|
|
72f86dac27 | ||
|
|
83628facb3 | ||
|
|
f6e171823a | ||
|
|
9b743a319f | ||
|
|
eedf2a6cc8 | ||
|
|
e1e738f772 | ||
|
|
182ff3df88 | ||
|
|
23fde95f9e | ||
|
|
78f9132eb3 | ||
|
|
84b7211588 | ||
|
|
2793e15c26 | ||
|
|
36bd76607a | ||
|
|
e17f416bb0 | ||
|
|
a577b1e22e | ||
|
|
2d324c77c1 | ||
|
|
88374eca74 | ||
|
|
386135788b | ||
|
|
a943d955dd | ||
|
|
15476ac32e | ||
|
|
17fb5b9eae | ||
|
|
fd27a0dfa2 | ||
|
|
5ffe69ec6c | ||
|
|
f5723b2a4d | ||
|
|
9e959a88f1 | ||
|
|
09abac41c5 | ||
|
|
1dbf013c1b | ||
|
|
a637d5b126 | ||
|
|
d409183751 | ||
|
|
e8feef1ce0 | ||
|
|
01491cc696 | ||
|
|
568a3f28cf | ||
|
|
02219df480 | ||
|
|
e006ca51ee | ||
|
|
86f651f3ec | ||
|
|
212b33a0ce | ||
|
|
6b7a121161 | ||
|
|
c89da718b1 | ||
|
|
eef3ae8387 | ||
|
|
0975a61725 | ||
|
|
0c53d54347 | ||
|
|
cbbb04cf69 | ||
|
|
120baf0a6e | ||
|
|
685dc7cc3a | ||
|
|
2fbb2ac77b | ||
|
|
2832db7aba | ||
|
|
18f2471d7c | ||
|
|
ea28f5a61b | ||
|
|
fe9cc6038e | ||
|
|
5ed335c499 | ||
|
|
fd04b97496 | ||
|
|
4c9d281b59 | ||
|
|
cfb683419f | ||
|
|
97887bdf02 | ||
|
|
38ea693e73 | ||
|
|
582a0ea6a5 | ||
|
|
71b9f0907e | ||
|
|
82b82b714d | ||
|
|
6356228053 | ||
|
|
18fd677550 | ||
|
|
d9698df721 | ||
|
|
2ee06a7cbf | ||
|
|
cf2ba90b1d | ||
|
|
8124b2143b | ||
|
|
5361573051 | ||
|
|
1d377c868d | ||
|
|
b0a855a326 | ||
|
|
fa35f29596 | ||
|
|
f429d23b6e | ||
|
|
eeeb903b70 | ||
|
|
78f12c4a75 | ||
|
|
c69f34d1e2 | ||
|
|
ccfcc5d8b4 | ||
|
|
210c248264 | ||
|
|
e3258657d0 | ||
|
|
dbc518bf53 | ||
|
|
cdbdb4510e | ||
|
|
e7ec5e5ba4 | ||
|
|
03d8a7f296 | ||
|
|
480035c065 | ||
|
|
b32c18cf72 | ||
|
|
337a6b276a | ||
|
|
06cf1f9477 | ||
|
|
190cfd2b7a | ||
|
|
63035b4d66 | ||
|
|
6a11c8fcb1 | ||
|
|
33ffb006e3 | ||
|
|
162e7f63a9 | ||
|
|
bb581eeec4 | ||
|
|
272c555bc5 | ||
|
|
fb64caab23 | ||
|
|
6f2a74a46d | ||
|
|
ec65a74492 | ||
|
|
ba791c42fa | ||
|
|
5896667c36 | ||
|
|
003c949d38 | ||
|
|
d31394254c | ||
|
|
1a497e246c | ||
|
|
d24276aa54 | ||
|
|
6ed21a3e6b | ||
|
|
8066f9ce96 | ||
|
|
a0276ec1ce | ||
|
|
2ab925a24c | ||
|
|
78fbad7d8d | ||
|
|
6652ae3042 | ||
|
|
aa12ab7d76 | ||
|
|
89be4bdce9 | ||
|
|
d09a064471 | ||
|
|
2b18ca5a6c | ||
|
|
6bc2f18bbf | ||
|
|
fbb892d84e | ||
|
|
4efb7c20e4 | ||
|
|
4beb1f07a6 | ||
|
|
45e640e5f9 | ||
|
|
e84beffee3 | ||
|
|
e07c7483a7 | ||
|
|
34ec7bf5eb | ||
|
|
01b20a4829 | ||
|
|
45ea898da4 | ||
|
|
fd34c5070f | ||
|
|
6c409d96c1 | ||
|
|
0cbe4bb3d4 | ||
|
|
d04a990235 | ||
|
|
4747c87632 | ||
|
|
f57452c7bf | ||
|
|
9a8e2eb675 | ||
|
|
8ef7f3cbe2 | ||
|
|
de30a65f01 | ||
|
|
0bcf613195 | ||
|
|
89fd9004d0 | ||
|
|
b2be108db1 | ||
|
|
6102c44b70 | ||
|
|
327cdc8e41 | ||
|
|
0bc3613989 | ||
|
|
30cea871f9 | ||
|
|
5f332fe4db | ||
|
|
7ee7f2716b | ||
|
|
5e8c39cb1e | ||
|
|
ee355200b3 | ||
|
|
986fbe2254 | ||
|
|
32f93a2be1 | ||
|
|
ab79065c13 | ||
|
|
77171b7894 | ||
|
|
cce8ddb84f | ||
|
|
648b7e5911 | ||
|
|
67a66fdc93 | ||
|
|
5fbaa26d05 | ||
|
|
1f4a3c4fd2 | ||
|
|
959dc7ffd4 | ||
|
|
0e34221cac | ||
|
|
0cad13b3a3 | ||
|
|
0776f71ca3 | ||
|
|
5a74e369ce | ||
|
|
799c5ac662 | ||
|
|
1080cf0ef4 | ||
|
|
867d49e3fb | ||
|
|
c7be406099 | ||
|
|
37257e77ce | ||
|
|
270d9ff0fc | ||
|
|
34b48598d9 | ||
|
|
5105ecb148 | ||
|
|
f47b151458 | ||
|
|
d3f15a58fc | ||
|
|
42f6adb7fa | ||
|
|
263f8a8e7d | ||
|
|
3a42d30cfd | ||
|
|
9275217a5a | ||
|
|
1fed19ad68 | ||
|
|
6a8a78abd1 | ||
|
|
de69f51084 | ||
|
|
c81a3f39fd | ||
|
|
6fcb925e34 | ||
|
|
8823b7280a | ||
|
|
ebadad97a8 | ||
|
|
a095c39626 | ||
|
|
fb9bcb44c3 | ||
|
|
38f593450a | ||
|
|
6d44f2c5cb | ||
|
|
d1786a64c4 | ||
|
|
616ae99c0b | ||
|
|
4d2ff80788 | ||
|
|
005295fd4c | ||
|
|
d6b46f8243 | ||
|
|
bac35e8f1b | ||
|
|
a003c4da85 | ||
|
|
0ae8e4fe8a | ||
|
|
26ba9bf94a | ||
|
|
85a2d41a72 | ||
|
|
c41d38bf61 | ||
|
|
b155e8480b | ||
|
|
e94f687573 | ||
|
|
5883082d01 | ||
|
|
75b53ffda1 | ||
|
|
324d9195c3 | ||
|
|
4ad115e024 | ||
|
|
f11d754cfe | ||
|
|
65019ce96f | ||
|
|
90e8fb2a57 |
14
.travis.yml
14
.travis.yml
@@ -5,14 +5,18 @@ matrix:
|
||||
r: release
|
||||
r_packages:
|
||||
- devtools
|
||||
- roxygen2
|
||||
script: ./tools/checkDocsCurrent.sh
|
||||
- rprojroot
|
||||
script: ./tools/documentation/checkDocsCurrent.sh
|
||||
env:
|
||||
# GITHUB_PAT for gh::gh calls
|
||||
- secure: "Hk4piVNtDobLT1dQPnCOcM7sOlwNGJOU5cpvbRvOxYSgxP+Bj2MyRZMe825rdHkHbFez0h8w3tJOBf9DDBH7PC1BhhNll2+WM/WxGlkNleg8vsoH/Xopffl+2YgtWbAYZjQ2j0QYdgNn0e/TY86/ggk9qit6+gpsZ7z/HmWQuVY="
|
||||
|
||||
- name: "Javascript check"
|
||||
language: node_js
|
||||
cache: yarn
|
||||
script: ./tools/checkJSCurrent.sh
|
||||
node_js:
|
||||
- "10"
|
||||
- "12"
|
||||
- r: 3.2
|
||||
- r: 3.3
|
||||
- r: 3.4
|
||||
@@ -26,7 +30,3 @@ notifications:
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: change
|
||||
slack:
|
||||
on_success: change
|
||||
secure: QoM0+hliVC4l2HYv126AkljG/uFvgwayW9IpuB5QNqjSukM122MhMDL7ZuMB9a2vWP24juzOTXiNIymgEspfnvvAMnZwYRBNWkuot2m8HIR2B9UjQLiztFnN1EAT+P+thz8Qax9TV2SOfXb2S2ZOeZmRTVkJctxkL8heAZadIC4=
|
||||
on_pull_requests: false
|
||||
|
||||
25
DESCRIPTION
25
DESCRIPTION
@@ -1,7 +1,7 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 1.4.0.9000
|
||||
Version: 1.4.0.9002
|
||||
Authors@R: c(
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
|
||||
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
|
||||
@@ -70,7 +70,7 @@ Imports:
|
||||
jsonlite (>= 0.9.16),
|
||||
xtable,
|
||||
digest,
|
||||
htmltools (>= 0.4.0),
|
||||
htmltools (>= 0.4.0.9001),
|
||||
R6 (>= 2.0),
|
||||
sourcetools,
|
||||
later (>= 1.0.0),
|
||||
@@ -78,7 +78,10 @@ Imports:
|
||||
tools,
|
||||
crayon,
|
||||
rlang (>= 0.4.0),
|
||||
fastmap (>= 1.0.0)
|
||||
fastmap (>= 1.0.0),
|
||||
withr,
|
||||
commonmark (>= 1.7),
|
||||
glue (>= 1.3.2)
|
||||
Suggests:
|
||||
datasets,
|
||||
Cairo (>= 1.5-5),
|
||||
@@ -89,11 +92,17 @@ Suggests:
|
||||
ggplot2,
|
||||
reactlog (>= 1.0.0),
|
||||
magrittr,
|
||||
yaml
|
||||
shinytest,
|
||||
yaml,
|
||||
future,
|
||||
dygraphs
|
||||
Remotes:
|
||||
rstudio/htmltools
|
||||
URL: http://shiny.rstudio.com
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
Collate:
|
||||
'app.R'
|
||||
'app_template.R'
|
||||
'bookmark-state-local.R'
|
||||
'stack.R'
|
||||
'bookmark-state.R'
|
||||
@@ -141,12 +150,15 @@ Collate:
|
||||
'jqueryui.R'
|
||||
'middleware-shiny.R'
|
||||
'middleware.R'
|
||||
'timer.R'
|
||||
'mock-session.R'
|
||||
'modal.R'
|
||||
'modules.R'
|
||||
'notifications.R'
|
||||
'priorityqueue.R'
|
||||
'progress.R'
|
||||
'react.R'
|
||||
'reexports.R'
|
||||
'render-cached-plot.R'
|
||||
'render-plot.R'
|
||||
'render-table.R'
|
||||
@@ -162,8 +174,9 @@ Collate:
|
||||
'snapshot.R'
|
||||
'tar.R'
|
||||
'test-export.R'
|
||||
'timer.R'
|
||||
'test-server.R'
|
||||
'test.R'
|
||||
'update-input.R'
|
||||
RoxygenNote: 6.1.1
|
||||
RoxygenNote: 7.1.0
|
||||
Encoding: UTF-8
|
||||
Roxygen: list(markdown = TRUE)
|
||||
|
||||
49
NAMESPACE
49
NAMESPACE
@@ -1,15 +1,18 @@
|
||||
# Generated by roxygen2: do not edit by hand
|
||||
|
||||
S3method("$",mockclientdata)
|
||||
S3method("$",reactivevalues)
|
||||
S3method("$",session_proxy)
|
||||
S3method("$",shinyoutput)
|
||||
S3method("$<-",reactivevalues)
|
||||
S3method("$<-",session_proxy)
|
||||
S3method("$<-",shinyoutput)
|
||||
S3method("[",mockclientdata)
|
||||
S3method("[",reactivevalues)
|
||||
S3method("[",shinyoutput)
|
||||
S3method("[<-",reactivevalues)
|
||||
S3method("[<-",shinyoutput)
|
||||
S3method("[[",mockclientdata)
|
||||
S3method("[[",reactivevalues)
|
||||
S3method("[[",session_proxy)
|
||||
S3method("[[",shinyoutput)
|
||||
@@ -34,6 +37,7 @@ export("conditionStackTrace<-")
|
||||
export(..stacktraceoff..)
|
||||
export(..stacktraceon..)
|
||||
export(HTML)
|
||||
export(MockShinySession)
|
||||
export(NS)
|
||||
export(Progress)
|
||||
export(a)
|
||||
@@ -138,10 +142,12 @@ export(loadSupport)
|
||||
export(mainPanel)
|
||||
export(makeReactiveBinding)
|
||||
export(markRenderFunction)
|
||||
export(markdown)
|
||||
export(maskReactiveContext)
|
||||
export(memoryCache)
|
||||
export(modalButton)
|
||||
export(modalDialog)
|
||||
export(moduleServer)
|
||||
export(navbarMenu)
|
||||
export(navbarPage)
|
||||
export(navlistPanel)
|
||||
@@ -212,6 +218,7 @@ export(runExample)
|
||||
export(runGadget)
|
||||
export(runGist)
|
||||
export(runGitHub)
|
||||
export(runTests)
|
||||
export(runUrl)
|
||||
export(safeError)
|
||||
export(selectInput)
|
||||
@@ -223,6 +230,7 @@ export(setSerializer)
|
||||
export(shinyApp)
|
||||
export(shinyAppDir)
|
||||
export(shinyAppFile)
|
||||
export(shinyAppTemplate)
|
||||
export(shinyOptions)
|
||||
export(shinyServer)
|
||||
export(shinyUI)
|
||||
@@ -257,6 +265,7 @@ export(tagHasAttribute)
|
||||
export(tagList)
|
||||
export(tagSetChildren)
|
||||
export(tags)
|
||||
export(testServer)
|
||||
export(textAreaInput)
|
||||
export(textInput)
|
||||
export(textOutput)
|
||||
@@ -306,3 +315,43 @@ importFrom(fastmap,is.key_missing)
|
||||
importFrom(fastmap,key_missing)
|
||||
importFrom(grDevices,dev.cur)
|
||||
importFrom(grDevices,dev.set)
|
||||
importFrom(htmltools,HTML)
|
||||
importFrom(htmltools,a)
|
||||
importFrom(htmltools,br)
|
||||
importFrom(htmltools,code)
|
||||
importFrom(htmltools,div)
|
||||
importFrom(htmltools,em)
|
||||
importFrom(htmltools,h1)
|
||||
importFrom(htmltools,h2)
|
||||
importFrom(htmltools,h3)
|
||||
importFrom(htmltools,h4)
|
||||
importFrom(htmltools,h5)
|
||||
importFrom(htmltools,h6)
|
||||
importFrom(htmltools,hr)
|
||||
importFrom(htmltools,htmlTemplate)
|
||||
importFrom(htmltools,img)
|
||||
importFrom(htmltools,includeCSS)
|
||||
importFrom(htmltools,includeHTML)
|
||||
importFrom(htmltools,includeMarkdown)
|
||||
importFrom(htmltools,includeScript)
|
||||
importFrom(htmltools,includeText)
|
||||
importFrom(htmltools,is.singleton)
|
||||
importFrom(htmltools,p)
|
||||
importFrom(htmltools,pre)
|
||||
importFrom(htmltools,singleton)
|
||||
importFrom(htmltools,span)
|
||||
importFrom(htmltools,strong)
|
||||
importFrom(htmltools,suppressDependencies)
|
||||
importFrom(htmltools,tag)
|
||||
importFrom(htmltools,tagAppendAttributes)
|
||||
importFrom(htmltools,tagAppendChild)
|
||||
importFrom(htmltools,tagAppendChildren)
|
||||
importFrom(htmltools,tagGetAttribute)
|
||||
importFrom(htmltools,tagHasAttribute)
|
||||
importFrom(htmltools,tagList)
|
||||
importFrom(htmltools,tagSetChildren)
|
||||
importFrom(htmltools,tags)
|
||||
importFrom(htmltools,validateCssUnit)
|
||||
importFrom(htmltools,withTags)
|
||||
importFrom(promises,"%...!%")
|
||||
importFrom(promises,"%...>%")
|
||||
|
||||
38
NEWS.md
38
NEWS.md
@@ -1,4 +1,4 @@
|
||||
shiny 1.4.0.9000
|
||||
shiny 1.4.0.9001
|
||||
===========
|
||||
|
||||
## Full changelog
|
||||
@@ -7,15 +7,47 @@ shiny 1.4.0.9000
|
||||
|
||||
### New features
|
||||
|
||||
* `runTests()` is a new function that behaves much like R CMD check. `runTests()` invokes all of the top-level R files in the tests/ directory inside an application, in that application's environment. ([#2585](https://github.com/rstudio/shiny/pull/2585))
|
||||
|
||||
* `testServer()` and `testModule()` are two new functions for testing reactive behavior inside server functions and modules, respectively. ([#2682](https://github.com/rstudio/shiny/pull/2682), [#2764](https://github.com/rstudio/shiny/pull/2764))
|
||||
|
||||
* The new `moduleServer` function provides a simpler interface for creating and using modules. ([#2773](https://github.com/rstudio/shiny/pull/2773))
|
||||
|
||||
* Resolved [#2732](https://github.com/rstudio/shiny/issues/2732): `markdown()` is a new function for writing Markdown with Github extensions directly in Shiny UIs. Markdown rendering is performed by the [commonmark](https://github.com/jeroen/commonmark) package. ([#2737](https://github.com/rstudio/shiny/pull/2737))
|
||||
|
||||
### Minor new features and improvements
|
||||
|
||||
* Fixed [#2042](https://github.com/rstudio/shiny/issues/2042), [#2628](https://github.com/rstudio/shiny/issues/2628): In a `dateInput` and `dateRangeInput`, disabled months and years are now a lighter gray, to make it easier to see that they are disabled. ([#2690](https://github.com/rstudio/shiny/pull/2690))
|
||||
|
||||
* `getCurrentOutputInfo()` previously threw an error when called from outside of an output; now it returns `NULL`. ([#2707](https://github.com/rstudio/shiny/pull/2707))
|
||||
|
||||
* Added a label to observer that auto-reloads `R/` directory to avoid confusion when using `reactlog`. ([#58](https://github.com/rstudio/reactlog/issues/58))
|
||||
|
||||
* `getDefaultReactiveDomain()` can now be called inside a `session$onSessionEnded` callback and will return the calling `session` information. ([#2757](https://github.com/rstudio/shiny/pull/2757))
|
||||
|
||||
* Added a `'function'` class to `reactive()` and `reactiveVal()` objects. ([#2793](https://github.com/rstudio/shiny/pull/2793))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
Fixed [#2653](https://github.com/rstudio/shiny/issues/2653): The `dataTableOutput()` could have incorrect output if certain characters were in the column names. ([#2658](https://github.com/rstudio/shiny/pull/2658))
|
||||
* Fixed [#2606](https://github.com/rstudio/shiny/issues/2606): `debounce()` would not work properly if the code in the reactive expression threw an error on the first run. ([#2652](https://github.com/rstudio/shiny/pull/2652))
|
||||
|
||||
* Fixed [#2653](https://github.com/rstudio/shiny/issues/2653): The `dataTableOutput()` could have incorrect output if certain characters were in the column names. ([#2658](https://github.com/rstudio/shiny/pull/2658))
|
||||
|
||||
### Documentation Updates
|
||||
|
||||
|
||||
shiny 1.4.0.2
|
||||
===========
|
||||
|
||||
Minor patch release: fixed some timing-dependent tests failed intermittently on CRAN build machines.
|
||||
|
||||
|
||||
shiny 1.4.0.1
|
||||
===========
|
||||
|
||||
Minor patch release to account for changes to the grid package that will be upcoming in the R 4.0 release ([#2776](https://github.com/rstudio/shiny/pull/2776)).
|
||||
|
||||
|
||||
shiny 1.4.0
|
||||
===========
|
||||
|
||||
@@ -85,7 +117,7 @@ shiny 1.3.2
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed [#2285](https://github.com/rstudio/shiny/issues/2285), [#2288](https://github.com/rstudio/shiny/issues/2288): Static CSS/JS resources in subapps in R Markdown documents did not render properly. ([#2386](https://github.com/rstudio/shiny/pull/2386))
|
||||
* Fixed [#2385](https://github.com/rstudio/shiny/issues/2385): Static CSS/JS resources in subapps in R Markdown documents did not render properly. ([#2386](https://github.com/rstudio/shiny/pull/2386))
|
||||
|
||||
* Fixed [#2280](https://github.com/rstudio/shiny/issues/2280): Shiny applications that used a www/index.html file did not serve up the index file. ([#2382](https://github.com/rstudio/shiny/pull/2382))
|
||||
|
||||
|
||||
36
R/app.R
36
R/app.R
@@ -13,7 +13,10 @@
|
||||
#' object to `print()` or [runApp()].
|
||||
#'
|
||||
#' @param ui The UI definition of the app (for example, a call to
|
||||
#' `fluidPage()` with nested controls)
|
||||
#' `fluidPage()` with nested controls).
|
||||
#'
|
||||
#' If bookmarking is enabled (see `enableBookmarking`), this must be
|
||||
#' a single argument function that returns the UI definition.
|
||||
#' @param server A function with three parameters: `input`, `output`, and
|
||||
#' `session`. The function is called once for each session ensuring that each
|
||||
#' app is independent.
|
||||
@@ -30,11 +33,9 @@
|
||||
#' request. Note that the entire request path must match the regular
|
||||
#' expression in order for the match to be considered successful.
|
||||
#' @param enableBookmarking Can be one of `"url"`, `"server"`, or
|
||||
#' `"disable"`. This is equivalent to calling the
|
||||
#' [enableBookmarking()] function just before calling
|
||||
#' `shinyApp()`. With the default value (`NULL`), the app will
|
||||
#' respect the setting from any previous calls to `enableBookmarking()`.
|
||||
#' See [enableBookmarking()] for more information.
|
||||
#' `"disable"`. The default value, `NULL`, will respect the setting from
|
||||
#' any previous calls to [enableBookmarking()]. See [enableBookmarking()]
|
||||
#' for more information on bookmarking your app.
|
||||
#' @return An object that represents the app. Printing the object or passing it
|
||||
#' to [runApp()] will run the app.
|
||||
#'
|
||||
@@ -283,7 +284,8 @@ initAutoReloadMonitor <- function(dir) {
|
||||
".*\\.(r|html?|js|css|png|jpe?g|gif)$")
|
||||
|
||||
lastValue <- NULL
|
||||
obs <- observe({
|
||||
observeLabel <- paste0("File Auto-Reload - '", basename(dir), "'")
|
||||
obs <- observe(label = observeLabel, {
|
||||
files <- sort(list.files(dir, pattern = filePattern, recursive = TRUE,
|
||||
ignore.case = TRUE))
|
||||
times <- file.info(files)$mtime
|
||||
@@ -313,14 +315,13 @@ initAutoReloadMonitor <- function(dir) {
|
||||
#' adjacent to the `app.R`/`server.R`/`ui.R` files.
|
||||
#'
|
||||
#' Since Shiny 1.5.0, this function is called by default when running an
|
||||
#' application. If it causes problems, you can opt out by using
|
||||
#' `options(shiny.autoload.r=FALSE)`. Note that in a future version of Shiny,
|
||||
#' this option will no longer be available. If you set this option, it will
|
||||
#' application. If it causes problems, there are two ways to opt out. You can
|
||||
#' either place a file named `_disable_autoload.R` in your R/ directory, or
|
||||
#' set `options(shiny.autoload.r=FALSE)`. If you set this option, it will
|
||||
#' affect any application that runs later in the same R session, potentially
|
||||
#' breaking it, so after running your application, you should unset option with
|
||||
#' `options(shiny.autoload.r=NULL)`
|
||||
#'
|
||||
#'
|
||||
#' @details The files are sourced in alphabetical order (as determined by
|
||||
#' [list.files]). `global.R` is evaluated before the supporting R files in the
|
||||
#' `R/` directory.
|
||||
@@ -339,8 +340,21 @@ loadSupport <- function(appDir, renv=new.env(parent=globalenv()), globalrenv=glo
|
||||
}
|
||||
|
||||
helpersDir <- file.path(appDir, "R")
|
||||
|
||||
disabled <- list.files(helpersDir, pattern="^_disable_autoload\\.r$", recursive=FALSE, ignore.case=TRUE)
|
||||
if (length(disabled) > 0){
|
||||
message("R/_disable_autoload.R detected; not loading the R/ directory automatically")
|
||||
return(invisible(renv))
|
||||
}
|
||||
|
||||
helpers <- list.files(helpersDir, pattern="\\.[rR]$", recursive=FALSE, full.names=TRUE)
|
||||
|
||||
if (length(helpers) > 0){
|
||||
message("Automatically loading ", length(helpers), " .R file",
|
||||
ifelse(length(helpers) != 1, "s", ""),
|
||||
" found in the R/ directory.\nSee https://rstd.io/shiny-autoload for more info.")
|
||||
}
|
||||
|
||||
lapply(helpers, sourceUTF8, envir=renv)
|
||||
|
||||
invisible(renv)
|
||||
|
||||
230
R/app_template.R
Normal file
230
R/app_template.R
Normal file
@@ -0,0 +1,230 @@
|
||||
#' Generate a Shiny application from a template
|
||||
#'
|
||||
#' This function populates a directory with files for a Shiny application.
|
||||
#'
|
||||
#' In an interactive R session, this function will, by default, prompt the user
|
||||
#' which components to add to the application.
|
||||
#'
|
||||
#' The full example application includes the following files and directories:
|
||||
#'
|
||||
#' ```
|
||||
#' appdir/
|
||||
#' |- app.R
|
||||
#' |- R
|
||||
#' | |- my-module.R
|
||||
#' | |-- sort.R
|
||||
#' `-- tests
|
||||
#' |- server.R
|
||||
#' |- server
|
||||
#' | |- test-mymodule.R
|
||||
#' | `- test-server.R
|
||||
#' |- shinytest.R
|
||||
#' |- shinytest
|
||||
#' | `- mytest.R
|
||||
#' |- testthat.R
|
||||
#' `-- testthat
|
||||
#' |- helper-load.R
|
||||
#' `- test-sort.R
|
||||
#' ```
|
||||
#'
|
||||
#' Some notes about these files:
|
||||
#' * `app.R` is the main application file.
|
||||
#' * All files in the `R/` subdirectory are automatically sourced when the
|
||||
#' application is run.
|
||||
#' * `R/sort.R` and `R/my-module.R` are automatically sourced when
|
||||
#' the application is run. The first contains a function `lexical_sort()`,
|
||||
#' and the second contains code for a [Shiny module](moduleServer()) which
|
||||
#' is used in the application.
|
||||
#' * `tests/` contains various tests for the application. You may
|
||||
#' choose to use or remove any of them. They can be executed by the
|
||||
#' [runTests()] function.
|
||||
#' * `tests/server.R` is a test runner for test files in
|
||||
#' `tests/server/`.
|
||||
#' * `tests/server/test-mymodule.R` is a test for the module.
|
||||
#' * `tests/shinytest.R` is a test runner for test files in the
|
||||
#' `tests/shinytest/` directory.
|
||||
#' * `tests/shinytest/mytest.R` is a test that uses the
|
||||
#' [shinytest](https://rstudio.github.io/shinytest/) package to do
|
||||
#' snapshot-based testing.
|
||||
#' * `tests/testthat.R` is a test runner for test files in the
|
||||
#' `tests/testthat/` directory.
|
||||
#' * `tests/testthat/helper-load.R` is a helper script that is automatically
|
||||
#' loaded before running `test-mymodule.`R. (This is performed by the testthat
|
||||
#' package.)
|
||||
#' * `tests/testthat/test-sort.R` is a set of tests that use the
|
||||
#' [testthat](https://testthat.r-lib.org/) package for testing.
|
||||
#'
|
||||
#' @param path Path to create new shiny application template.
|
||||
#' @param examples Either one of "default", "ask", "all", or any combination of
|
||||
#' "app", "rdir", "module", "shinytest", "testthat", and "server". In an
|
||||
#' interactive session, "default" falls back to "ask"; in a non-interactive
|
||||
#' session, "default" falls back to "all". With "ask", this function will
|
||||
#' prompt the user to select which template items will be added to the new app
|
||||
#' directory. With "all", all template items will be added to the app
|
||||
#' directory.
|
||||
#'
|
||||
#' @export
|
||||
shinyAppTemplate <- function(path = NULL, examples = "default")
|
||||
{
|
||||
if (is.null(path)) {
|
||||
stop("Please provide a `path`.")
|
||||
}
|
||||
|
||||
choices <- c(
|
||||
app = "app.R : Main application file",
|
||||
rdir = "R/sort.R : Helper file with R code",
|
||||
module = "R/my-module.R : Example module",
|
||||
shinytest = "tests/shinytest/ : Tests using shinytest package",
|
||||
testthat = "tests/testthat/ : Tests using testthat",
|
||||
server = "tests/server/ : Tests of server and module code"
|
||||
)
|
||||
|
||||
if (identical(examples, "default")) {
|
||||
if (interactive()) {
|
||||
examples <- "ask"
|
||||
} else {
|
||||
examples <- "all"
|
||||
}
|
||||
}
|
||||
|
||||
if (!identical(examples, "ask") &&
|
||||
!identical(examples, "all") &&
|
||||
any(! examples %in% names(choices)))
|
||||
{
|
||||
stop('`examples` must be one of "default", "ask", "all", or any combination of "',
|
||||
paste(names(choices), collapse = '", "'), '".')
|
||||
}
|
||||
|
||||
if (identical(examples, "ask")) {
|
||||
response <- select_menu(
|
||||
c(all = "All", choices),
|
||||
title = paste0(
|
||||
"Select which of the following to add at ", path, "/ :"
|
||||
),
|
||||
msg = "Enter one or more numbers (with spaces), or an empty line to exit: \n"
|
||||
)
|
||||
|
||||
examples <- names(response)
|
||||
}
|
||||
|
||||
examples <- unique(examples)
|
||||
|
||||
if ("all" %in% examples) {
|
||||
examples <- names(choices)
|
||||
}
|
||||
|
||||
if (length(examples) == 0) {
|
||||
return(invisible())
|
||||
}
|
||||
|
||||
# Check if a directory is empty, ignoring certain files
|
||||
dir_is_empty <- function(path) {
|
||||
files <- list.files(path, all.files = TRUE, no.. = TRUE)
|
||||
# Ignore .DS_Store files, which are sometimes automatically created on macOS
|
||||
files <- setdiff(files, ".DS_Store")
|
||||
return(length(files) != 0)
|
||||
}
|
||||
|
||||
# Helper to resolve paths relative to our example
|
||||
example_path <- function(path) {
|
||||
system.file("app_template", path, package = "shiny")
|
||||
}
|
||||
|
||||
# Copy the files for a tests/ subdirectory
|
||||
copy_test_dir <- function(name, with_rdir, with_module) {
|
||||
tests_dir <- file.path(path, "tests")
|
||||
dir.create(tests_dir, showWarnings = FALSE, recursive = TRUE)
|
||||
|
||||
files <- dir(example_path("tests"), recursive = TRUE)
|
||||
# Note: This is not the same as using dir(pattern = "^shinytest"), since
|
||||
# that will not match files inside of shinytest/.
|
||||
files <- files[grepl(paste0("^", name), files)]
|
||||
|
||||
# Filter out files related to R/sort.R, if applicable.
|
||||
if (!with_rdir) {
|
||||
files <- files[!grepl("utils", files)]
|
||||
}
|
||||
|
||||
# Filter out module files, if applicable.
|
||||
if (!with_module) {
|
||||
files <- files[!grepl("module", files)]
|
||||
}
|
||||
|
||||
# Create any subdirectories if needed
|
||||
dirs <- setdiff(unique(dirname(files)), ".")
|
||||
for (dir in dirs) {
|
||||
dir.create(file.path(tests_dir, dir), showWarnings = FALSE, recursive = TRUE)
|
||||
}
|
||||
|
||||
file.copy(
|
||||
file.path(example_path("tests"), files),
|
||||
file.path(path, "tests", files)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
if (is.null(path)) {
|
||||
stop("`path` is missing.")
|
||||
}
|
||||
if (file.exists(path) && !dir.exists(path)) {
|
||||
stop(path, " exists but is not a directory.")
|
||||
}
|
||||
|
||||
if (dir.exists(path) && dir_is_empty(path)) {
|
||||
if (interactive()) {
|
||||
response <- readline(paste0(
|
||||
ensure_trailing_slash(path),
|
||||
" is not empty. Do you want to create a Shiny app in this directory anyway? [y/n] "
|
||||
))
|
||||
if (tolower(response) != "y") {
|
||||
return(invisible())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dir.create(path)
|
||||
}
|
||||
|
||||
# app.R - If "app", populate with example; otherwise use empty file.
|
||||
app_file <- file.path(path, "app.R")
|
||||
if ("app" %in% examples) {
|
||||
if (file.exists(app_file)) {
|
||||
message("Not writing ", app_file, "because file already exists.")
|
||||
|
||||
} else {
|
||||
writeChar(
|
||||
as.character(htmlTemplate(
|
||||
example_path("app.R"),
|
||||
rdir = "rdir" %in% examples,
|
||||
module = "module" %in% examples
|
||||
)),
|
||||
con = app_file,
|
||||
eos = NULL
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
# R/ dir with utils and/or module
|
||||
r_dir <- file.path(path, "R")
|
||||
if ("rdir" %in% examples) {
|
||||
dir.create(r_dir, showWarnings = FALSE, recursive = TRUE)
|
||||
file.copy(example_path("R/sort.R"), r_dir, recursive = TRUE)
|
||||
}
|
||||
if ("module" %in% examples) {
|
||||
dir.create(r_dir, showWarnings = FALSE, recursive = TRUE)
|
||||
file.copy(example_path("R/my-module.R"), r_dir, recursive = TRUE)
|
||||
}
|
||||
|
||||
# tests/ dir
|
||||
if ("shinytest" %in% examples) {
|
||||
copy_test_dir("shinytest", "rdir" %in% examples, "module" %in% examples)
|
||||
}
|
||||
if ("testthat" %in% examples) {
|
||||
copy_test_dir("testthat", "rdir" %in% examples, "module" %in% examples)
|
||||
}
|
||||
if ("server" %in% examples) {
|
||||
copy_test_dir("server", "rdir" %in% examples, "module" %in% examples)
|
||||
}
|
||||
if ("app" %in% examples) {
|
||||
message("Shiny application created at ", ensure_trailing_slash(path))
|
||||
}
|
||||
}
|
||||
@@ -231,8 +231,12 @@ column <- function(width, ..., offset = 0) {
|
||||
stop("column width must be between 1 and 12")
|
||||
|
||||
colClass <- paste0("col-sm-", width)
|
||||
if (offset > 0)
|
||||
colClass <- paste0(colClass, " col-sm-offset-", offset)
|
||||
if (offset > 0) {
|
||||
# offset-md-x is for bootstrap 4 forward compat
|
||||
# (every size tier has been bumped up one level)
|
||||
# https://github.com/twbs/bootstrap/blob/74b8fe7/docs/4.3/migration/index.html#L659
|
||||
colClass <- paste0(colClass, " offset-md-", offset, " col-sm-offset-", offset)
|
||||
}
|
||||
div(class = colClass, ...)
|
||||
}
|
||||
|
||||
@@ -566,7 +570,7 @@ splitLayout <- function(..., cellWidths = NULL, cellArgs = list()) {
|
||||
#' @param flex Determines how space should be distributed to the cells. Can be a
|
||||
#' single value like `1` or `2` to evenly distribute the available
|
||||
#' space; or use a vector of numbers to specify the proportions. For example,
|
||||
#' `flex = c(2, 3)` would cause the space to be split 40\%/60\% between
|
||||
#' `flex = c(2, 3)` would cause the space to be split 40%/60% between
|
||||
#' two cells. NA values will cause the corresponding cell to be sized
|
||||
#' according to its contents (without growing or shrinking).
|
||||
#' @param width,height The total amount of width and height to use for the
|
||||
|
||||
@@ -103,7 +103,7 @@ basicPage <- function(...) {
|
||||
#' *automatic* height; that is, they determine their own height based on
|
||||
#' the height of their contained elements. However,
|
||||
#' `fillPage(plotOutput("plot", height = "100%"))` will work because
|
||||
#' `fillPage` fixes the `<body>` height at 100\% of the window height.
|
||||
#' `fillPage` fixes the `<body>` height at 100% of the window height.
|
||||
#'
|
||||
#' Note that `fillPage(plotOutput("plot"))` will not cause the plot to fill
|
||||
#' the page. Like most Shiny output widgets, `plotOutput`'s default height
|
||||
@@ -793,50 +793,43 @@ buildTabItem <- function(index, tabsetId, foundSelected, tabs = NULL,
|
||||
|
||||
#' Create a text output element
|
||||
#'
|
||||
#' Render a reactive output variable as text within an application page. The
|
||||
#' text will be included within an HTML `div` tag by default.
|
||||
#' Render a reactive output variable as text within an application page.
|
||||
#' `textOutput()` is usually paired with [renderText()] and puts regular text
|
||||
#' in `<div>` or `<span>`; `verbatimTextOutput()` is usually paired with
|
||||
#' [renderPrint()] and provudes fixed-width text in a `<pre>`.
|
||||
#'
|
||||
#' In both funtions, text is HTML-escaped prior to rendering.
|
||||
#'
|
||||
#' @param outputId output variable to read the value from
|
||||
#' @param container a function to generate an HTML element to contain the text
|
||||
#' @param inline use an inline (`span()`) or block container (`div()`)
|
||||
#' for the output
|
||||
#' @return A text output element that can be included in a panel
|
||||
#' @details Text is HTML-escaped prior to rendering. This element is often used
|
||||
#' to display [renderText] output variables.
|
||||
#' @examples
|
||||
#' h3(textOutput("caption"))
|
||||
#' @export
|
||||
textOutput <- function(outputId, container = if (inline) span else div, inline = FALSE) {
|
||||
container(id = outputId, class = "shiny-text-output")
|
||||
}
|
||||
|
||||
#' Create a verbatim text output element
|
||||
#'
|
||||
#' Render a reactive output variable as verbatim text within an
|
||||
#' application page. The text will be included within an HTML `pre` tag.
|
||||
#' @param outputId output variable to read the value from
|
||||
#' @param placeholder if the output is empty or `NULL`, should an empty
|
||||
#' rectangle be displayed to serve as a placeholder? (does not affect
|
||||
#' behavior when the the output in nonempty)
|
||||
#' @return A verbatim text output element that can be included in a panel
|
||||
#' @details Text is HTML-escaped prior to rendering. This element is often used
|
||||
#' with the [renderPrint] function to preserve fixed-width formatting
|
||||
#' of printed objects.
|
||||
#' @return A output element for use in UI.
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' shinyApp(
|
||||
#' ui = basicPage(
|
||||
#' textInput("txt", "Enter the text to display below:"),
|
||||
#' verbatimTextOutput("default"),
|
||||
#' verbatimTextOutput("placeholder", placeholder = TRUE)
|
||||
#' textOutput("text"),
|
||||
#' verbatimTextOutput("verb")
|
||||
#' ),
|
||||
#' server = function(input, output) {
|
||||
#' output$default <- renderText({ input$txt })
|
||||
#' output$placeholder <- renderText({ input$txt })
|
||||
#' output$text <- renderText({ input$txt })
|
||||
#' output$verb <- renderText({ input$txt })
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#' @export
|
||||
textOutput <- function(outputId, container = if (inline) span else div, inline = FALSE) {
|
||||
container(id = outputId, class = "shiny-text-output")
|
||||
}
|
||||
|
||||
#' @param placeholder if the output is empty or `NULL`, should an empty
|
||||
#' rectangle be displayed to serve as a placeholder? (does not affect
|
||||
#' behavior when the the output in nonempty)
|
||||
#' @export
|
||||
#' @rdname textOutput
|
||||
verbatimTextOutput <- function(outputId, placeholder = FALSE) {
|
||||
pre(id = outputId,
|
||||
class = paste(c("shiny-text-output", if (!placeholder) "noplaceholder"),
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
#' @importFrom fastmap key_missing
|
||||
#' @export
|
||||
fastmap::key_missing
|
||||
|
||||
#' @importFrom fastmap is.key_missing
|
||||
#' @export
|
||||
fastmap::is.key_missing
|
||||
|
||||
|
||||
validate_key <- function(key) {
|
||||
if (!is.character(key) || length(key) != 1 || nchar(key) == 0) {
|
||||
@@ -15,4 +7,3 @@ validate_key <- function(key) {
|
||||
stop("Invalid key: ", key, ". Only lowercase letters and numbers are allowed.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' sliderInput("obs", "Number of observations", 0, 1000, 500),
|
||||
#' actionButton("goButton", "Go!"),
|
||||
#' actionButton("goButton", "Go!", class = "btn-success"),
|
||||
#' plotOutput("distPlot")
|
||||
#' )
|
||||
#'
|
||||
@@ -36,6 +36,10 @@
|
||||
#'
|
||||
#' }
|
||||
#'
|
||||
#' ## Example of adding extra class values
|
||||
#' actionButton("largeButton", "Large Primary Button", class = "btn-primary btn-lg")
|
||||
#' actionLink("infoLink", "Information Link", class = "btn-info")
|
||||
#'
|
||||
#' @seealso [observeEvent()] and [eventReactive()]
|
||||
#'
|
||||
#' @section Server value:
|
||||
|
||||
@@ -96,9 +96,9 @@ dateRangeInput <- function(inputId, label, start = NULL, end = NULL,
|
||||
|
||||
shinyInputLabel(inputId, label),
|
||||
# input-daterange class is needed for dropdown behavior
|
||||
div(class = "input-daterange input-group",
|
||||
div(class = "input-daterange input-group input-group-sm",
|
||||
tags$input(
|
||||
class = "input-sm form-control",
|
||||
class = "form-control",
|
||||
type = "text",
|
||||
`data-date-language` = language,
|
||||
`data-date-week-start` = weekstart,
|
||||
@@ -109,9 +109,14 @@ dateRangeInput <- function(inputId, label, start = NULL, end = NULL,
|
||||
`data-initial-date` = start,
|
||||
`data-date-autoclose` = if (autoclose) "true" else "false"
|
||||
),
|
||||
span(class = "input-group-addon", separator),
|
||||
# input-group-prepend and input-group-append are for bootstrap 4 forward compat
|
||||
span(class = "input-group-addon input-group-prepend input-group-append",
|
||||
span(class = "input-group-text",
|
||||
separator
|
||||
)
|
||||
),
|
||||
tags$input(
|
||||
class = "input-sm form-control",
|
||||
class = "form-control",
|
||||
type = "text",
|
||||
`data-date-language` = language,
|
||||
`data-date-week-start` = weekstart,
|
||||
|
||||
@@ -11,8 +11,15 @@
|
||||
#' @param multiple Whether the user should be allowed to select and upload
|
||||
#' multiple files at once. **Does not work on older browsers, including
|
||||
#' Internet Explorer 9 and earlier.**
|
||||
#' @param accept A character vector of MIME types; gives the browser a hint of
|
||||
#' what kind of files the server is expecting.
|
||||
#' @param accept A character vector of "unique file type specifiers" which
|
||||
#' gives the browser a hint as to the type of file the server expects.
|
||||
#' Many browsers use this prevent the user from selecting an invalid file.
|
||||
#'
|
||||
#' A unique file type specifier can be:
|
||||
#' * A case insensitive extension like `.csv` or `.rds`.
|
||||
#' * A valid MIME type, like `text/plain` or `application/pdf`
|
||||
#' * One of `audio/*`, `video/*`, or `image/*` meaning any audio, video,
|
||||
#' or image type, respectively.
|
||||
#' @param buttonLabel The label used on the button. Can be text or an HTML tag
|
||||
#' object.
|
||||
#' @param placeholder The text to show before a file has been uploaded.
|
||||
@@ -24,13 +31,7 @@
|
||||
#' ui <- fluidPage(
|
||||
#' sidebarLayout(
|
||||
#' sidebarPanel(
|
||||
#' fileInput("file1", "Choose CSV File",
|
||||
#' accept = c(
|
||||
#' "text/csv",
|
||||
#' "text/comma-separated-values,text/plain",
|
||||
#' ".csv")
|
||||
#' ),
|
||||
#' tags$hr(),
|
||||
#' fileInput("file1", "Choose CSV File", accept = ".csv"),
|
||||
#' checkboxInput("header", "Header", TRUE)
|
||||
#' ),
|
||||
#' mainPanel(
|
||||
@@ -41,17 +42,13 @@
|
||||
#'
|
||||
#' server <- function(input, output) {
|
||||
#' output$contents <- renderTable({
|
||||
#' # input$file1 will be NULL initially. After the user selects
|
||||
#' # and uploads a file, it will be a data frame with 'name',
|
||||
#' # 'size', 'type', and 'datapath' columns. The 'datapath'
|
||||
#' # column will contain the local filenames where the data can
|
||||
#' # be found.
|
||||
#' inFile <- input$file1
|
||||
#' file <- input$file1
|
||||
#' ext <- tools::file_ext(file$datapath)
|
||||
#'
|
||||
#' if (is.null(inFile))
|
||||
#' return(NULL)
|
||||
#' req(file)
|
||||
#' validate(need(ext == "csv", "Please upload a csv file"))
|
||||
#'
|
||||
#' read.csv(inFile$datapath, header = input$header)
|
||||
#' read.csv(file$datapath, header = input$header)
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
@@ -109,7 +106,8 @@ fileInput <- function(inputId, label, multiple = FALSE, accept = NULL,
|
||||
shinyInputLabel(inputId, label),
|
||||
|
||||
div(class = "input-group",
|
||||
tags$label(class = "input-group-btn",
|
||||
# input-group-prepend is for bootstrap 4 compat
|
||||
tags$label(class = "input-group-btn input-group-prepend",
|
||||
span(class = "btn btn-default btn-file",
|
||||
buttonLabel,
|
||||
inputTag
|
||||
@@ -122,7 +120,7 @@ fileInput <- function(inputId, label, multiple = FALSE, accept = NULL,
|
||||
|
||||
tags$div(
|
||||
id=paste(inputId, "_progress", sep=""),
|
||||
class="progress progress-striped active shiny-file-input-progress",
|
||||
class="progress active shiny-file-input-progress",
|
||||
tags$div(class="progress-bar")
|
||||
)
|
||||
)
|
||||
|
||||
160
R/insert-ui.R
160
R/insert-ui.R
@@ -1,55 +1,54 @@
|
||||
#' Insert UI objects
|
||||
#' Insert and remove UI objects
|
||||
#'
|
||||
#' Insert a UI object into the app.
|
||||
#'
|
||||
#' This function allows you to dynamically add an arbitrarily large UI
|
||||
#' object into your app, whenever you want, as many times as you want.
|
||||
#' Unlike [renderUI()], the UI generated with `insertUI`
|
||||
#' is not updatable as a whole: once it's created, it stays there. Each
|
||||
#' new call to `insertUI` creates more UI objects, in addition to
|
||||
#' These functions allow you to dynamically add and remove arbirary UI
|
||||
#' into your app, whenever you want, as many times as you want.
|
||||
#' Unlike [renderUI()], the UI generated with `insertUI()` is persistent:
|
||||
#' once it's created, it stays there until removed by `removeUI()`. Each
|
||||
#' new call to `insertUI()` creates more UI objects, in addition to
|
||||
#' the ones already there (all independent from one another). To
|
||||
#' update a part of the UI (ex: an input object), you must use the
|
||||
#' appropriate `render` function or a customized `reactive`
|
||||
#' function. To remove any part of your UI, use [removeUI()].
|
||||
#' function.
|
||||
#'
|
||||
#' @param selector A string that is accepted by jQuery's selector (i.e. the
|
||||
#' string `s` to be placed in a `$(s)` jQuery call). This selector
|
||||
#' will determine the element(s) relative to which you want to insert your
|
||||
#' UI object.
|
||||
#' It's particularly useful to pair `removeUI` with `insertUI()`, but there is
|
||||
#' no restriction on what you can use on. Any element that can be selected
|
||||
#' through a jQuery selector can be removed through this function.
|
||||
#'
|
||||
#' @param selector A string that is accepted by jQuery's selector
|
||||
#' (i.e. the string `s` to be placed in a `$(s)` jQuery call).
|
||||
#'
|
||||
#' For `insertUI()` this determines the element(s) relative to which you
|
||||
#' want to insert your UI object. For `removeUI()` this determine the
|
||||
#' element(s) to be removed. If you want to remove a Shiny input or output,
|
||||
#' note that many of these are wrapped in `<div>`s, so you may need to use a
|
||||
#' somewhat complex selector --- see the Examples below. (Alternatively, you
|
||||
#' could also wrap the inputs/outputs that you want to be able to remove
|
||||
#' easily in a `<div>` with an id.)
|
||||
#' @param where Where your UI object should go relative to the selector:
|
||||
#' \describe{
|
||||
#' \item{`beforeBegin`}{Before the selector element itself}
|
||||
#' \item{`afterBegin`}{Just inside the selector element, before its
|
||||
#' first child}
|
||||
#' \item{`beforeEnd`}{Just inside the selector element, after its
|
||||
#' last child (default)}
|
||||
#' \item{`afterEnd`}{After the selector element itself}
|
||||
#' }
|
||||
#' Adapted from
|
||||
#' [here](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML).
|
||||
#'
|
||||
#' \describe{
|
||||
#' \item{`beforeBegin`}{Before the selector element itself}
|
||||
#' \item{`afterBegin`}{Just inside the selector element, before its
|
||||
#' first child}
|
||||
#' \item{`beforeEnd`}{Just inside the selector element, after its
|
||||
#' last child (default)}
|
||||
#' \item{`afterEnd`}{After the selector element itself}
|
||||
#' }
|
||||
#' Adapted from <https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML>.
|
||||
#' @param ui The UI object you want to insert. This can be anything that
|
||||
#' you usually put inside your apps's `ui` function. If you're inserting
|
||||
#' multiple elements in one call, make sure to wrap them in either a
|
||||
#' `tagList()` or a `tags$div()` (the latter option has the
|
||||
#' advantage that you can give it an `id` to make it easier to
|
||||
#' reference or remove it later on). If you want to insert raw html, use
|
||||
#' `ui = HTML()`.
|
||||
#'
|
||||
#' you usually put inside your apps's `ui` function. If you're inserting
|
||||
#' multiple elements in one call, make sure to wrap them in either a
|
||||
#' `tagList()` or a `tags$div()` (the latter option has the
|
||||
#' advantage that you can give it an `id` to make it easier to
|
||||
#' reference or remove it later on). If you want to insert raw html, use
|
||||
#' `ui = HTML()`.
|
||||
#' @param multiple In case your selector matches more than one element,
|
||||
#' `multiple` determines whether Shiny should insert the UI object
|
||||
#' relative to all matched elements or just relative to the first
|
||||
#' matched element (default).
|
||||
#'
|
||||
#' @param immediate Whether the UI object should be immediately inserted into
|
||||
#' the app when you call `insertUI`, or whether Shiny should wait until
|
||||
#' all outputs have been updated and all observers have been run (default).
|
||||
#'
|
||||
#' @param session The shiny session within which to call `insertUI`.
|
||||
#'
|
||||
#' @seealso [removeUI()]
|
||||
#'
|
||||
#' `multiple` determines whether Shiny should insert the UI object
|
||||
#' relative to all matched elements or just relative to the first
|
||||
#' matched element (default).
|
||||
#' @param immediate Whether the UI object should be immediately inserted
|
||||
#' or removed, or whether Shiny should wait until all outputs have been
|
||||
#' updated and all observers have been run (default).
|
||||
#' @param session The shiny session. Advanced use only.
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
@@ -73,6 +72,26 @@
|
||||
#' # Complete app with UI and server components
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' if (interactive()) {
|
||||
#' # Define UI
|
||||
#' ui <- fluidPage(
|
||||
#' actionButton("rmv", "Remove UI"),
|
||||
#' textInput("txt", "This is no longer useful")
|
||||
#' )
|
||||
#'
|
||||
#' # Server logic
|
||||
#' server <- function(input, output, session) {
|
||||
#' observeEvent(input$rmv, {
|
||||
#' removeUI(
|
||||
#' selector = "div:has(> #txt)"
|
||||
#' )
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' # Complete app with UI and server components
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#' @export
|
||||
insertUI <- function(selector,
|
||||
where = c("beforeBegin", "afterBegin", "beforeEnd", "afterEnd"),
|
||||
@@ -100,60 +119,7 @@ insertUI <- function(selector,
|
||||
}
|
||||
|
||||
|
||||
#' Remove UI objects
|
||||
#'
|
||||
#' Remove a UI object from the app.
|
||||
#'
|
||||
#' This function allows you to remove any part of your UI. Once `removeUI`
|
||||
#' is executed on some element, it is gone forever.
|
||||
#'
|
||||
#' While it may be a particularly useful pattern to pair this with
|
||||
#' [insertUI()] (to remove some UI you had previously inserted),
|
||||
#' there is no restriction on what you can use `removeUI` on. Any
|
||||
#' element that can be selected through a jQuery selector can be removed
|
||||
#' through this function.
|
||||
#'
|
||||
#' @param selector A string that is accepted by jQuery's selector (i.e. the
|
||||
#' string `s` to be placed in a `$(s)` jQuery call). This selector
|
||||
#' will determine the element(s) to be removed. If you want to remove a
|
||||
#' Shiny input or output, note that many of these are wrapped in `div`s,
|
||||
#' so you may need to use a somewhat complex selector --- see the Examples below.
|
||||
#' (Alternatively, you could also wrap the inputs/outputs that you want to be
|
||||
#' able to remove easily in a `div` with an id.)
|
||||
#'
|
||||
#' @param multiple In case your selector matches more than one element,
|
||||
#' `multiple` determines whether Shiny should remove all the matched
|
||||
#' elements or just the first matched element (default).
|
||||
#'
|
||||
#' @param immediate Whether the element(s) should be immediately removed from
|
||||
#' the app when you call `removeUI`, or whether Shiny should wait until
|
||||
#' all outputs have been updated and all observers have been run (default).
|
||||
#'
|
||||
#' @param session The shiny session within which to call `removeUI`.
|
||||
#'
|
||||
#' @seealso [insertUI()]
|
||||
#'
|
||||
#' @examples
|
||||
#' ## Only run this example in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' # Define UI
|
||||
#' ui <- fluidPage(
|
||||
#' actionButton("rmv", "Remove UI"),
|
||||
#' textInput("txt", "This is no longer useful")
|
||||
#' )
|
||||
#'
|
||||
#' # Server logic
|
||||
#' server <- function(input, output, session) {
|
||||
#' observeEvent(input$rmv, {
|
||||
#' removeUI(
|
||||
#' selector = "div:has(> #txt)"
|
||||
#' )
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' # Complete app with UI and server components
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#' @rdname insertUI
|
||||
#' @export
|
||||
removeUI <- function(selector,
|
||||
multiple = FALSE,
|
||||
|
||||
455
R/mock-session.R
Normal file
455
R/mock-session.R
Normal file
@@ -0,0 +1,455 @@
|
||||
# Promise helpers taken from:
|
||||
# https://github.com/rstudio/promises/blob/master/tests/testthat/common.R
|
||||
# Block until all pending later tasks have executed
|
||||
wait_for_it <- function() {
|
||||
while (!later::loop_empty()) {
|
||||
later::run_now(0.1)
|
||||
}
|
||||
}
|
||||
|
||||
# Block until the promise is resolved/rejected. If resolved, return the value.
|
||||
# If rejected, throw (yes throw, not return) the error.
|
||||
#' @importFrom promises %...!%
|
||||
#' @importFrom promises %...>%
|
||||
extract <- function(promise) {
|
||||
promise_value <- NULL
|
||||
error <- NULL
|
||||
promise %...>%
|
||||
(function(value) promise_value <<- value) %...!%
|
||||
(function(reason) error <<- reason)
|
||||
|
||||
wait_for_it()
|
||||
if (!is.null(error))
|
||||
stop(error)
|
||||
else
|
||||
promise_value
|
||||
}
|
||||
|
||||
# TODO: is there a way to get this behavior without exporting these functions? R6?
|
||||
# TODO: clientData is documented as a reactiveValues, which this is not. Is it possible that
|
||||
# users are currently assigning into clientData? That would not work as expected here.
|
||||
#' @noRd
|
||||
#' @export
|
||||
`$.mockclientdata` <- function(x, name) {
|
||||
if (name == "allowDataUriScheme") { return(TRUE) }
|
||||
if (name == "pixelratio") { return(1) }
|
||||
if (name == "url_protocol") { return("http:") }
|
||||
if (name == "url_hostname") { return("mocksession") }
|
||||
if (name == "url_port") { return(1234) }
|
||||
if (name == "url_pathname") { return("/mockpath") }
|
||||
if (name == "url_hash") { return("#mockhash") }
|
||||
if (name == "url_hash_initial") { return("#mockhash") }
|
||||
if (name == "url_search") { return("?mocksearch=1") }
|
||||
|
||||
clientRE <- "^output_(.+)_([^_]+)$"
|
||||
if(grepl(clientRE, name)) {
|
||||
# TODO: use proper regex group matching here instead of redundantly parsing
|
||||
el <- sub(clientRE, "\\1", name)
|
||||
att <- sub(clientRE, "\\2", name)
|
||||
|
||||
if (att == "width") {
|
||||
return(600)
|
||||
} else if (att == "height") {
|
||||
return(400)
|
||||
} else if (att == "hidden") {
|
||||
return(FALSE)
|
||||
}
|
||||
}
|
||||
warning("Unexpected clientdata attribute accessed: ", name)
|
||||
return(NULL)
|
||||
}
|
||||
|
||||
#' @noRd
|
||||
#' @export
|
||||
`[[.mockclientdata` <- `$.mockclientdata`
|
||||
|
||||
#' @noRd
|
||||
#' @export
|
||||
`[.mockclientdata` <- function(values, name) {
|
||||
stop("Single-bracket indexing of mockclientdata is not allowed.")
|
||||
}
|
||||
|
||||
#' @noRd
|
||||
mapNames <- function(func, ...) {
|
||||
vals <- list(...)
|
||||
names(vals) <- vapply(names(vals), func, character(1))
|
||||
vals
|
||||
}
|
||||
|
||||
#' Mock Shiny Session
|
||||
#'
|
||||
#' @description
|
||||
#' An R6 class suitable for testing that simulates the `session` parameter
|
||||
#' provided to Shiny server functions or modules.
|
||||
#'
|
||||
#' @include timer.R
|
||||
#' @export
|
||||
MockShinySession <- R6Class(
|
||||
'MockShinySession',
|
||||
portable = FALSE,
|
||||
public = list(
|
||||
#' @field env The environment associated with the session.
|
||||
env = NULL,
|
||||
#' @field returned The value returned by the module.
|
||||
returned = NULL,
|
||||
#' @field singletons Hardcoded as empty. Needed for rendering HTML (i.e. renderUI)
|
||||
singletons = character(0),
|
||||
#' @field clientData Mock client data that always returns a size for plots
|
||||
clientData = structure(list(), class="mockclientdata"),
|
||||
#' @description No-op
|
||||
#' @param logEntry Not used
|
||||
reactlog = function(logEntry){},
|
||||
#' @description No-op
|
||||
incrementBusyCount = function(){},
|
||||
#' @field output The shinyoutputs associated with the session
|
||||
output = NULL,
|
||||
#' @field input The reactive inputs associated with the session
|
||||
input = NULL,
|
||||
#' @field userData An environment initialized as empty.
|
||||
userData = NULL,
|
||||
#' @field progressStack A stack of progress objects
|
||||
progressStack = 'Stack',
|
||||
|
||||
#' @description Create a new MockShinySession
|
||||
initialize = function() {
|
||||
private$.input <- ReactiveValues$new(dedupe = FALSE, label = "input")
|
||||
private$flushCBs <- Callbacks$new()
|
||||
private$flushedCBs <- Callbacks$new()
|
||||
private$endedCBs <- Callbacks$new()
|
||||
private$timer <- MockableTimerCallbacks$new()
|
||||
self$progressStack <- Stack$new()
|
||||
|
||||
self$userData <- new.env(parent=emptyenv())
|
||||
|
||||
# create output
|
||||
out <- .createOutputWriter(self)
|
||||
class(out) <- "shinyoutput"
|
||||
self$output <- out
|
||||
|
||||
# Create a read-only copy of the inputs reactive.
|
||||
self$input <- .createReactiveValues(private$.input, readonly = TRUE)
|
||||
},
|
||||
#' @description Define a callback to be invoked before a reactive flush
|
||||
#' @param fun The function to invoke
|
||||
#' @param once If `TRUE`, will only run once. Otherwise, will run every time reactives are flushed.
|
||||
onFlush = function(fun, once=TRUE) {
|
||||
if (!isTRUE(once)) {
|
||||
return(private$flushCBs$register(fun))
|
||||
} else {
|
||||
dereg <- private$flushCBs$register(function() {
|
||||
dereg()
|
||||
fun()
|
||||
})
|
||||
return(dereg)
|
||||
}
|
||||
},
|
||||
#' @description Define a callback to be invoked after a reactive flush
|
||||
#' @param fun The function to invoke
|
||||
#' @param once If `TRUE`, will only run once. Otherwise, will run every time reactives are flushed.
|
||||
onFlushed = function(fun, once=TRUE) {
|
||||
if (!isTRUE(once)) {
|
||||
return(private$flushedCBs$register(fun))
|
||||
} else {
|
||||
dereg <- private$flushedCBs$register(function() {
|
||||
dereg()
|
||||
fun()
|
||||
})
|
||||
return(dereg)
|
||||
}
|
||||
},
|
||||
#' @description Define a callback to be invoked when the session ends
|
||||
#' @param sessionEndedCallback The callback to invoke when the session has ended.
|
||||
onEnded = function(sessionEndedCallback) {
|
||||
private$endedCBs$register(sessionEndedCallback)
|
||||
},
|
||||
|
||||
#' @description Returns `FALSE` if the session has not yet been closed
|
||||
isEnded = function(){ private$closed },
|
||||
#' @description Returns `FALSE` if the session has not yet been closed
|
||||
isClosed = function(){ private$closed },
|
||||
#' @description Closes the session
|
||||
close = function(){ private$closed <- TRUE },
|
||||
|
||||
#FIXME: this is wrong. Will need to be more complex.
|
||||
#' @description Unsophisticated mock implementation that merely invokes
|
||||
#' the given callback immediately.
|
||||
#' @param callback The callback ato be invoked.
|
||||
cycleStartAction = function(callback){ callback() },
|
||||
|
||||
#' @description Base64-encode the given file. Needed for image rendering.
|
||||
#' @param name Not used
|
||||
#' @param file The file to be encoded
|
||||
#' @param contentType The content type of the base64-encoded string
|
||||
fileUrl = function(name, file, contentType='application/octet-stream') {
|
||||
bytes <- file.info(file)$size
|
||||
if (is.na(bytes))
|
||||
return(NULL)
|
||||
|
||||
fileData <- readBin(file, 'raw', n=bytes)
|
||||
b64 <- rawToBase64(fileData)
|
||||
return(paste('data:', contentType, ';base64,', b64, sep=''))
|
||||
},
|
||||
|
||||
#' @description Sets reactive values associated with the `session$inputs` object
|
||||
#' and flushes the reactives.
|
||||
#' @param ... The inputs to set.
|
||||
#' @examples
|
||||
#' s <- MockShinySession$new()
|
||||
#' s$setInputs(x=1, y=2)
|
||||
setInputs = function(...) {
|
||||
vals <- list(...)
|
||||
mapply(names(vals), vals, FUN = function(name, value) {
|
||||
private$.input$set(name, value)
|
||||
})
|
||||
private$flush()
|
||||
},
|
||||
|
||||
#' @description An internal method which shouldn't be used by others.
|
||||
#' @param millis The number of milliseconds on which to schedule a callback
|
||||
#' @param callback The function to schedule
|
||||
.scheduleTask = function(millis, callback) {
|
||||
id <- private$timer$schedule(millis, callback)
|
||||
|
||||
# Return a deregistration callback
|
||||
function() {
|
||||
invisible(private$timer$unschedule(id))
|
||||
}
|
||||
},
|
||||
|
||||
#' @description Simulate the passing of time by the given number of milliseconds.
|
||||
#' @param millis The number of milliseconds to advance time.
|
||||
elapse = function(millis) {
|
||||
msLeft <- millis
|
||||
|
||||
while (msLeft > 0){
|
||||
t <- private$timer$timeToNextEvent()
|
||||
|
||||
if (is.infinite(t) || t <= 0 || msLeft < t){
|
||||
# Either there's no good upcoming event or we can't make it to it in the allotted time.
|
||||
break
|
||||
}
|
||||
msLeft <- msLeft - t
|
||||
private$timer$elapse(t)
|
||||
|
||||
# timerCallbacks must run before flushReact.
|
||||
private$timer$executeElapsed()
|
||||
private$flush()
|
||||
}
|
||||
|
||||
private$timer$elapse(msLeft)
|
||||
|
||||
# Run again in case our callbacks resulted in a scheduled
|
||||
# function that needs executing.
|
||||
private$timer$executeElapsed()
|
||||
private$flush()
|
||||
},
|
||||
|
||||
#' @description An internal method which shouldn't be used by others.
|
||||
.now = function() {
|
||||
# Returns elapsed time in milliseconds
|
||||
private$timer$getElapsed()
|
||||
},
|
||||
|
||||
#' @description An internal method which shouldn't be used by others.
|
||||
#' @param name The name of the output
|
||||
#' @param func The render definition
|
||||
#' @param label Not used
|
||||
defineOutput = function(name, func, label) {
|
||||
force(name)
|
||||
|
||||
if (!is.null(private$outs[[name]]$obs)) {
|
||||
private$outs[[name]]$obs$destroy()
|
||||
}
|
||||
|
||||
if (is.null(func)) func <- missingOutput
|
||||
|
||||
if (!is.function(func))
|
||||
stop(paste("Unexpected", class(func), "output for", name))
|
||||
|
||||
obs <- observe({
|
||||
# We could just stash the promise, but we get an "unhandled promise error". This bypasses
|
||||
prom <- NULL
|
||||
tryCatch({
|
||||
v <- func(self, name)
|
||||
if (!promises::is.promise(v)){
|
||||
# Make our sync value into a promise
|
||||
prom <- promises::promise(function(resolve, reject){ resolve(v) })
|
||||
} else {
|
||||
prom <- v
|
||||
}
|
||||
}, error=function(e){
|
||||
# Error running value()
|
||||
prom <<- promises::promise(function(resolve, reject){ reject(e) })
|
||||
})
|
||||
|
||||
private$outs[[name]]$promise <- hybrid_chain(
|
||||
prom,
|
||||
function(v){
|
||||
list(val = v, err = NULL)
|
||||
}, catch=function(e){
|
||||
list(val = NULL, err = e)
|
||||
})
|
||||
})
|
||||
private$outs[[name]] <- list(obs = obs, func = func, promise = NULL)
|
||||
},
|
||||
|
||||
#' @description An internal method which shouldn't be used by others.
|
||||
#' @param name The name of the output
|
||||
getOutput = function(name) {
|
||||
# Unlike the real outputs, we're going to return the last value rather than the unevaluated function
|
||||
if (is.null(private$outs[[name]])) {
|
||||
stop("The test referenced an output that hasn't been defined yet: output$", name)
|
||||
}
|
||||
|
||||
if (is.null(private$outs[[name]]$promise)) {
|
||||
# Means the output was defined but the observer hasn't had a chance to run
|
||||
# yet. Run flushReact() now to force the observer to run.
|
||||
flushReact()
|
||||
|
||||
if (is.null(private$outs[[name]]$promise)) {
|
||||
stop("output$", name, " encountered an unexpected error resolving its promise")
|
||||
}
|
||||
}
|
||||
|
||||
# Make promise return
|
||||
v <- extract(private$outs[[name]]$promise)
|
||||
if (!is.null(v$err)){
|
||||
stop(v$err)
|
||||
} else {
|
||||
v$val
|
||||
}
|
||||
},
|
||||
|
||||
#' @description No-op
|
||||
#' @param name Not used
|
||||
#' @param data Not used
|
||||
#' @param filterFunc Not used
|
||||
registerDataObj = function(name, data, filterFunc) {},
|
||||
#' @description No-op
|
||||
#' @param value Not used
|
||||
allowReconnect = function(value) {},
|
||||
#' @description No-op
|
||||
reload = function() {},
|
||||
#' @description No-op
|
||||
#' @param brushId Not used
|
||||
resetBrush = function(brushId) {
|
||||
warning("session$brush isn't meaningfully mocked on the MockShinySession")
|
||||
},
|
||||
#' @description No-op
|
||||
#' @param type Not used
|
||||
#' @param message Not used
|
||||
sendCustomMessage = function(type, message) {},
|
||||
#' @description No-op
|
||||
#' @param type Not used
|
||||
#' @param message Not used
|
||||
sendBinaryMessage = function(type, message) {},
|
||||
#' @description No-op
|
||||
#' @param inputId Not used
|
||||
#' @param message Not used
|
||||
sendInputMessage = function(inputId, message) {},
|
||||
#' @description No-op
|
||||
#' @param names Not used
|
||||
setBookmarkExclude = function(names) {
|
||||
warning("Bookmarking isn't meaningfully mocked in MockShinySession")
|
||||
},
|
||||
#' @description No-op
|
||||
getBookmarkExclude = function() {
|
||||
warning("Bookmarking isn't meaningfully mocked in MockShinySession")
|
||||
},
|
||||
#' @description No-op
|
||||
#' @param fun Not used
|
||||
onBookmark = function(fun) {},
|
||||
#' @description No-op
|
||||
#' @param fun Not used
|
||||
onBookmarked = function(fun) {},
|
||||
#' @description No-op
|
||||
doBookmark = function() {
|
||||
warning("Bookmarking isn't meaningfully mocked in MockShinySession")
|
||||
},
|
||||
#' @description No-op
|
||||
#' @param fun Not used
|
||||
onRestore = function(fun) {},
|
||||
#' @description No-op
|
||||
#' @param fun Not used
|
||||
onRestored = function(fun) {},
|
||||
#' @description No-op
|
||||
exportTestValues = function() {},
|
||||
#' @description No-op
|
||||
#' @param input Not used
|
||||
#' @param output Not used
|
||||
#' @param export Not used
|
||||
#' @param format Not used
|
||||
getTestSnapshotUrl = function(input=TRUE, output=TRUE, export=TRUE, format="json") {},
|
||||
#' @description Returns the given id prefixed by this namespace's id.
|
||||
#' @param id The id to modify.
|
||||
ns = function(id) {
|
||||
NS(private$nsPrefix, id)
|
||||
},
|
||||
#' @description Trigger a reactive flush right now.
|
||||
flushReact = function(){
|
||||
private$flush()
|
||||
},
|
||||
#' @description Create and return a namespace-specific session proxy.
|
||||
#' @param namespace Character vector indicating a namespace.
|
||||
makeScope = function(namespace) {
|
||||
ns <- NS(namespace)
|
||||
createSessionProxy(
|
||||
self,
|
||||
input = .createReactiveValues(private$.input, readonly = TRUE, ns = ns),
|
||||
output = structure(.createOutputWriter(self, ns = ns), class = "shinyoutput"),
|
||||
makeScope = function(namespace) self$makeScope(ns(namespace)),
|
||||
ns = function(namespace) ns(namespace),
|
||||
setInputs = function(...) do.call(self$setInputs, mapNames(ns, ...))
|
||||
)
|
||||
},
|
||||
#' @description Set the environment associated with a testServer() call.
|
||||
#' @param env The environment to retain.
|
||||
setEnv = function(env) {
|
||||
self$env <- env
|
||||
},
|
||||
#' @description Set the value returned by the module call and proactively flush.
|
||||
#' @param value The value returned from the module
|
||||
setReturned = function(value) {
|
||||
self$returned <- value
|
||||
private$flush()
|
||||
value
|
||||
},
|
||||
#' @description Get the value returned by the module call.
|
||||
getReturned = function() self$returned,
|
||||
#' @description Return a distinct character identifier for use as a proxy
|
||||
#' namespace.
|
||||
genId = function() {
|
||||
private$idCounter <- private$idCounter + 1
|
||||
paste0("proxy", private$idCounter)
|
||||
}
|
||||
),
|
||||
private = list(
|
||||
.input = NULL,
|
||||
flushCBs = NULL,
|
||||
flushedCBs = NULL,
|
||||
endedCBs = NULL,
|
||||
timer = NULL,
|
||||
closed = FALSE,
|
||||
outs = list(),
|
||||
nsPrefix = "mock-session",
|
||||
idCounter = 0,
|
||||
|
||||
flush = function(){
|
||||
isolate(private$flushCBs$invoke(..stacktraceon = TRUE))
|
||||
shiny:::flushReact() # namespace to avoid calling our own method
|
||||
isolate(private$flushedCBs$invoke(..stacktraceon = TRUE))
|
||||
later::run_now()
|
||||
}
|
||||
),
|
||||
active = list(
|
||||
#' @field request An empty environment where the request should be. The request isn't meaningfully mocked currently.
|
||||
request = function(value) {
|
||||
if (!missing(value)){
|
||||
stop("session$request can't be assigned to")
|
||||
}
|
||||
warning("session$request doesn't currently simulate a realistic request on MockShinySession")
|
||||
new.env(parent=emptyenv())
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
104
R/modules.R
104
R/modules.R
@@ -36,25 +36,117 @@ createSessionProxy <- function(parentSession, ...) {
|
||||
|
||||
`[[<-.session_proxy` <- `$<-.session_proxy`
|
||||
|
||||
|
||||
#' Invoke a Shiny module
|
||||
#' Shiny modules
|
||||
#'
|
||||
#' Shiny's module feature lets you break complicated UI and server logic into
|
||||
#' smaller, self-contained pieces. Compared to large monolithic Shiny apps,
|
||||
#' modules are easier to reuse and easier to reason about. See the article at
|
||||
#' <http://shiny.rstudio.com/articles/modules.html> to learn more.
|
||||
#'
|
||||
#' @param module A Shiny module server function
|
||||
#' Starting in Shiny 1.5.0, we recommend using `moduleServer` instead of
|
||||
#' `callModule`, because the syntax is a little easier to understand, and
|
||||
#' modules created with `moduleServer` can be tested with [`testServer()`].
|
||||
#'
|
||||
#' @param module A Shiny module server function.
|
||||
#' @param id An ID string that corresponds with the ID used to call the module's
|
||||
#' UI function
|
||||
#' @param ... Additional parameters to pass to module server function
|
||||
#' UI function.
|
||||
#' @param ... For `callModule`, additional parameters to pass to module server
|
||||
#' function.
|
||||
#' @param session Session from which to make a child scope (the default should
|
||||
#' almost always be used)
|
||||
#' almost always be used).
|
||||
#'
|
||||
#' @return The return value, if any, from executing the module server function
|
||||
#' @seealso <http://shiny.rstudio.com/articles/modules.html>
|
||||
#'
|
||||
#' @examples
|
||||
#' # Define the UI for a module
|
||||
#' counterUI <- function(id, label = "Counter") {
|
||||
#' ns <- NS(id)
|
||||
#' tagList(
|
||||
#' actionButton(ns("button"), label = label),
|
||||
#' verbatimTextOutput(ns("out"))
|
||||
#' )
|
||||
#' }
|
||||
#'
|
||||
#' # Define the server logic for a module
|
||||
#' counterServer <- function(id) {
|
||||
#' moduleServer(
|
||||
#' id,
|
||||
#' function(input, output, session) {
|
||||
#' count <- reactiveVal(0)
|
||||
#' observeEvent(input$button, {
|
||||
#' count(count() + 1)
|
||||
#' })
|
||||
#' output$out <- renderText({
|
||||
#' count()
|
||||
#' })
|
||||
#' count
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#'
|
||||
#' # Use the module in an app
|
||||
#' ui <- fluidPage(
|
||||
#' counterUI("counter1", "Counter #1"),
|
||||
#' counterUI("counter2", "Counter #2")
|
||||
#' )
|
||||
#' server <- function(input, output, session) {
|
||||
#' counterServer("counter1")
|
||||
#' counterServer("counter2")
|
||||
#' }
|
||||
#' if (interactive()) {
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#'
|
||||
#'
|
||||
#' # If you want to pass extra parameters to the module's server logic, you can
|
||||
#' # add them to your function. In this case `prefix` is text that will be
|
||||
#' # printed before the count.
|
||||
#' counterServer2 <- function(id, prefix = NULL) {
|
||||
#' moduleServer(
|
||||
#' id,
|
||||
#' function(input, output, session) {
|
||||
#' count <- reactiveVal(0)
|
||||
#' observeEvent(input$button, {
|
||||
#' count(count() + 1)
|
||||
#' })
|
||||
#' output$out <- renderText({
|
||||
#' paste0(prefix, count())
|
||||
#' })
|
||||
#' count
|
||||
#' }
|
||||
#' )
|
||||
#' }
|
||||
#'
|
||||
#' ui <- fluidPage(
|
||||
#' counterUI("counter", "Counter"),
|
||||
#' )
|
||||
#' server <- function(input, output, session) {
|
||||
#' counterServer2("counter", "The current count is: ")
|
||||
#' }
|
||||
#' if (interactive()) {
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @export
|
||||
moduleServer <- function(id, module, session = getDefaultReactiveDomain()) {
|
||||
if (inherits(session, "MockShinySession")) {
|
||||
body(module) <- rlang::expr({
|
||||
session$setEnv(base::environment())
|
||||
session$setReturned({ !!!body(module) })
|
||||
})
|
||||
}
|
||||
callModule(module, id, session = session)
|
||||
}
|
||||
|
||||
|
||||
#' @rdname moduleServer
|
||||
#' @export
|
||||
callModule <- function(module, id, ..., session = getDefaultReactiveDomain()) {
|
||||
if (!inherits(session, c("ShinySession", "session_proxy", "MockShinySession"))) {
|
||||
stop("session must be a ShinySession or session_proxy object.")
|
||||
}
|
||||
childScope <- session$makeScope(id)
|
||||
|
||||
withReactiveDomain(childScope, {
|
||||
|
||||
74
R/progress.R
74
R/progress.R
@@ -20,50 +20,12 @@
|
||||
#' [`shinyOptions(progress.style="old")`][shinyOptions] just once, inside the server
|
||||
#' function.
|
||||
#'
|
||||
#' **Methods**
|
||||
#' \describe{
|
||||
#' \item{`initialize(session, min = 0, max = 1)`}{
|
||||
#' Creates a new progress panel (but does not display it).
|
||||
#' }
|
||||
#' \item{`set(value = NULL, message = NULL, detail = NULL)`}{
|
||||
#' Updates the progress panel. When called the first time, the
|
||||
#' progress panel is displayed.
|
||||
#' }
|
||||
#' \item{`inc(amount = 0.1, message = NULL, detail = NULL)`}{
|
||||
#' Like `set`, this updates the progress panel. The difference is
|
||||
#' that `inc` increases the progress bar by `amount`, instead
|
||||
#' of setting it to a specific value.
|
||||
#' }
|
||||
#' \item{`close()`}{
|
||||
#' Removes the progress panel. Future calls to `set` and
|
||||
#' `close` will be ignored.
|
||||
#' }
|
||||
#' }
|
||||
#'
|
||||
#' @param session The Shiny session object, as provided by
|
||||
#' `shinyServer` to the server function.
|
||||
#' @param min The value that represents the starting point of the
|
||||
#' progress bar. Must be less than `max`.
|
||||
#' @param max The value that represents the end of the progress bar.
|
||||
#' Must be greater than `min`.
|
||||
#' @param message A single-element character vector; the message to be
|
||||
#' displayed to the user, or `NULL` to hide the current message
|
||||
#' (if any).
|
||||
#' @param detail A single-element character vector; the detail message
|
||||
#' to be displayed to the user, or `NULL` to hide the current
|
||||
#' detail message (if any). The detail message will be shown with a
|
||||
#' de-emphasized appearance relative to `message`.
|
||||
#' @param value A numeric value at which to set
|
||||
#' the progress bar, relative to `min` and `max`.
|
||||
#' @param style Progress display style. If `"notification"` (the default),
|
||||
#' the progress indicator will show using Shiny's notification API. If
|
||||
#' `"old"`, use the same HTML and CSS used in Shiny 0.13.2 and below
|
||||
#' (this is for backward-compatibility).
|
||||
#' @param amount Single-element numeric vector; the value at which to set
|
||||
#' the progress bar, relative to `min` and `max`.
|
||||
#' `NULL` hides the progress bar, if it is currently visible.
|
||||
#' @param amount For the `inc()` method, a numeric value to increment the
|
||||
#' progress bar.
|
||||
#' displayed to the user, or `NULL` to hide the current message (if any).
|
||||
#' @param detail A single-element character vector; the detail message to be
|
||||
#' displayed to the user, or `NULL` to hide the current detail message (if
|
||||
#' any). The detail message will be shown with a de-emphasized appearance
|
||||
#' relative to `message`.
|
||||
#'
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
@@ -99,6 +61,17 @@ Progress <- R6Class(
|
||||
'Progress',
|
||||
public = list(
|
||||
|
||||
#' @description Creates a new progress panel (but does not display it).
|
||||
#' @param session The Shiny session object, as provided by `shinyServer` to
|
||||
#' the server function.
|
||||
#' @param min The value that represents the starting point of the progress
|
||||
#' bar. Must be less than `max`.
|
||||
#' @param max The value that represents the end of the progress bar. Must be
|
||||
#' greater than `min`.
|
||||
#' @param style Progress display style. If `"notification"` (the default),
|
||||
#' the progress indicator will show using Shiny's notification API. If
|
||||
#' `"old"`, use the same HTML and CSS used in Shiny 0.13.2 and below (this
|
||||
#' is for backward-compatibility).
|
||||
initialize = function(session = getDefaultReactiveDomain(),
|
||||
min = 0, max = 1,
|
||||
style = getShinyOption("progress.style", default = "notification"))
|
||||
@@ -117,6 +90,11 @@ Progress <- R6Class(
|
||||
session$sendProgress('open', list(id = private$id, style = private$style))
|
||||
},
|
||||
|
||||
#' @description Updates the progress panel. When called the first time, the
|
||||
#' progress panel is displayed.
|
||||
#' @param value Single-element numeric vector; the value at which to set the
|
||||
#' progress bar, relative to `min` and `max`. `NULL` hides the progress
|
||||
#' bar, if it is currently visible.
|
||||
set = function(value = NULL, message = NULL, detail = NULL) {
|
||||
if (private$closed) {
|
||||
warning("Attempting to set progress, but progress already closed.")
|
||||
@@ -143,6 +121,11 @@ Progress <- R6Class(
|
||||
private$session$sendProgress('update', data)
|
||||
},
|
||||
|
||||
#' @description Like `set`, this updates the progress panel. The difference
|
||||
#' is that `inc` increases the progress bar by `amount`, instead of
|
||||
#' setting it to a specific value.
|
||||
#' @param amount For the `inc()` method, a numeric value to increment the
|
||||
#' progress bar.
|
||||
inc = function(amount = 0.1, message = NULL, detail = NULL) {
|
||||
if (is.null(private$value))
|
||||
private$value <- private$min
|
||||
@@ -151,12 +134,17 @@ Progress <- R6Class(
|
||||
self$set(value, message, detail)
|
||||
},
|
||||
|
||||
#' @description Returns the minimum value.
|
||||
getMin = function() private$min,
|
||||
|
||||
#' @description Returns the maximum value.
|
||||
getMax = function() private$max,
|
||||
|
||||
#' @description Returns the current value.
|
||||
getValue = function() private$value,
|
||||
|
||||
#' @description Removes the progress panel. Future calls to `set` and
|
||||
#' `close` will be ignored.
|
||||
close = function() {
|
||||
if (private$closed) {
|
||||
warning("Attempting to close progress, but progress already closed.")
|
||||
|
||||
@@ -222,7 +222,7 @@ reactiveVal <- function(value = NULL, label = NULL) {
|
||||
rv$set(x)
|
||||
}
|
||||
},
|
||||
class = c("reactiveVal", "reactive"),
|
||||
class = c("reactiveVal", "reactive", "function"),
|
||||
label = label,
|
||||
.impl = rv
|
||||
)
|
||||
@@ -969,7 +969,7 @@ reactive <- function(x, env = parent.frame(), quoted = FALSE, label = NULL,
|
||||
if (length(srcref) >= 2) attr(label, "srcref") <- srcref[[2]]
|
||||
attr(label, "srcfile") <- srcFileOfRef(srcref[[1]])
|
||||
o <- Observable$new(fun, label, domain, ..stacktraceon = ..stacktraceon)
|
||||
structure(o$getValue, observable = o, class = c("reactiveExpr", "reactive"))
|
||||
structure(o$getValue, observable = o, class = c("reactiveExpr", "reactive", "function"))
|
||||
}
|
||||
|
||||
# Given the srcref to a reactive expression, attempts to figure out what the
|
||||
@@ -2362,20 +2362,24 @@ debounce <- function(r, millis, priority = 100, domain = getDefaultReactiveDomai
|
||||
when = NULL # the deadline for the timer to fire; NULL if not scheduled
|
||||
)
|
||||
|
||||
# Responsible for tracking when f() changes.
|
||||
# Responsible for tracking when r() changes.
|
||||
firstRun <- TRUE
|
||||
observe({
|
||||
r()
|
||||
|
||||
if (firstRun) {
|
||||
# During the first run we don't want to set v$when, as this will kick off
|
||||
# the timer. We only want to do that when we see r() change.
|
||||
firstRun <<- FALSE
|
||||
|
||||
# Ensure r() is called only after setting firstRun to FALSE since r()
|
||||
# may throw an error
|
||||
r()
|
||||
return()
|
||||
}
|
||||
# This ensures r() is still tracked after firstRun
|
||||
r()
|
||||
|
||||
# The value (or possibly millis) changed. Start or reset the timer.
|
||||
v$when <- getTime(domain) + millis()/1000
|
||||
v$when <- getDomainTimeMs(domain) + millis()
|
||||
}, label = "debounce tracker", domain = domain, priority = priority)
|
||||
|
||||
# This observer is the timer. It rests until v$when elapses, then touches
|
||||
@@ -2384,13 +2388,13 @@ debounce <- function(r, millis, priority = 100, domain = getDefaultReactiveDomai
|
||||
if (is.null(v$when))
|
||||
return()
|
||||
|
||||
now <- getTime(domain)
|
||||
now <- getDomainTimeMs(domain)
|
||||
if (now >= v$when) {
|
||||
# Mod by 999999999 to get predictable overflow behavior
|
||||
v$trigger <- isolate(v$trigger %OR% 0) %% 999999999 + 1
|
||||
v$when <- NULL
|
||||
} else {
|
||||
invalidateLater((v$when - now) * 1000)
|
||||
invalidateLater(v$when - now)
|
||||
}
|
||||
}, label = "debounce timer", domain = domain, priority = priority)
|
||||
|
||||
@@ -2435,12 +2439,12 @@ throttle <- function(r, millis, priority = 100, domain = getDefaultReactiveDomai
|
||||
if (is.null(v$lastTriggeredAt)) {
|
||||
0
|
||||
} else {
|
||||
max(0, (v$lastTriggeredAt + millis()/1000) - getTime(domain)) * 1000
|
||||
max(0, v$lastTriggeredAt + millis() - getDomainTimeMs(domain))
|
||||
}
|
||||
}
|
||||
|
||||
trigger <- function() {
|
||||
v$lastTriggeredAt <- getTime(domain)
|
||||
v$lastTriggeredAt <- getDomainTimeMs(domain)
|
||||
# Mod by 999999999 to get predictable overflow behavior
|
||||
v$trigger <- isolate(v$trigger) %% 999999999 + 1
|
||||
v$pending <- FALSE
|
||||
|
||||
195
R/reexports.R
Normal file
195
R/reexports.R
Normal file
@@ -0,0 +1,195 @@
|
||||
####
|
||||
# Generated by `./tools/updateReexports.R`: do not edit by hand
|
||||
# Please call `source('tools/updateReexports.R') from the root folder to update`
|
||||
####
|
||||
|
||||
|
||||
# fastmap key_missing.Rd -------------------------------------------------------
|
||||
|
||||
#' @importFrom fastmap key_missing
|
||||
#' @export
|
||||
fastmap::key_missing
|
||||
|
||||
#' @importFrom fastmap is.key_missing
|
||||
#' @export
|
||||
fastmap::is.key_missing
|
||||
|
||||
|
||||
|
||||
# htmltools builder.Rd ---------------------------------------------------------
|
||||
|
||||
#' @importFrom htmltools tags
|
||||
#' @export
|
||||
htmltools::tags
|
||||
|
||||
#' @importFrom htmltools p
|
||||
#' @export
|
||||
htmltools::p
|
||||
|
||||
#' @importFrom htmltools h1
|
||||
#' @export
|
||||
htmltools::h1
|
||||
|
||||
#' @importFrom htmltools h2
|
||||
#' @export
|
||||
htmltools::h2
|
||||
|
||||
#' @importFrom htmltools h3
|
||||
#' @export
|
||||
htmltools::h3
|
||||
|
||||
#' @importFrom htmltools h4
|
||||
#' @export
|
||||
htmltools::h4
|
||||
|
||||
#' @importFrom htmltools h5
|
||||
#' @export
|
||||
htmltools::h5
|
||||
|
||||
#' @importFrom htmltools h6
|
||||
#' @export
|
||||
htmltools::h6
|
||||
|
||||
#' @importFrom htmltools a
|
||||
#' @export
|
||||
htmltools::a
|
||||
|
||||
#' @importFrom htmltools br
|
||||
#' @export
|
||||
htmltools::br
|
||||
|
||||
#' @importFrom htmltools div
|
||||
#' @export
|
||||
htmltools::div
|
||||
|
||||
#' @importFrom htmltools span
|
||||
#' @export
|
||||
htmltools::span
|
||||
|
||||
#' @importFrom htmltools pre
|
||||
#' @export
|
||||
htmltools::pre
|
||||
|
||||
#' @importFrom htmltools code
|
||||
#' @export
|
||||
htmltools::code
|
||||
|
||||
#' @importFrom htmltools img
|
||||
#' @export
|
||||
htmltools::img
|
||||
|
||||
#' @importFrom htmltools strong
|
||||
#' @export
|
||||
htmltools::strong
|
||||
|
||||
#' @importFrom htmltools em
|
||||
#' @export
|
||||
htmltools::em
|
||||
|
||||
#' @importFrom htmltools hr
|
||||
#' @export
|
||||
htmltools::hr
|
||||
|
||||
|
||||
# htmltools tag.Rd -------------------------------------------------------------
|
||||
|
||||
#' @importFrom htmltools tag
|
||||
#' @export
|
||||
htmltools::tag
|
||||
|
||||
#' @importFrom htmltools tagList
|
||||
#' @export
|
||||
htmltools::tagList
|
||||
|
||||
#' @importFrom htmltools tagAppendAttributes
|
||||
#' @export
|
||||
htmltools::tagAppendAttributes
|
||||
|
||||
#' @importFrom htmltools tagHasAttribute
|
||||
#' @export
|
||||
htmltools::tagHasAttribute
|
||||
|
||||
#' @importFrom htmltools tagGetAttribute
|
||||
#' @export
|
||||
htmltools::tagGetAttribute
|
||||
|
||||
#' @importFrom htmltools tagAppendChild
|
||||
#' @export
|
||||
htmltools::tagAppendChild
|
||||
|
||||
#' @importFrom htmltools tagAppendChildren
|
||||
#' @export
|
||||
htmltools::tagAppendChildren
|
||||
|
||||
#' @importFrom htmltools tagSetChildren
|
||||
#' @export
|
||||
htmltools::tagSetChildren
|
||||
|
||||
|
||||
# htmltools HTML.Rd ------------------------------------------------------------
|
||||
|
||||
#' @importFrom htmltools HTML
|
||||
#' @export
|
||||
htmltools::HTML
|
||||
|
||||
|
||||
# htmltools include.Rd ---------------------------------------------------------
|
||||
|
||||
#' @importFrom htmltools includeHTML
|
||||
#' @export
|
||||
htmltools::includeHTML
|
||||
|
||||
#' @importFrom htmltools includeText
|
||||
#' @export
|
||||
htmltools::includeText
|
||||
|
||||
#' @importFrom htmltools includeMarkdown
|
||||
#' @export
|
||||
htmltools::includeMarkdown
|
||||
|
||||
#' @importFrom htmltools includeCSS
|
||||
#' @export
|
||||
htmltools::includeCSS
|
||||
|
||||
#' @importFrom htmltools includeScript
|
||||
#' @export
|
||||
htmltools::includeScript
|
||||
|
||||
|
||||
# htmltools singleton.Rd -------------------------------------------------------
|
||||
|
||||
#' @importFrom htmltools singleton
|
||||
#' @export
|
||||
htmltools::singleton
|
||||
|
||||
#' @importFrom htmltools is.singleton
|
||||
#' @export
|
||||
htmltools::is.singleton
|
||||
|
||||
|
||||
# htmltools validateCssUnit.Rd -------------------------------------------------
|
||||
|
||||
#' @importFrom htmltools validateCssUnit
|
||||
#' @export
|
||||
htmltools::validateCssUnit
|
||||
|
||||
|
||||
# htmltools htmlTemplate.Rd ----------------------------------------------------
|
||||
|
||||
#' @importFrom htmltools htmlTemplate
|
||||
#' @export
|
||||
htmltools::htmlTemplate
|
||||
|
||||
|
||||
# htmltools suppressDependencies.Rd --------------------------------------------
|
||||
|
||||
#' @importFrom htmltools suppressDependencies
|
||||
#' @export
|
||||
htmltools::suppressDependencies
|
||||
|
||||
|
||||
# htmltools withTags.Rd --------------------------------------------------------
|
||||
|
||||
#' @importFrom htmltools withTags
|
||||
#' @export
|
||||
htmltools::withTags
|
||||
@@ -889,6 +889,14 @@ find_panel_info_non_api <- function(b, ggplot_format) {
|
||||
})
|
||||
}
|
||||
|
||||
# Use public API for getting the unit's type (grid::unitType(), added in R 4.0)
|
||||
# https://github.com/wch/r-source/blob/f9b8a42/src/library/grid/R/unit.R#L179
|
||||
getUnitType <- function(u) {
|
||||
tryCatch(
|
||||
get("unitType", envir = asNamespace("grid"))(u),
|
||||
error = function(e) attr(u, "unit", exact = TRUE)
|
||||
)
|
||||
}
|
||||
|
||||
# Given a gtable object, return the x and y ranges (in pixel dimensions)
|
||||
find_panel_ranges <- function(g, res) {
|
||||
@@ -904,11 +912,11 @@ find_panel_ranges <- function(g, res) {
|
||||
if (inherits(x, "unit.list")) {
|
||||
# For ggplot2 <= 1.0.1
|
||||
vapply(x, FUN.VALUE = logical(1), function(u) {
|
||||
isTRUE(attr(u, "unit", exact = TRUE) == "null")
|
||||
isTRUE(getUnitType(u) == "null")
|
||||
})
|
||||
} else {
|
||||
# For later versions of ggplot2
|
||||
attr(x, "unit", exact = TRUE) == "null"
|
||||
getUnitType(x) == "null"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -948,7 +956,11 @@ find_panel_ranges <- function(g, res) {
|
||||
|
||||
# The plotting panels all are 'null' units.
|
||||
null_sizes <- rep(NA_real_, length(rel_sizes))
|
||||
null_sizes[null_idx] <- as.numeric(rel_sizes[null_idx])
|
||||
# Workaround for `[.unit` forbidding zero-length subsets
|
||||
# https://github.com/wch/r-source/blob/f9b8a42/src/library/grid/R/unit.R#L448-L450
|
||||
if (length(null_idx)) {
|
||||
null_sizes[null_idx] <- as.numeric(rel_sizes[null_idx])
|
||||
}
|
||||
|
||||
# Total size allocated for panels is the total image size minus absolute
|
||||
# (non-panel) elements.
|
||||
|
||||
@@ -1230,5 +1230,5 @@ inShinyServer <- function() {
|
||||
# This check was moved out of the main function body because of an issue with
|
||||
# the RStudio debugger. (#1474)
|
||||
isEmptyMessage <- function(msg) {
|
||||
identical(charToRaw("\003\xe9"), msg)
|
||||
identical(as.raw(c(0x03, 0xe9)), msg)
|
||||
}
|
||||
|
||||
@@ -35,81 +35,81 @@ getShinyOption <- function(name, default = NULL) {
|
||||
#' `shinyOptions()`.
|
||||
#'
|
||||
#' \describe{
|
||||
#' \item{shiny.autoreload (defaults to `FALSE`)}{If `TRUE` when a Shiny app is launched, the
|
||||
#' app directory will be continually monitored for changes to files that
|
||||
#' have the extensions: r, htm, html, js, css, png, jpg, jpeg, gif. If any
|
||||
#' changes are detected, all connected Shiny sessions are reloaded. This
|
||||
#' allows for fast feedback loops when tweaking Shiny UI.
|
||||
#' \item{shiny.autoreload (defaults to `FALSE`)}{If `TRUE` when a Shiny app is launched, the
|
||||
#' app directory will be continually monitored for changes to files that
|
||||
#' have the extensions: r, htm, html, js, css, png, jpg, jpeg, gif. If any
|
||||
#' changes are detected, all connected Shiny sessions are reloaded. This
|
||||
#' allows for fast feedback loops when tweaking Shiny UI.
|
||||
#'
|
||||
#' Since monitoring for changes is expensive (we simply poll for last
|
||||
#' modified times), this feature is intended only for development.
|
||||
#' Since monitoring for changes is expensive (we simply poll for last
|
||||
#' modified times), this feature is intended only for development.
|
||||
#'
|
||||
#' You can customize the file patterns Shiny will monitor by setting the
|
||||
#' shiny.autoreload.pattern option. For example, to monitor only ui.R:
|
||||
#' `options(shiny.autoreload.pattern = glob2rx("ui.R"))`
|
||||
#' You can customize the file patterns Shiny will monitor by setting the
|
||||
#' shiny.autoreload.pattern option. For example, to monitor only ui.R:
|
||||
#' `options(shiny.autoreload.pattern = glob2rx("ui.R"))`
|
||||
#'
|
||||
#' The default polling interval is 500 milliseconds. You can change this
|
||||
#' by setting e.g. `options(shiny.autoreload.interval = 2000)` (every
|
||||
#' two seconds).}
|
||||
#' \item{shiny.deprecation.messages (defaults to `TRUE`)}{This controls whether messages for
|
||||
#' deprecated functions in Shiny will be printed. See
|
||||
#' [shinyDeprecated()] for more information.}
|
||||
#' \item{shiny.error (defaults to `NULL`)}{This can be a function which is called when an error
|
||||
#' occurs. For example, `options(shiny.error=recover)` will result a
|
||||
#' the debugger prompt when an error occurs.}
|
||||
#' \item{shiny.fullstacktrace (defaults to `FALSE`)}{Controls whether "pretty" (`FALSE`) or full
|
||||
#' stack traces (`TRUE`) are dumped to the console when errors occur during Shiny app execution.
|
||||
#' Pretty stack traces attempt to only show user-supplied code, but this pruning can't always
|
||||
#' be done 100\% correctly.}
|
||||
#' \item{shiny.host (defaults to `"127.0.0.1"`)}{The IP address that Shiny should listen on. See
|
||||
#' [runApp()] for more information.}
|
||||
#' \item{shiny.jquery.version (defaults to `3`)}{The major version of jQuery to use.
|
||||
#' Currently only values of `3` or `1` are supported. If `1`, then jQuery 1.12.4 is used. If `3`,
|
||||
#' then jQuery 3.4.1 is used.}
|
||||
#' \item{shiny.json.digits (defaults to `16`)}{The number of digits to use when converting
|
||||
#' numbers to JSON format to send to the client web browser.}
|
||||
#' \item{shiny.launch.browser (defaults to `interactive()`)}{A boolean which controls the default behavior
|
||||
#' when an app is run. See [runApp()] for more information.}
|
||||
#' \item{shiny.maxRequestSize (defaults to 5MB)}{This is a number which specifies the maximum
|
||||
#' web request size, which serves as a size limit for file uploads.}
|
||||
#' \item{shiny.minified (defaults to `TRUE`)}{By default
|
||||
#' Whether or not to include Shiny's JavaScript as a minified (`shiny.min.js`)
|
||||
#' or un-minified (`shiny.js`) file. The un-minified version is larger,
|
||||
#' but can be helpful for development and debugging.}
|
||||
#' \item{shiny.port (defaults to a random open port)}{A port number that Shiny will listen on. See
|
||||
#' [runApp()] for more information.}
|
||||
#' \item{shiny.reactlog (defaults to `FALSE`)}{If `TRUE`, enable logging of reactive events,
|
||||
#' which can be viewed later with the [reactlogShow()] function.
|
||||
#' This incurs a substantial performance penalty and should not be used in
|
||||
#' production.}
|
||||
#' \item{shiny.sanitize.errors (defaults to `FALSE`)}{If `TRUE`, then normal errors (i.e.
|
||||
#' errors not wrapped in `safeError`) won't show up in the app; a simple
|
||||
#' generic error message is printed instead (the error and strack trace printed
|
||||
#' to the console remain unchanged). If you want to sanitize errors in general, but you DO want a
|
||||
#' particular error `e` to get displayed to the user, then set this option
|
||||
#' to `TRUE` and use `stop(safeError(e))` for errors you want the
|
||||
#' user to see.}
|
||||
#' \item{shiny.stacktraceoffset (defaults to `TRUE`)}{If `TRUE`, then Shiny's printed stack
|
||||
#' traces will display srcrefs one line above their usual location. This is
|
||||
#' an arguably more intuitive arrangement for casual R users, as the name
|
||||
#' of a function appears next to the srcref where it is defined, rather than
|
||||
#' where it is currently being called from.}
|
||||
#' \item{shiny.suppressMissingContextError (defaults to `FALSE`)}{Normally, invoking a reactive
|
||||
#' outside of a reactive context (or [isolate()]) results in
|
||||
#' an error. If this is `TRUE`, don't error in these cases. This
|
||||
#' should only be used for debugging or demonstrations of reactivity at the
|
||||
#' console.}
|
||||
#' \item{shiny.testmode (defaults to `FALSE`)}{If `TRUE`, then various features for testing Shiny
|
||||
#' applications are enabled.}
|
||||
#' \item{shiny.trace (defaults to `FALSE`)}{Print messages sent between the R server and the web
|
||||
#' browser client to the R console. This is useful for debugging. Possible
|
||||
#' values are `"send"` (only print messages sent to the client),
|
||||
#' `"recv"` (only print messages received by the server), `TRUE`
|
||||
#' (print all messages), or `FALSE` (default; don't print any of these
|
||||
#' messages).}
|
||||
#' \item{shiny.usecairo (defaults to `TRUE`)}{This is used to disable graphical rendering by the
|
||||
#' Cairo package, if it is installed. See [plotPNG()] for more
|
||||
#' information.}
|
||||
#' The default polling interval is 500 milliseconds. You can change this
|
||||
#' by setting e.g. `options(shiny.autoreload.interval = 2000)` (every
|
||||
#' two seconds).}
|
||||
#' \item{shiny.deprecation.messages (defaults to `TRUE`)}{This controls whether messages for
|
||||
#' deprecated functions in Shiny will be printed. See
|
||||
#' [shinyDeprecated()] for more information.}
|
||||
#' \item{shiny.error (defaults to `NULL`)}{This can be a function which is called when an error
|
||||
#' occurs. For example, `options(shiny.error=recover)` will result a
|
||||
#' the debugger prompt when an error occurs.}
|
||||
#' \item{shiny.fullstacktrace (defaults to `FALSE`)}{Controls whether "pretty" (`FALSE`) or full
|
||||
#' stack traces (`TRUE`) are dumped to the console when errors occur during Shiny app execution.
|
||||
#' Pretty stack traces attempt to only show user-supplied code, but this pruning can't always
|
||||
#' be done 100% correctly.}
|
||||
#' \item{shiny.host (defaults to `"127.0.0.1"`)}{The IP address that Shiny should listen on. See
|
||||
#' [runApp()] for more information.}
|
||||
#' \item{shiny.jquery.version (defaults to `3`)}{The major version of jQuery to use.
|
||||
#' Currently only values of `3` or `1` are supported. If `1`, then jQuery 1.12.4 is used. If `3`,
|
||||
#' then jQuery 3.4.1 is used.}
|
||||
#' \item{shiny.json.digits (defaults to `16`)}{The number of digits to use when converting
|
||||
#' numbers to JSON format to send to the client web browser.}
|
||||
#' \item{shiny.launch.browser (defaults to `interactive()`)}{A boolean which controls the default behavior
|
||||
#' when an app is run. See [runApp()] for more information.}
|
||||
#' \item{shiny.maxRequestSize (defaults to 5MB)}{This is a number which specifies the maximum
|
||||
#' web request size, which serves as a size limit for file uploads.}
|
||||
#' \item{shiny.minified (defaults to `TRUE`)}{By default
|
||||
#' Whether or not to include Shiny's JavaScript as a minified (`shiny.min.js`)
|
||||
#' or un-minified (`shiny.js`) file. The un-minified version is larger,
|
||||
#' but can be helpful for development and debugging.}
|
||||
#' \item{shiny.port (defaults to a random open port)}{A port number that Shiny will listen on. See
|
||||
#' [runApp()] for more information.}
|
||||
#' \item{shiny.reactlog (defaults to `FALSE`)}{If `TRUE`, enable logging of reactive events,
|
||||
#' which can be viewed later with the [reactlogShow()] function.
|
||||
#' This incurs a substantial performance penalty and should not be used in
|
||||
#' production.}
|
||||
#' \item{shiny.sanitize.errors (defaults to `FALSE`)}{If `TRUE`, then normal errors (i.e.
|
||||
#' errors not wrapped in `safeError`) won't show up in the app; a simple
|
||||
#' generic error message is printed instead (the error and strack trace printed
|
||||
#' to the console remain unchanged). If you want to sanitize errors in general, but you DO want a
|
||||
#' particular error `e` to get displayed to the user, then set this option
|
||||
#' to `TRUE` and use `stop(safeError(e))` for errors you want the
|
||||
#' user to see.}
|
||||
#' \item{shiny.stacktraceoffset (defaults to `TRUE`)}{If `TRUE`, then Shiny's printed stack
|
||||
#' traces will display srcrefs one line above their usual location. This is
|
||||
#' an arguably more intuitive arrangement for casual R users, as the name
|
||||
#' of a function appears next to the srcref where it is defined, rather than
|
||||
#' where it is currently being called from.}
|
||||
#' \item{shiny.suppressMissingContextError (defaults to `FALSE`)}{Normally, invoking a reactive
|
||||
#' outside of a reactive context (or [isolate()]) results in
|
||||
#' an error. If this is `TRUE`, don't error in these cases. This
|
||||
#' should only be used for debugging or demonstrations of reactivity at the
|
||||
#' console.}
|
||||
#' \item{shiny.testmode (defaults to `FALSE`)}{If `TRUE`, then various features for testing Shiny
|
||||
#' applications are enabled.}
|
||||
#' \item{shiny.trace (defaults to `FALSE`)}{Print messages sent between the R server and the web
|
||||
#' browser client to the R console. This is useful for debugging. Possible
|
||||
#' values are `"send"` (only print messages sent to the client),
|
||||
#' `"recv"` (only print messages received by the server), `TRUE`
|
||||
#' (print all messages), or `FALSE` (default; don't print any of these
|
||||
#' messages).}
|
||||
#' \item{shiny.usecairo (defaults to `TRUE`)}{This is used to disable graphical rendering by the
|
||||
#' Cairo package, if it is installed. See [plotPNG()] for more
|
||||
#' information.}
|
||||
#' }
|
||||
#' @param ... Options to set, with the form `name = value`.
|
||||
#' @aliases shiny-options
|
||||
|
||||
69
R/shiny.R
69
R/shiny.R
@@ -723,9 +723,12 @@ ShinySession <- R6Class(
|
||||
requestFlush = function() {
|
||||
appsNeedingFlush$set(self$token, self)
|
||||
},
|
||||
scheduleTask = function(millis, callback) {
|
||||
.scheduleTask = function(millis, callback) {
|
||||
scheduleTask(millis, callback)
|
||||
},
|
||||
.now = function(){
|
||||
getTimeMs()
|
||||
},
|
||||
rootScope = function() {
|
||||
self
|
||||
},
|
||||
@@ -959,7 +962,9 @@ ShinySession <- R6Class(
|
||||
output$suspend()
|
||||
}
|
||||
# ..stacktraceon matches with the top-level ..stacktraceoff..
|
||||
private$closedCallbacks$invoke(onError = printError, ..stacktraceon = TRUE)
|
||||
withReactiveDomain(self, {
|
||||
private$closedCallbacks$invoke(onError = printError, ..stacktraceon = TRUE)
|
||||
})
|
||||
},
|
||||
isClosed = function() {
|
||||
return(self$closed)
|
||||
@@ -1294,6 +1299,9 @@ ShinySession <- R6Class(
|
||||
|
||||
getCurrentOutputInfo = function() {
|
||||
name <- private$currentOutputName
|
||||
if (is.null(name)) {
|
||||
return(NULL)
|
||||
}
|
||||
|
||||
tmp_info <- private$outputInfo[[name]] %OR% list(name = name)
|
||||
|
||||
@@ -2093,6 +2101,10 @@ outputOptions <- function(x, name, ...) {
|
||||
|
||||
#' Get information about the output that is currently being executed.
|
||||
#'
|
||||
#' @return A list with information about the current output, including the
|
||||
#' `name` of the output. If no output is currently being executed, this will
|
||||
#' return `NULL`.
|
||||
#'
|
||||
#' @param session The current Shiny session.
|
||||
#'
|
||||
#' @export
|
||||
@@ -2284,3 +2296,56 @@ ShinyServerTimingRecorder <- R6Class("ShinyServerTimingRecorder",
|
||||
)
|
||||
|
||||
missingOutput <- function(...) req(FALSE)
|
||||
|
||||
#' Insert inline Markdown
|
||||
#'
|
||||
#' This function accepts
|
||||
#' [Markdown](https://en.wikipedia.org/wiki/Markdown)-syntax text and returns
|
||||
#' HTML that may be included in Shiny UIs.
|
||||
#'
|
||||
#' Leading whitespace is trimmed from Markdown text with [glue::trim()].
|
||||
#' Whitespace trimming ensures Markdown is processed correctly even when the
|
||||
#' call to `markdown()` is indented within surrounding R code.
|
||||
#'
|
||||
#' By default, [Github extensions][commonmark::extensions] are enabled, but this
|
||||
#' can be disabled by passing `extensions = FALSE`.
|
||||
#'
|
||||
#' Markdown rendering is performed by [commonmark::markdown_html()]. Additional
|
||||
#' arguments to `markdown()` are passed as arguments to `markdown_html()`
|
||||
#'
|
||||
#' @param mds A character vector of Markdown source to convert to HTML. If the
|
||||
#' vector has more than one element, a single-element character vector of
|
||||
#' concatenated HTML is returned.
|
||||
#' @param extensions Enable Github syntax extensions; defaults to `TRUE`.
|
||||
#' @param .noWS Character vector used to omit some of the whitespace that would
|
||||
#' normally be written around generated HTML. Valid options include `before`,
|
||||
#' `after`, and `outside` (equivalent to `before` and `end`).
|
||||
#' @param ... Additional arguments to pass to [commonmark::markdown_html()].
|
||||
#' These arguments are _[dynamic][rlang::dyn-dots]_.
|
||||
#'
|
||||
#' @return a character vector marked as HTML.
|
||||
#' @export
|
||||
#' @examples
|
||||
#' ui <- fluidPage(
|
||||
#' markdown("
|
||||
#' # Markdown Example
|
||||
#'
|
||||
#' This is a markdown paragraph, and will be contained within a `<p>` tag
|
||||
#' in the UI.
|
||||
#'
|
||||
#' The following is an unordered list, which will be represented in the UI as
|
||||
#' a `<ul>` with `<li>` children:
|
||||
#'
|
||||
#' * a bullet
|
||||
#' * another
|
||||
#'
|
||||
#' [Links](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) work;
|
||||
#' so does *emphasis*.
|
||||
#'
|
||||
#' To see more of what's possible, check out [commonmark.org/help](https://commonmark.org/help).
|
||||
#' ")
|
||||
#' )
|
||||
markdown <- function(mds, extensions = TRUE, .noWS = NULL, ...) {
|
||||
html <- rlang::exec(commonmark::markdown_html, glue::trim(mds), extensions = extensions, ...)
|
||||
htmltools::HTML(html, .noWS = .noWS)
|
||||
}
|
||||
|
||||
119
R/test-server.R
Normal file
119
R/test-server.R
Normal file
@@ -0,0 +1,119 @@
|
||||
# Create a "data mask" suitable for passing to rlang::eval_tidy. Bindings in
|
||||
# `env` and bindings in the parent of `env` are merged into a single named list.
|
||||
# Bindings in `env` take precedence over bindings in the parent of `env`.
|
||||
#' @noRd
|
||||
makeMask <- function(env) {
|
||||
stopifnot(length(rlang::env_parents(env)) > 1)
|
||||
child <- as.list(env)
|
||||
parent <- as.list(rlang::env_parent(env))
|
||||
parent_only <- setdiff(names(parent), names(child))
|
||||
append(child, parent[parent_only])
|
||||
}
|
||||
|
||||
#' @noRd
|
||||
isModuleServer <- function(x) {
|
||||
is.function(x) && names(formals(x))[1] == "id"
|
||||
}
|
||||
|
||||
#' Reactive testing for Shiny server functions and modules
|
||||
#'
|
||||
#' A way to test the reactive interactions in Shiny applications. Reactive
|
||||
#' interactions are defined in the server function of applications and in
|
||||
#' modules.
|
||||
#' @param app The path to an application or module to test. In addition to
|
||||
#' paths, applications may be represented by any object suitable for coercion
|
||||
#' to an `appObj` by `as.shiny.appobj`. Application server functions must
|
||||
#' include a `session` argument in order to be tested.
|
||||
#' @param expr Test code containing expectations. The test expression will run
|
||||
#' in the server function environment, meaning that the parameters of the
|
||||
#' server function (e.g. `input`, `output`, and `session`) will be available
|
||||
#' along with any other values created inside of the server function.
|
||||
#' @param ... Additional arguments to pass to the module function. These
|
||||
#' arguments are processed with [rlang::list2()] and so are
|
||||
#' _[dynamic][rlang::dyn-dots]_. If `app` is a module, and no `id` argument is
|
||||
#' provided, one will be generated and supplied automatically.
|
||||
#' @return The result of evaluating `expr`.
|
||||
#' @include mock-session.R
|
||||
#' @rdname testServer
|
||||
#' @examples
|
||||
#' server <- function(id, multiplier = 2, prefix = "I am ") {
|
||||
#' moduleServer(id, function(input, output, session) {
|
||||
#' myreactive <- reactive({
|
||||
#' input$x * multiplier
|
||||
#' })
|
||||
#' output$txt <- renderText({
|
||||
#' paste0(prefix, myreactive())
|
||||
#' })
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' testServer(server, {
|
||||
#' session$setInputs(x = 1)
|
||||
#' # You're also free to use third-party
|
||||
#' # testing packages like testthat:
|
||||
#' # expect_equal(myreactive(), 2)
|
||||
#' stopifnot(myreactive() == 2)
|
||||
#' stopifnot(output$txt == "I am 2")
|
||||
#'
|
||||
#' session$setInputs(x = 2)
|
||||
#' stopifnot(myreactive() == 4)
|
||||
#' stopifnot(output$txt == "I am 4")
|
||||
#' # Any additional arguments, below, are passed along to the module.
|
||||
#' }, multiplier = 2)
|
||||
#' @export
|
||||
testServer <- function(app, expr, ...) {
|
||||
|
||||
args <- rlang::list2(...)
|
||||
|
||||
session <- getDefaultReactiveDomain()
|
||||
|
||||
if (inherits(session, "MockShinySession"))
|
||||
stop("Test expressions may not call testServer()")
|
||||
if (inherits(session, "session_proxy")
|
||||
&& inherits(get("parent", envir = session), "MockShinySession"))
|
||||
stop("Modules may not call testServer()")
|
||||
|
||||
session <- MockShinySession$new()
|
||||
on.exit(if (!session$isClosed()) session$close())
|
||||
|
||||
if (isModuleServer(app)) {
|
||||
if (!("id" %in% names(args)))
|
||||
args[["id"]] <- session$genId()
|
||||
} else {
|
||||
appobj <- as.shiny.appobj(app)
|
||||
server <- appobj$serverFuncSource()
|
||||
if (! "session" %in% names(formals(server)))
|
||||
stop("Tested application server functions must declare input, output, and session arguments.")
|
||||
body(server) <- rlang::expr({
|
||||
session$setEnv(base::environment())
|
||||
!!!body(server)
|
||||
})
|
||||
app <- function() {
|
||||
session$setReturned(server(input = session$input, output = session$output, session = session))
|
||||
}
|
||||
if (length(args))
|
||||
message("Discarding unused arguments to server function")
|
||||
}
|
||||
|
||||
isolate(
|
||||
withReactiveDomain(
|
||||
session,
|
||||
withr::with_options(list(`shiny.allowoutputreads` = TRUE), {
|
||||
rlang::exec(app, !!!args)
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
stopifnot(all(c("input", "output", "session") %in% ls(session$env)))
|
||||
|
||||
quosure <- rlang::enquo(expr)
|
||||
|
||||
isolate(
|
||||
withReactiveDomain(
|
||||
session,
|
||||
withr::with_options(list(`shiny.allowoutputreads` = TRUE), {
|
||||
rlang::eval_tidy(quosure, makeMask(session$env), rlang::caller_env())
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
141
R/test.R
Normal file
141
R/test.R
Normal file
@@ -0,0 +1,141 @@
|
||||
#' Creates and returns run result data frame.
|
||||
#'
|
||||
#' @param file Name of the test runner file, a character vector of length 1.
|
||||
#' @param pass Whether or not the test passed, a logical vector of length 1.
|
||||
#' @param result Value (wrapped in a list) obtained by evaluating `file` or `NA`
|
||||
#' if no value was obtained, such as with `shinytest`.
|
||||
#' @param error Error, if any, (and wrapped in a list) that was signaled during
|
||||
#' evaluation of `file`.
|
||||
#'
|
||||
#' @return A 1-row data frame representing a single test run. `result` and
|
||||
#' `error` are "list columns", or columns that may contain list elements.
|
||||
#' @noRd
|
||||
result_row <- function(file, pass, result, error) {
|
||||
stopifnot(is.list(result))
|
||||
stopifnot(is.list(error))
|
||||
df <- data.frame(
|
||||
file = file,
|
||||
pass = pass,
|
||||
result = I(result),
|
||||
error = I(error),
|
||||
stringsAsFactors = FALSE
|
||||
)
|
||||
class(df) <- c("shinytestrun", class(df))
|
||||
df
|
||||
}
|
||||
|
||||
#' Check to see if the given text is a shinytest
|
||||
#' Scans for the magic string of `app <- ShinyDriver$new(` as an indicator that this is a shinytest.
|
||||
#' Brought in from shinytest to avoid having to export this function.
|
||||
#' @noRd
|
||||
isShinyTest <- function(text){
|
||||
lines <- grepl("app\\s*<-\\s*ShinyDriver\\$new\\(", text, perl=TRUE)
|
||||
any(lines)
|
||||
}
|
||||
|
||||
#' Runs the tests associated with this Shiny app
|
||||
#'
|
||||
#' Sources the `.R` files in the top-level of `tests/` much like `R CMD check`.
|
||||
#' These files are typically simple runners for tests nested in other
|
||||
#' directories under `tests/`.
|
||||
#'
|
||||
#' @param appDir The base directory for the application.
|
||||
#' @param filter If not `NULL`, only tests with file names matching this regular
|
||||
#' expression will be executed. Matching is performed on the file name
|
||||
#' including the extension.
|
||||
#'
|
||||
#' @return A data frame classed with the supplemental class `"shinytestrun"`.
|
||||
#' The data frame has the following columns:
|
||||
#'
|
||||
#' | **Name** | **Type** | **Meaning** |
|
||||
#' | :-- | :-- | :-- |
|
||||
#' | `file` | `character(1)` | File name of the runner script in `tests/` that was sourced. |
|
||||
#' | `pass` | `logical(1)` | Whether or not the runner script signaled an error when sourced. |
|
||||
#' | `result` | any or `NA` | The return value of the runner, or `NA` if `pass == FALSE`. |
|
||||
#' | `error` | any or `NA` | The error signaled by the runner, or `NA` if `pass == TRUE`. |
|
||||
#'
|
||||
#' @details Historically, [shinytest](https://rstudio.github.io/shinytest/)
|
||||
#' recommended placing tests at the top-level of the `tests/` directory. In
|
||||
#' order to support that model, `testApp` first checks to see if the `.R`
|
||||
#' files in the `tests/` directory are all shinytests; if so, just calls out
|
||||
#' to [shinytest::testApp()].
|
||||
#' @export
|
||||
runTests <- function(appDir=".", filter=NULL){
|
||||
require(shiny)
|
||||
|
||||
testsDir <- file.path(appDir, "tests")
|
||||
if (!dirExists(testsDir)){
|
||||
stop("No tests directory found: ", testsDir)
|
||||
}
|
||||
runners <- list.files(testsDir, pattern="\\.r$", ignore.case = TRUE)
|
||||
|
||||
if (length(runners) == 0){
|
||||
message("No test runners found in ", testsDir)
|
||||
return(result_row(character(0), logical(0), list(), list()))
|
||||
}
|
||||
|
||||
if (!is.null(filter)){
|
||||
runners <- runners[grepl(filter, runners)]
|
||||
}
|
||||
if (length(runners) == 0){
|
||||
stop("No test runners matched the given filter: '", filter, "'")
|
||||
}
|
||||
|
||||
# Inspect each runner to see if it appears to be a shinytest
|
||||
isST <- vapply(runners, function(r){
|
||||
text <- readLines(file.path(testsDir, r), warn = FALSE)
|
||||
isShinyTest(text)
|
||||
}, logical(1))
|
||||
|
||||
# See the @details section of the runTests() docs above for why this branch exists.
|
||||
if (all(isST)){
|
||||
# just call out to shinytest
|
||||
# We don't need to message/warn here since shinytest already does it.
|
||||
if (!requireNamespace("shinytest", quietly=TRUE) ){
|
||||
stop("It appears that the .R files in ", testsDir,
|
||||
" are all shinytests, but shinytest is not installed.")
|
||||
}
|
||||
|
||||
if (!getOption("shiny.autoload.r", TRUE)) {
|
||||
warning("You've disabled `shiny.autoload.r` via an option but this is not passed through to shinytest. Consider using a _disable_autoload.R file as described at https://rstd.io/shiny-autoload")
|
||||
}
|
||||
|
||||
return(do.call(rbind, lapply(shinytest::testApp(appDir)[["results"]], function(r) {
|
||||
error <- if (r[["pass"]]) NA else simpleError("Unknown shinytest error")
|
||||
result_row(r[["name"]], r[["pass"]], list(NA), list(error))
|
||||
})))
|
||||
}
|
||||
|
||||
testenv <- new.env(parent=globalenv())
|
||||
renv <- new.env(parent=testenv)
|
||||
if (getOption("shiny.autoload.r", TRUE)) {
|
||||
loadSupport(appDir, renv=renv, globalrenv=testenv)
|
||||
} else if (file.exists.ci(file.path(appDir, "server.R"))){
|
||||
# then check for global.R to load
|
||||
if (file.exists(file.path.ci(appDir, "global.R"))){
|
||||
sourceUTF8(file.path.ci(appDir, "global.R"))
|
||||
}
|
||||
}
|
||||
|
||||
oldwd <- getwd()
|
||||
on.exit({
|
||||
setwd(oldwd)
|
||||
}, add=TRUE)
|
||||
|
||||
setwd(testsDir)
|
||||
|
||||
# Otherwise source all the runners -- each in their own environment.
|
||||
return(do.call(rbind, lapply(runners, function(r) {
|
||||
result <- NA
|
||||
error <- NA
|
||||
pass <- FALSE
|
||||
tryCatch({
|
||||
env <- new.env(parent = renv)
|
||||
result <- sourceUTF8(r, envir = env)
|
||||
pass <- TRUE
|
||||
}, error = function(e) {
|
||||
error <<- e
|
||||
})
|
||||
result_row(r, pass, list(result), list(error))
|
||||
})))
|
||||
}
|
||||
61
R/timer.R
61
R/timer.R
@@ -1,10 +1,9 @@
|
||||
# Return the current time, in milliseconds from epoch, with
|
||||
# unspecified time zone.
|
||||
getNow <- function() {
|
||||
# Return the current time, in milliseconds from epoch.
|
||||
getTimeMs <- function() {
|
||||
as.numeric(Sys.time()) * 1000
|
||||
}
|
||||
|
||||
BaseTimerCallbacks <- R6Class(
|
||||
TimerCallbacks <- R6Class(
|
||||
'TimerCallbacks',
|
||||
portable = FALSE,
|
||||
class = FALSE,
|
||||
@@ -14,7 +13,7 @@ BaseTimerCallbacks <- R6Class(
|
||||
.times = data.frame(),
|
||||
.now = 'Function',
|
||||
|
||||
initialize = function(nowFn=getNow) {
|
||||
initialize = function(nowFn = getTimeMs) {
|
||||
.funcs <<- Map$new()
|
||||
.now <<- nowFn
|
||||
},
|
||||
@@ -62,7 +61,7 @@ BaseTimerCallbacks <- R6Class(
|
||||
},
|
||||
takeElapsed = function() {
|
||||
t <- .now()
|
||||
elapsed <- .times$time < .now()
|
||||
elapsed <- .times$time <= .now()
|
||||
result <- .times[elapsed,]
|
||||
.times <<- .times[!elapsed,]
|
||||
|
||||
@@ -88,34 +87,24 @@ BaseTimerCallbacks <- R6Class(
|
||||
)
|
||||
)
|
||||
|
||||
TimerCallbacks <- R6Class(
|
||||
'TimerCallbacks',
|
||||
inherit=BaseTimerCallbacks,
|
||||
portable = FALSE,
|
||||
class = FALSE,
|
||||
public = list(
|
||||
# Empty constructor defaults to the getNow implementation
|
||||
initialize = function() {
|
||||
super$initialize(getNow)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
MockableTimerCallbacks <- R6Class(
|
||||
'MockableTimerCallbacks',
|
||||
inherit=BaseTimerCallbacks,
|
||||
inherit = TimerCallbacks,
|
||||
portable = FALSE,
|
||||
class = FALSE,
|
||||
public = list(
|
||||
# Empty constructor defaults to the getNow implementation
|
||||
initialize = function() {
|
||||
super$initialize(self$now)
|
||||
super$initialize(self$mockNow)
|
||||
},
|
||||
now = function(){
|
||||
mockNow = function() {
|
||||
return(private$time)
|
||||
},
|
||||
elapse = function(millis){
|
||||
private$time <<- private$time + millis
|
||||
elapse = function(millis) {
|
||||
private$time <- private$time + millis
|
||||
},
|
||||
getElapsed = function() {
|
||||
private$time
|
||||
}
|
||||
), private = list(
|
||||
time = 0L
|
||||
@@ -137,24 +126,22 @@ scheduleTask <- function(millis, callback) {
|
||||
#' session scheduler, but if it doesn't exist, use the global one.
|
||||
#' @noRd
|
||||
defineScheduler <- function(session){
|
||||
if (!is.null(session)){
|
||||
if (!is.null(session$scheduleTask)){
|
||||
return(session$scheduleTask)
|
||||
}
|
||||
if (!is.null(session) && !is.null(session$.scheduleTask)){
|
||||
return(session$.scheduleTask)
|
||||
}
|
||||
scheduleTask
|
||||
}
|
||||
|
||||
|
||||
#' Get the current time a la `Sys.time()`. Prefer to get it via the
|
||||
#' `session$now()` function, but if that's not available, just return the
|
||||
#' current system time.
|
||||
#' Get the current time using the current reactive domain. This will try to use
|
||||
#' the session's .now() method, but if that's not available, it will just return
|
||||
#' the real time (from getTimeMs()). The purpose of this function is to allow
|
||||
#' MockableTimerCallbacks to work.
|
||||
#' @noRd
|
||||
getTime <- function(session){
|
||||
if (!is.null(session)){
|
||||
if (!is.null(session$now)){
|
||||
return(session$now())
|
||||
}
|
||||
getDomainTimeMs <- function(session){
|
||||
if (!is.null(session) && !is.null(session$.now)){
|
||||
return(session$.now())
|
||||
} else {
|
||||
getTimeMs()
|
||||
}
|
||||
Sys.time()
|
||||
}
|
||||
|
||||
26
R/utils.R
26
R/utils.R
@@ -316,6 +316,15 @@ resolve <- function(dir, relpath) {
|
||||
return(abs.path)
|
||||
}
|
||||
|
||||
# Given a string, make sure it has a trailing slash.
|
||||
ensure_trailing_slash <- function(path) {
|
||||
if (!grepl("/$", path)) {
|
||||
path <- paste0(path, "/")
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
|
||||
isWindows <- function() .Platform$OS.type == 'windows'
|
||||
|
||||
# This is a wrapper for download.file and has the same interface.
|
||||
@@ -1812,3 +1821,20 @@ cat_line <- function(...) {
|
||||
cat(paste(..., "\n", collapse = ""))
|
||||
}
|
||||
|
||||
select_menu <- function(choices, title = NULL, msg = "Enter one or more numbers (with spaces), or an empty line to exit: \n")
|
||||
{
|
||||
if (!is.null(title)) {
|
||||
cat(title, "\n", sep = "")
|
||||
}
|
||||
nc <- length(choices)
|
||||
op <- paste0(format(seq_len(nc)), ": ", choices)
|
||||
fop <- format(op)
|
||||
cat("", fop, "", sep = "\n")
|
||||
repeat {
|
||||
answer <- readline(msg)
|
||||
answer <- strsplit(answer, "[ ,]+")[[1]]
|
||||
if (all(answer %in% seq_along(choices))) {
|
||||
return(choices[as.integer(answer)])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
appveyor.yml
10
appveyor.yml
@@ -11,14 +11,16 @@ install:
|
||||
ps: Bootstrap
|
||||
|
||||
cache:
|
||||
- C:\RLibrary
|
||||
# Bust library cache every time the description file changes
|
||||
# as appveyor cache can not be busted manually
|
||||
# This helps get around errors such as "can't update curl because it's already loaded"
|
||||
# when trying to update the existing cache
|
||||
# PR: https://github.com/rstudio/shiny/pull/2722
|
||||
- C:\RLibrary -> DESCRIPTION
|
||||
|
||||
# Adapt as necessary starting from here
|
||||
|
||||
build_script:
|
||||
- travis-tool.sh install_github rstudio/htmltools@rc-v0.4.0
|
||||
- travis-tool.sh install_github rstudio/promises@rc-v1.1.0
|
||||
- travis-tool.sh install_github r-lib/later@rc-v1.0.0
|
||||
- travis-tool.sh install_deps
|
||||
|
||||
test_script:
|
||||
|
||||
27
inst/app_template/R/my-module.R
Normal file
27
inst/app_template/R/my-module.R
Normal file
@@ -0,0 +1,27 @@
|
||||
mymoduleUI <- function(id, label = "Counter") {
|
||||
# Al uses of Shiny input/output IDs in the UI must be namespaced,
|
||||
# as in ns("x").
|
||||
ns <- NS(id)
|
||||
tagList(
|
||||
actionButton(ns("button"), label = label),
|
||||
verbatimTextOutput(ns("out"))
|
||||
)
|
||||
}
|
||||
|
||||
mymoduleServer <- function(id) {
|
||||
# moduleServer() wraps a function to create the server component of a
|
||||
# module.
|
||||
moduleServer(
|
||||
id,
|
||||
function(input, output, session) {
|
||||
count <- reactiveVal(0)
|
||||
observeEvent(input$button, {
|
||||
count(count() + 1)
|
||||
})
|
||||
output$out <- renderText({
|
||||
count()
|
||||
})
|
||||
count
|
||||
}
|
||||
)
|
||||
}
|
||||
5
inst/app_template/R/sort.R
Normal file
5
inst/app_template/R/sort.R
Normal file
@@ -0,0 +1,5 @@
|
||||
# Given a numeric vector, convert to strings, sort, and convert back to
|
||||
# numeric.
|
||||
lexical_sort <- function(x) {
|
||||
as.numeric(sort(as.character(x)))
|
||||
}
|
||||
52
inst/app_template/app.R
Normal file
52
inst/app_template/app.R
Normal file
@@ -0,0 +1,52 @@
|
||||
ui <- fluidPage(
|
||||
{{
|
||||
# These blocks of code are processed with htmlTemplate()
|
||||
if (isTRUE(module)) {
|
||||
' # ======== Modules ========
|
||||
# mymoduleUI is defined in R/my-module.R
|
||||
mymoduleUI("mymodule1", "Click counter #1"),
|
||||
mymoduleUI("mymodule2", "Click counter #2"),
|
||||
# =========================
|
||||
'
|
||||
}
|
||||
}}
|
||||
wellPanel(
|
||||
sliderInput("size", "Data size", min = 5, max = 20, value = 10),
|
||||
{{
|
||||
if (isTRUE(rdir)) {
|
||||
' div("Lexically sorted sequence:"),'
|
||||
} else {
|
||||
' div("Sorted sequence:"),'
|
||||
}
|
||||
}}
|
||||
verbatimTextOutput("sequence")
|
||||
)
|
||||
)
|
||||
|
||||
server <- function(input, output, session) {
|
||||
{{
|
||||
if (isTRUE(module)) {
|
||||
' # ======== Modules ========
|
||||
# mymoduleServer is defined in R/my-module.R
|
||||
mymoduleServer("mymodule1")
|
||||
mymoduleServer("mymodule2")
|
||||
# =========================
|
||||
'
|
||||
}
|
||||
}}
|
||||
data <- reactive({
|
||||
{{
|
||||
if (isTRUE(rdir)) {
|
||||
' # lexical_sort from R/sort.R
|
||||
lexical_sort(seq_len(input$size))'
|
||||
} else {
|
||||
' sort(seq_len(input$size))'
|
||||
}
|
||||
}}
|
||||
})
|
||||
output$sequence <- renderText({
|
||||
paste(data(), collapse = " ")
|
||||
})
|
||||
}
|
||||
|
||||
shinyApp(ui, server)
|
||||
6
inst/app_template/tests/server.R
Normal file
6
inst/app_template/tests/server.R
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
files <- list.files("./server", full.names = FALSE)
|
||||
origwd <- getwd()
|
||||
setwd("./server")
|
||||
on.exit(setwd(origwd), add=TRUE)
|
||||
lapply(files, source, local=environment())
|
||||
19
inst/app_template/tests/server/test-mymodule.R
Normal file
19
inst/app_template/tests/server/test-mymodule.R
Normal file
@@ -0,0 +1,19 @@
|
||||
# Use testthat just for expectations
|
||||
library(testthat)
|
||||
|
||||
# See ?testServer for more information
|
||||
testServer(mymoduleServer, {
|
||||
# Set initial value of a button
|
||||
session$setInputs(button = 0)
|
||||
|
||||
# Check the value of the reactiveVal `count()`
|
||||
expect_equal(count(), 1)
|
||||
# Check the value of the renderText()
|
||||
expect_equal(output$out, "1")
|
||||
|
||||
# Simulate a click
|
||||
session$setInputs(button = 1)
|
||||
|
||||
expect_equal(count(), 2)
|
||||
expect_equal(output$out, "2")
|
||||
})
|
||||
11
inst/app_template/tests/server/test-server.R
Normal file
11
inst/app_template/tests/server/test-server.R
Normal file
@@ -0,0 +1,11 @@
|
||||
# Use testthat just for expectations
|
||||
library(testthat)
|
||||
|
||||
testServer('../..', {
|
||||
# Set the `size` slider and check the output
|
||||
session$setInputs(size = 6)
|
||||
expect_equal(output$sequence, "1 2 3 4 5 6")
|
||||
|
||||
session$setInputs(size = 12)
|
||||
expect_equal(output$sequence, "1 2 3 4 5 6 7 8 9 10 11 12")
|
||||
})
|
||||
3
inst/app_template/tests/shinytest.R
Normal file
3
inst/app_template/tests/shinytest.R
Normal file
@@ -0,0 +1,3 @@
|
||||
library(shinytest)
|
||||
shinytest::testApp("../")
|
||||
|
||||
7
inst/app_template/tests/shinytest/mytest.R
Normal file
7
inst/app_template/tests/shinytest/mytest.R
Normal file
@@ -0,0 +1,7 @@
|
||||
app <- ShinyDriver$new("../../")
|
||||
app$snapshotInit("mytest")
|
||||
|
||||
app$snapshot()
|
||||
app$setInputs(`mymodule1-button` = "click")
|
||||
app$setInputs(`mymodule1-button` = "click")
|
||||
app$snapshot()
|
||||
6
inst/app_template/tests/testthat.R
Normal file
6
inst/app_template/tests/testthat.R
Normal file
@@ -0,0 +1,6 @@
|
||||
library(testthat)
|
||||
|
||||
# Run in the "current" environment, because shiny::runTests() is going to
|
||||
# provision a new environment that's just for our test. And we'll want access to
|
||||
# the supporting files that were already loaded into that env.
|
||||
testthat::test_dir("./testthat", env = environment())
|
||||
11
inst/app_template/tests/testthat/helper-load.R
Normal file
11
inst/app_template/tests/testthat/helper-load.R
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
# The RStudio IDE offers a "Run Tests" button when it sees testthat tests but it runs
|
||||
# in its own environment/process. Which means that any helpers we've loaded into our
|
||||
# environment won't be visible. So we add this helper not because it's actually needed
|
||||
# in the typical `shiny::runTests` workflow, but to make that IDE button work.
|
||||
# Once the IDE adds proper support for this style, we'll be able to drop these files.
|
||||
#
|
||||
# Note that this may redundantly source the files in your R/ dir depending on your
|
||||
# workflow.
|
||||
library(shiny)
|
||||
shiny::loadSupport("../../", renv = globalenv())
|
||||
5
inst/app_template/tests/testthat/test-sort.R
Normal file
5
inst/app_template/tests/testthat/test-sort.R
Normal file
@@ -0,0 +1,5 @@
|
||||
# Test the lexical_sort function from R/utils.R
|
||||
test_that("Lexical sorting works", {
|
||||
expect_equal(lexical_sort(c(1, 2, 3)), c(1, 2, 3))
|
||||
expect_equal(lexical_sort(c(1, 2, 3, 13, 11, 21)), c(1, 11, 13, 2, 21, 3))
|
||||
})
|
||||
@@ -29,8 +29,8 @@ $.extend( true, DataTable.defaults, {
|
||||
/* Default class modification */
|
||||
$.extend( DataTable.ext.classes, {
|
||||
sWrapper: "dataTables_wrapper form-inline dt-bootstrap",
|
||||
sFilterInput: "form-control input-sm",
|
||||
sLengthSelect: "form-control input-sm"
|
||||
sFilterInput: "form-control form-control-sm input-sm",
|
||||
sLengthSelect: "form-control form-control-sm input-sm"
|
||||
} );
|
||||
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -401,6 +401,9 @@ pre.shiny-text-output {
|
||||
/* Overrides bootstrap-datepicker3.css styling for invalid date ranges.
|
||||
See https://github.com/rstudio/shiny/issues/2042 for details. */
|
||||
.datepicker table tbody tr td.disabled,
|
||||
.datepicker table tbody tr td.disabled:hover {
|
||||
.datepicker table tbody tr td.disabled:hover,
|
||||
.datepicker table tbody tr td span.disabled,
|
||||
.datepicker table tbody tr td span.disabled:hover {
|
||||
color: #aaa;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
6
inst/www/shared/shiny.min.js
vendored
6
inst/www/shared/shiny.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
24
man/HTML.Rd
24
man/HTML.Rd
@@ -1,24 +0,0 @@
|
||||
\name{HTML}
|
||||
\alias{HTML}
|
||||
\title{Mark Characters as HTML}
|
||||
\usage{
|
||||
HTML(text, ...)
|
||||
}
|
||||
\arguments{
|
||||
\item{text}{The text value to mark with HTML}
|
||||
|
||||
\item{...}{Any additional values to be converted to character and
|
||||
concatenated together}
|
||||
}
|
||||
\value{
|
||||
The same value, but marked as HTML.
|
||||
}
|
||||
\description{
|
||||
Marks the given text as HTML, which means the \link{tag} functions will know
|
||||
not to perform HTML escaping on it.
|
||||
}
|
||||
\examples{
|
||||
el <- div(HTML("I like <u>turtles</u>"))
|
||||
cat(as.character(el))
|
||||
|
||||
}
|
||||
746
man/MockShinySession.Rd
Normal file
746
man/MockShinySession.Rd
Normal file
@@ -0,0 +1,746 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/mock-session.R
|
||||
\name{MockShinySession}
|
||||
\alias{MockShinySession}
|
||||
\title{Mock Shiny Session}
|
||||
\description{
|
||||
An R6 class suitable for testing that simulates the \code{session} parameter
|
||||
provided to Shiny server functions or modules.
|
||||
}
|
||||
\examples{
|
||||
|
||||
## ------------------------------------------------
|
||||
## Method `MockShinySession$setInputs`
|
||||
## ------------------------------------------------
|
||||
|
||||
s <- MockShinySession$new()
|
||||
s$setInputs(x=1, y=2)
|
||||
}
|
||||
\section{Public fields}{
|
||||
\if{html}{\out{<div class="r6-fields">}}
|
||||
\describe{
|
||||
\item{\code{env}}{The environment associated with the session.}
|
||||
|
||||
\item{\code{returned}}{The value returned by the module.}
|
||||
|
||||
\item{\code{singletons}}{Hardcoded as empty. Needed for rendering HTML (i.e. renderUI)}
|
||||
|
||||
\item{\code{clientData}}{Mock client data that always returns a size for plots}
|
||||
|
||||
\item{\code{output}}{The shinyoutputs associated with the session}
|
||||
|
||||
\item{\code{input}}{The reactive inputs associated with the session}
|
||||
|
||||
\item{\code{userData}}{An environment initialized as empty.}
|
||||
|
||||
\item{\code{progressStack}}{A stack of progress objects}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
\section{Active bindings}{
|
||||
\if{html}{\out{<div class="r6-active-bindings">}}
|
||||
\describe{
|
||||
\item{\code{request}}{An empty environment where the request should be. The request isn't meaningfully mocked currently.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
\section{Methods}{
|
||||
\subsection{Public methods}{
|
||||
\itemize{
|
||||
\item \href{#method-reactlog}{\code{MockShinySession$reactlog()}}
|
||||
\item \href{#method-incrementBusyCount}{\code{MockShinySession$incrementBusyCount()}}
|
||||
\item \href{#method-new}{\code{MockShinySession$new()}}
|
||||
\item \href{#method-onFlush}{\code{MockShinySession$onFlush()}}
|
||||
\item \href{#method-onFlushed}{\code{MockShinySession$onFlushed()}}
|
||||
\item \href{#method-onEnded}{\code{MockShinySession$onEnded()}}
|
||||
\item \href{#method-isEnded}{\code{MockShinySession$isEnded()}}
|
||||
\item \href{#method-isClosed}{\code{MockShinySession$isClosed()}}
|
||||
\item \href{#method-close}{\code{MockShinySession$close()}}
|
||||
\item \href{#method-cycleStartAction}{\code{MockShinySession$cycleStartAction()}}
|
||||
\item \href{#method-fileUrl}{\code{MockShinySession$fileUrl()}}
|
||||
\item \href{#method-setInputs}{\code{MockShinySession$setInputs()}}
|
||||
\item \href{#method-.scheduleTask}{\code{MockShinySession$.scheduleTask()}}
|
||||
\item \href{#method-elapse}{\code{MockShinySession$elapse()}}
|
||||
\item \href{#method-.now}{\code{MockShinySession$.now()}}
|
||||
\item \href{#method-defineOutput}{\code{MockShinySession$defineOutput()}}
|
||||
\item \href{#method-getOutput}{\code{MockShinySession$getOutput()}}
|
||||
\item \href{#method-registerDataObj}{\code{MockShinySession$registerDataObj()}}
|
||||
\item \href{#method-allowReconnect}{\code{MockShinySession$allowReconnect()}}
|
||||
\item \href{#method-reload}{\code{MockShinySession$reload()}}
|
||||
\item \href{#method-resetBrush}{\code{MockShinySession$resetBrush()}}
|
||||
\item \href{#method-sendCustomMessage}{\code{MockShinySession$sendCustomMessage()}}
|
||||
\item \href{#method-sendBinaryMessage}{\code{MockShinySession$sendBinaryMessage()}}
|
||||
\item \href{#method-sendInputMessage}{\code{MockShinySession$sendInputMessage()}}
|
||||
\item \href{#method-setBookmarkExclude}{\code{MockShinySession$setBookmarkExclude()}}
|
||||
\item \href{#method-getBookmarkExclude}{\code{MockShinySession$getBookmarkExclude()}}
|
||||
\item \href{#method-onBookmark}{\code{MockShinySession$onBookmark()}}
|
||||
\item \href{#method-onBookmarked}{\code{MockShinySession$onBookmarked()}}
|
||||
\item \href{#method-doBookmark}{\code{MockShinySession$doBookmark()}}
|
||||
\item \href{#method-onRestore}{\code{MockShinySession$onRestore()}}
|
||||
\item \href{#method-onRestored}{\code{MockShinySession$onRestored()}}
|
||||
\item \href{#method-exportTestValues}{\code{MockShinySession$exportTestValues()}}
|
||||
\item \href{#method-getTestSnapshotUrl}{\code{MockShinySession$getTestSnapshotUrl()}}
|
||||
\item \href{#method-ns}{\code{MockShinySession$ns()}}
|
||||
\item \href{#method-flushReact}{\code{MockShinySession$flushReact()}}
|
||||
\item \href{#method-makeScope}{\code{MockShinySession$makeScope()}}
|
||||
\item \href{#method-setEnv}{\code{MockShinySession$setEnv()}}
|
||||
\item \href{#method-setReturned}{\code{MockShinySession$setReturned()}}
|
||||
\item \href{#method-getReturned}{\code{MockShinySession$getReturned()}}
|
||||
\item \href{#method-genId}{\code{MockShinySession$genId()}}
|
||||
\item \href{#method-clone}{\code{MockShinySession$clone()}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-reactlog"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-reactlog}{}}}
|
||||
\subsection{Method \code{reactlog()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$reactlog(logEntry)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{logEntry}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-incrementBusyCount"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-incrementBusyCount}{}}}
|
||||
\subsection{Method \code{incrementBusyCount()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$incrementBusyCount()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-new"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-new}{}}}
|
||||
\subsection{Method \code{new()}}{
|
||||
Create a new MockShinySession
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$new()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-onFlush"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onFlush}{}}}
|
||||
\subsection{Method \code{onFlush()}}{
|
||||
Define a callback to be invoked before a reactive flush
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$onFlush(fun, once = TRUE)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{fun}}{The function to invoke}
|
||||
|
||||
\item{\code{once}}{If \code{TRUE}, will only run once. Otherwise, will run every time reactives are flushed.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-onFlushed"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onFlushed}{}}}
|
||||
\subsection{Method \code{onFlushed()}}{
|
||||
Define a callback to be invoked after a reactive flush
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$onFlushed(fun, once = TRUE)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{fun}}{The function to invoke}
|
||||
|
||||
\item{\code{once}}{If \code{TRUE}, will only run once. Otherwise, will run every time reactives are flushed.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-onEnded"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onEnded}{}}}
|
||||
\subsection{Method \code{onEnded()}}{
|
||||
Define a callback to be invoked when the session ends
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$onEnded(sessionEndedCallback)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{sessionEndedCallback}}{The callback to invoke when the session has ended.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-isEnded"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-isEnded}{}}}
|
||||
\subsection{Method \code{isEnded()}}{
|
||||
Returns \code{FALSE} if the session has not yet been closed
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$isEnded()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-isClosed"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-isClosed}{}}}
|
||||
\subsection{Method \code{isClosed()}}{
|
||||
Returns \code{FALSE} if the session has not yet been closed
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$isClosed()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-close"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-close}{}}}
|
||||
\subsection{Method \code{close()}}{
|
||||
Closes the session
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$close()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-cycleStartAction"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-cycleStartAction}{}}}
|
||||
\subsection{Method \code{cycleStartAction()}}{
|
||||
Unsophisticated mock implementation that merely invokes
|
||||
the given callback immediately.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$cycleStartAction(callback)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{callback}}{The callback ato be invoked.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-fileUrl"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-fileUrl}{}}}
|
||||
\subsection{Method \code{fileUrl()}}{
|
||||
Base64-encode the given file. Needed for image rendering.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$fileUrl(name, file, contentType = "application/octet-stream")}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{name}}{Not used}
|
||||
|
||||
\item{\code{file}}{The file to be encoded}
|
||||
|
||||
\item{\code{contentType}}{The content type of the base64-encoded string}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-setInputs"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-setInputs}{}}}
|
||||
\subsection{Method \code{setInputs()}}{
|
||||
Sets reactive values associated with the \code{session$inputs} object
|
||||
and flushes the reactives.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$setInputs(...)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{...}}{The inputs to set.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
\subsection{Examples}{
|
||||
\if{html}{\out{<div class="r example copy">}}
|
||||
\preformatted{s <- MockShinySession$new()
|
||||
s$setInputs(x=1, y=2)
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-.scheduleTask"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-.scheduleTask}{}}}
|
||||
\subsection{Method \code{.scheduleTask()}}{
|
||||
An internal method which shouldn't be used by others.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$.scheduleTask(millis, callback)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{millis}}{The number of milliseconds on which to schedule a callback}
|
||||
|
||||
\item{\code{callback}}{The function to schedule}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-elapse"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-elapse}{}}}
|
||||
\subsection{Method \code{elapse()}}{
|
||||
Simulate the passing of time by the given number of milliseconds.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$elapse(millis)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{millis}}{The number of milliseconds to advance time.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-.now"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-.now}{}}}
|
||||
\subsection{Method \code{.now()}}{
|
||||
An internal method which shouldn't be used by others.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$.now()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-defineOutput"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-defineOutput}{}}}
|
||||
\subsection{Method \code{defineOutput()}}{
|
||||
An internal method which shouldn't be used by others.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$defineOutput(name, func, label)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{name}}{The name of the output}
|
||||
|
||||
\item{\code{func}}{The render definition}
|
||||
|
||||
\item{\code{label}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-getOutput"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getOutput}{}}}
|
||||
\subsection{Method \code{getOutput()}}{
|
||||
An internal method which shouldn't be used by others.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$getOutput(name)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{name}}{The name of the output}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-registerDataObj"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-registerDataObj}{}}}
|
||||
\subsection{Method \code{registerDataObj()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$registerDataObj(name, data, filterFunc)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{name}}{Not used}
|
||||
|
||||
\item{\code{data}}{Not used}
|
||||
|
||||
\item{\code{filterFunc}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-allowReconnect"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-allowReconnect}{}}}
|
||||
\subsection{Method \code{allowReconnect()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$allowReconnect(value)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{value}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-reload"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-reload}{}}}
|
||||
\subsection{Method \code{reload()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$reload()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-resetBrush"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-resetBrush}{}}}
|
||||
\subsection{Method \code{resetBrush()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$resetBrush(brushId)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{brushId}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-sendCustomMessage"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-sendCustomMessage}{}}}
|
||||
\subsection{Method \code{sendCustomMessage()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$sendCustomMessage(type, message)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{type}}{Not used}
|
||||
|
||||
\item{\code{message}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-sendBinaryMessage"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-sendBinaryMessage}{}}}
|
||||
\subsection{Method \code{sendBinaryMessage()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$sendBinaryMessage(type, message)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{type}}{Not used}
|
||||
|
||||
\item{\code{message}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-sendInputMessage"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-sendInputMessage}{}}}
|
||||
\subsection{Method \code{sendInputMessage()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$sendInputMessage(inputId, message)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{inputId}}{Not used}
|
||||
|
||||
\item{\code{message}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-setBookmarkExclude"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-setBookmarkExclude}{}}}
|
||||
\subsection{Method \code{setBookmarkExclude()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$setBookmarkExclude(names)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{names}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-getBookmarkExclude"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getBookmarkExclude}{}}}
|
||||
\subsection{Method \code{getBookmarkExclude()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$getBookmarkExclude()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-onBookmark"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onBookmark}{}}}
|
||||
\subsection{Method \code{onBookmark()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$onBookmark(fun)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{fun}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-onBookmarked"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onBookmarked}{}}}
|
||||
\subsection{Method \code{onBookmarked()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$onBookmarked(fun)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{fun}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-doBookmark"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-doBookmark}{}}}
|
||||
\subsection{Method \code{doBookmark()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$doBookmark()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-onRestore"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onRestore}{}}}
|
||||
\subsection{Method \code{onRestore()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$onRestore(fun)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{fun}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-onRestored"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-onRestored}{}}}
|
||||
\subsection{Method \code{onRestored()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$onRestored(fun)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{fun}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-exportTestValues"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-exportTestValues}{}}}
|
||||
\subsection{Method \code{exportTestValues()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$exportTestValues()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-getTestSnapshotUrl"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getTestSnapshotUrl}{}}}
|
||||
\subsection{Method \code{getTestSnapshotUrl()}}{
|
||||
No-op
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$getTestSnapshotUrl(
|
||||
input = TRUE,
|
||||
output = TRUE,
|
||||
export = TRUE,
|
||||
format = "json"
|
||||
)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{input}}{Not used}
|
||||
|
||||
\item{\code{output}}{Not used}
|
||||
|
||||
\item{\code{export}}{Not used}
|
||||
|
||||
\item{\code{format}}{Not used}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-ns"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-ns}{}}}
|
||||
\subsection{Method \code{ns()}}{
|
||||
Returns the given id prefixed by this namespace's id.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$ns(id)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{id}}{The id to modify.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-flushReact"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-flushReact}{}}}
|
||||
\subsection{Method \code{flushReact()}}{
|
||||
Trigger a reactive flush right now.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$flushReact()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-makeScope"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-makeScope}{}}}
|
||||
\subsection{Method \code{makeScope()}}{
|
||||
Create and return a namespace-specific session proxy.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$makeScope(namespace)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{namespace}}{Character vector indicating a namespace.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-setEnv"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-setEnv}{}}}
|
||||
\subsection{Method \code{setEnv()}}{
|
||||
Set the environment associated with a testServer() call.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$setEnv(env)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{env}}{The environment to retain.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-setReturned"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-setReturned}{}}}
|
||||
\subsection{Method \code{setReturned()}}{
|
||||
Set the value returned by the module call and proactively flush.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$setReturned(value)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{value}}{The value returned from the module}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-getReturned"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getReturned}{}}}
|
||||
\subsection{Method \code{getReturned()}}{
|
||||
Get the value returned by the module call.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$getReturned()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-genId"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-genId}{}}}
|
||||
\subsection{Method \code{genId()}}{
|
||||
Return a distinct character identifier for use as a proxy
|
||||
namespace.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$genId()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-clone"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-clone}{}}}
|
||||
\subsection{Method \code{clone()}}{
|
||||
The objects of this class are cloneable with this method.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$clone(deep = FALSE)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{deep}}{Whether to make a deep clone.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,9 @@
|
||||
\alias{NS}
|
||||
\alias{ns.sep}
|
||||
\title{Namespaced IDs for inputs/outputs}
|
||||
\format{An object of class \code{character} of length 1.}
|
||||
\format{
|
||||
An object of class \code{character} of length 1.
|
||||
}
|
||||
\usage{
|
||||
NS(namespace, id = NULL)
|
||||
|
||||
|
||||
224
man/Progress.Rd
224
man/Progress.Rd
@@ -1,47 +1,16 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/progress.R
|
||||
\docType{data}
|
||||
\name{Progress}
|
||||
\alias{Progress}
|
||||
\title{Reporting progress (object-oriented API)}
|
||||
\arguments{
|
||||
\item{session}{The Shiny session object, as provided by
|
||||
\code{shinyServer} to the server function.}
|
||||
|
||||
\item{min}{The value that represents the starting point of the
|
||||
progress bar. Must be less than \code{max}.}
|
||||
|
||||
\item{max}{The value that represents the end of the progress bar.
|
||||
Must be greater than \code{min}.}
|
||||
|
||||
\item{message}{A single-element character vector; the message to be
|
||||
displayed to the user, or \code{NULL} to hide the current message
|
||||
(if any).}
|
||||
|
||||
\item{detail}{A single-element character vector; the detail message
|
||||
to be displayed to the user, or \code{NULL} to hide the current
|
||||
detail message (if any). The detail message will be shown with a
|
||||
de-emphasized appearance relative to \code{message}.}
|
||||
|
||||
\item{value}{A numeric value at which to set
|
||||
the progress bar, relative to \code{min} and \code{max}.}
|
||||
|
||||
\item{style}{Progress display style. If \code{"notification"} (the default),
|
||||
the progress indicator will show using Shiny's notification API. If
|
||||
\code{"old"}, use the same HTML and CSS used in Shiny 0.13.2 and below
|
||||
(this is for backward-compatibility).}
|
||||
|
||||
\item{amount}{Single-element numeric vector; the value at which to set
|
||||
the progress bar, relative to \code{min} and \code{max}.
|
||||
\code{NULL} hides the progress bar, if it is currently visible.}
|
||||
|
||||
\item{amount}{For the \code{inc()} method, a numeric value to increment the
|
||||
progress bar.}
|
||||
}
|
||||
\description{
|
||||
Reports progress to the user during long-running operations.
|
||||
Reporting progress (object-oriented API)
|
||||
|
||||
Reporting progress (object-oriented API)
|
||||
}
|
||||
\details{
|
||||
Reports progress to the user during long-running operations.
|
||||
|
||||
This package exposes two distinct programming APIs for working with
|
||||
progress. \code{\link[=withProgress]{withProgress()}} and \code{\link[=setProgress]{setProgress()}}
|
||||
together provide a simple function-based interface, while the
|
||||
@@ -59,26 +28,6 @@ CSS), you can use \code{style="old"} each time you call
|
||||
\code{Progress$new} is called, you can instead call
|
||||
\code{\link[=shinyOptions]{shinyOptions(progress.style="old")}} just once, inside the server
|
||||
function.
|
||||
|
||||
\strong{Methods}
|
||||
\describe{
|
||||
\item{\code{initialize(session, min = 0, max = 1)}}{
|
||||
Creates a new progress panel (but does not display it).
|
||||
}
|
||||
\item{\code{set(value = NULL, message = NULL, detail = NULL)}}{
|
||||
Updates the progress panel. When called the first time, the
|
||||
progress panel is displayed.
|
||||
}
|
||||
\item{\code{inc(amount = 0.1, message = NULL, detail = NULL)}}{
|
||||
Like \code{set}, this updates the progress panel. The difference is
|
||||
that \code{inc} increases the progress bar by \code{amount}, instead
|
||||
of setting it to a specific value.
|
||||
}
|
||||
\item{\code{close()}}{
|
||||
Removes the progress panel. Future calls to \code{set} and
|
||||
\code{close} will be ignored.
|
||||
}
|
||||
}
|
||||
}
|
||||
\examples{
|
||||
## Only run examples in interactive R sessions
|
||||
@@ -110,4 +59,165 @@ shinyApp(ui, server)
|
||||
\seealso{
|
||||
\code{\link[=withProgress]{withProgress()}}
|
||||
}
|
||||
\keyword{datasets}
|
||||
\section{Methods}{
|
||||
\subsection{Public methods}{
|
||||
\itemize{
|
||||
\item \href{#method-new}{\code{Progress$new()}}
|
||||
\item \href{#method-set}{\code{Progress$set()}}
|
||||
\item \href{#method-inc}{\code{Progress$inc()}}
|
||||
\item \href{#method-getMin}{\code{Progress$getMin()}}
|
||||
\item \href{#method-getMax}{\code{Progress$getMax()}}
|
||||
\item \href{#method-getValue}{\code{Progress$getValue()}}
|
||||
\item \href{#method-close}{\code{Progress$close()}}
|
||||
\item \href{#method-clone}{\code{Progress$clone()}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-new"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-new}{}}}
|
||||
\subsection{Method \code{new()}}{
|
||||
Creates a new progress panel (but does not display it).
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{Progress$new(
|
||||
session = getDefaultReactiveDomain(),
|
||||
min = 0,
|
||||
max = 1,
|
||||
style = getShinyOption("progress.style", default = "notification")
|
||||
)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{session}}{The Shiny session object, as provided by \code{shinyServer} to
|
||||
the server function.}
|
||||
|
||||
\item{\code{min}}{The value that represents the starting point of the progress
|
||||
bar. Must be less than \code{max}.}
|
||||
|
||||
\item{\code{max}}{The value that represents the end of the progress bar. Must be
|
||||
greater than \code{min}.}
|
||||
|
||||
\item{\code{style}}{Progress display style. If \code{"notification"} (the default),
|
||||
the progress indicator will show using Shiny's notification API. If
|
||||
\code{"old"}, use the same HTML and CSS used in Shiny 0.13.2 and below (this
|
||||
is for backward-compatibility).}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-set"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-set}{}}}
|
||||
\subsection{Method \code{set()}}{
|
||||
Updates the progress panel. When called the first time, the
|
||||
progress panel is displayed.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{Progress$set(value = NULL, message = NULL, detail = NULL)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{value}}{Single-element numeric vector; the value at which to set the
|
||||
progress bar, relative to \code{min} and \code{max}. \code{NULL} hides the progress
|
||||
bar, if it is currently visible.}
|
||||
|
||||
\item{\code{message}}{A single-element character vector; the message to be
|
||||
displayed to the user, or \code{NULL} to hide the current message (if any).}
|
||||
|
||||
\item{\code{detail}}{A single-element character vector; the detail message to be
|
||||
displayed to the user, or \code{NULL} to hide the current detail message (if
|
||||
any). The detail message will be shown with a de-emphasized appearance
|
||||
relative to \code{message}.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-inc"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-inc}{}}}
|
||||
\subsection{Method \code{inc()}}{
|
||||
Like \code{set}, this updates the progress panel. The difference
|
||||
is that \code{inc} increases the progress bar by \code{amount}, instead of
|
||||
setting it to a specific value.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{Progress$inc(amount = 0.1, message = NULL, detail = NULL)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{amount}}{For the \code{inc()} method, a numeric value to increment the
|
||||
progress bar.}
|
||||
|
||||
\item{\code{message}}{A single-element character vector; the message to be
|
||||
displayed to the user, or \code{NULL} to hide the current message (if any).}
|
||||
|
||||
\item{\code{detail}}{A single-element character vector; the detail message to be
|
||||
displayed to the user, or \code{NULL} to hide the current detail message (if
|
||||
any). The detail message will be shown with a de-emphasized appearance
|
||||
relative to \code{message}.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-getMin"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getMin}{}}}
|
||||
\subsection{Method \code{getMin()}}{
|
||||
Returns the minimum value.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{Progress$getMin()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-getMax"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getMax}{}}}
|
||||
\subsection{Method \code{getMax()}}{
|
||||
Returns the maximum value.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{Progress$getMax()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-getValue"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-getValue}{}}}
|
||||
\subsection{Method \code{getValue()}}{
|
||||
Returns the current value.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{Progress$getValue()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-close"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-close}{}}}
|
||||
\subsection{Method \code{close()}}{
|
||||
Removes the progress panel. Future calls to \code{set} and
|
||||
\code{close} will be ignored.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{Progress$close()}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
}
|
||||
\if{html}{\out{<hr>}}
|
||||
\if{html}{\out{<a id="method-clone"></a>}}
|
||||
\if{latex}{\out{\hypertarget{method-clone}{}}}
|
||||
\subsection{Method \code{clone()}}{
|
||||
The objects of this class are cloneable with this method.
|
||||
\subsection{Usage}{
|
||||
\if{html}{\out{<div class="r">}}\preformatted{Progress$clone(deep = FALSE)}\if{html}{\out{</div>}}
|
||||
}
|
||||
|
||||
\subsection{Arguments}{
|
||||
\if{html}{\out{<div class="arguments">}}
|
||||
\describe{
|
||||
\item{\code{deep}}{Whether to make a deep clone.}
|
||||
}
|
||||
\if{html}{\out{</div>}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,30 @@
|
||||
\alias{fixedPanel}
|
||||
\title{Panel with absolute positioning}
|
||||
\usage{
|
||||
absolutePanel(..., top = NULL, left = NULL, right = NULL,
|
||||
bottom = NULL, width = NULL, height = NULL, draggable = FALSE,
|
||||
fixed = FALSE, cursor = c("auto", "move", "default", "inherit"))
|
||||
absolutePanel(
|
||||
...,
|
||||
top = NULL,
|
||||
left = NULL,
|
||||
right = NULL,
|
||||
bottom = NULL,
|
||||
width = NULL,
|
||||
height = NULL,
|
||||
draggable = FALSE,
|
||||
fixed = FALSE,
|
||||
cursor = c("auto", "move", "default", "inherit")
|
||||
)
|
||||
|
||||
fixedPanel(..., top = NULL, left = NULL, right = NULL,
|
||||
bottom = NULL, width = NULL, height = NULL, draggable = FALSE,
|
||||
cursor = c("auto", "move", "default", "inherit"))
|
||||
fixedPanel(
|
||||
...,
|
||||
top = NULL,
|
||||
left = NULL,
|
||||
right = NULL,
|
||||
bottom = NULL,
|
||||
width = NULL,
|
||||
height = NULL,
|
||||
draggable = FALSE,
|
||||
cursor = c("auto", "move", "default", "inherit")
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{Attributes (named arguments) or children (unnamed arguments) that
|
||||
@@ -53,7 +70,7 @@ An HTML element or list of elements.
|
||||
Creates a panel whose contents are absolutely positioned.
|
||||
}
|
||||
\details{
|
||||
The \code{absolutePanel} function creates a \code{<div>} tag whose CSS
|
||||
The \code{absolutePanel} function creates a \verb{<div>} tag whose CSS
|
||||
position is set to \code{absolute} (or fixed if \code{fixed = TRUE}). The way
|
||||
absolute positioning works in HTML is that absolute coordinates are specified
|
||||
relative to its nearest parent element whose position is not set to
|
||||
|
||||
@@ -43,7 +43,7 @@ if (interactive()) {
|
||||
|
||||
ui <- fluidPage(
|
||||
sliderInput("obs", "Number of observations", 0, 1000, 500),
|
||||
actionButton("goButton", "Go!"),
|
||||
actionButton("goButton", "Go!", class = "btn-success"),
|
||||
plotOutput("distPlot")
|
||||
)
|
||||
|
||||
@@ -63,17 +63,28 @@ shinyApp(ui, server)
|
||||
|
||||
}
|
||||
|
||||
## Example of adding extra class values
|
||||
actionButton("largeButton", "Large Primary Button", class = "btn-primary btn-lg")
|
||||
actionLink("infoLink", "Information Link", class = "btn-info")
|
||||
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=observeEvent]{observeEvent()}} and \code{\link[=eventReactive]{eventReactive()}}
|
||||
|
||||
Other input elements: \code{\link{checkboxGroupInput}},
|
||||
\code{\link{checkboxInput}}, \code{\link{dateInput}},
|
||||
\code{\link{dateRangeInput}}, \code{\link{fileInput}},
|
||||
\code{\link{numericInput}}, \code{\link{passwordInput}},
|
||||
\code{\link{radioButtons}}, \code{\link{selectInput}},
|
||||
\code{\link{sliderInput}}, \code{\link{submitButton}},
|
||||
\code{\link{textAreaInput}}, \code{\link{textInput}},
|
||||
\code{\link{varSelectInput}}
|
||||
Other input elements:
|
||||
\code{\link{checkboxGroupInput}()},
|
||||
\code{\link{checkboxInput}()},
|
||||
\code{\link{dateInput}()},
|
||||
\code{\link{dateRangeInput}()},
|
||||
\code{\link{fileInput}()},
|
||||
\code{\link{numericInput}()},
|
||||
\code{\link{passwordInput}()},
|
||||
\code{\link{radioButtons}()},
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
}
|
||||
\concept{input elements}
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
\alias{bookmarkButton}
|
||||
\title{Create a button for bookmarking/sharing}
|
||||
\usage{
|
||||
bookmarkButton(label = "Bookmark...", icon = shiny::icon("link", lib =
|
||||
"glyphicon"),
|
||||
bookmarkButton(
|
||||
label = "Bookmark...",
|
||||
icon = shiny::icon("link", lib = "glyphicon"),
|
||||
title = "Bookmark this application's state and get a URL for sharing.",
|
||||
..., id = "._bookmark_")
|
||||
...,
|
||||
id = "._bookmark_"
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{label}{The contents of the button or link--usually a text label, but
|
||||
|
||||
@@ -4,9 +4,17 @@
|
||||
\alias{brushOpts}
|
||||
\title{Create an object representing brushing options}
|
||||
\usage{
|
||||
brushOpts(id = NULL, fill = "#9cf", stroke = "#036",
|
||||
opacity = 0.25, delay = 300, delayType = c("debounce", "throttle"),
|
||||
clip = TRUE, direction = c("xy", "x", "y"), resetOnNew = FALSE)
|
||||
brushOpts(
|
||||
id = NULL,
|
||||
fill = "#9cf",
|
||||
stroke = "#036",
|
||||
opacity = 0.25,
|
||||
delay = 300,
|
||||
delayType = c("debounce", "throttle"),
|
||||
clip = TRUE,
|
||||
direction = c("xy", "x", "y"),
|
||||
resetOnNew = FALSE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{id}{Input value name. For example, if the value is \code{"plot_brush"},
|
||||
|
||||
@@ -4,8 +4,15 @@
|
||||
\alias{brushedPoints}
|
||||
\title{Find rows of data that are selected by a brush}
|
||||
\usage{
|
||||
brushedPoints(df, brush, xvar = NULL, yvar = NULL, panelvar1 = NULL,
|
||||
panelvar2 = NULL, allRows = FALSE)
|
||||
brushedPoints(
|
||||
df,
|
||||
brush,
|
||||
xvar = NULL,
|
||||
yvar = NULL,
|
||||
panelvar1 = NULL,
|
||||
panelvar2 = NULL,
|
||||
allRows = FALSE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{df}{A data frame from which to select rows.}
|
||||
|
||||
124
man/builder.Rd
124
man/builder.Rd
@@ -1,124 +0,0 @@
|
||||
\name{builder}
|
||||
\alias{builder}
|
||||
\alias{tags}
|
||||
\alias{p}
|
||||
\alias{h1}
|
||||
\alias{h2}
|
||||
\alias{h3}
|
||||
\alias{h4}
|
||||
\alias{h5}
|
||||
\alias{h6}
|
||||
\alias{a}
|
||||
\alias{br}
|
||||
\alias{div}
|
||||
\alias{span}
|
||||
\alias{pre}
|
||||
\alias{code}
|
||||
\alias{img}
|
||||
\alias{strong}
|
||||
\alias{em}
|
||||
\alias{hr}
|
||||
\title{HTML Builder Functions}
|
||||
\usage{
|
||||
tags
|
||||
|
||||
p(..., .noWS = NULL)
|
||||
|
||||
h1(..., .noWS = NULL)
|
||||
|
||||
h2(..., .noWS = NULL)
|
||||
|
||||
h3(..., .noWS = NULL)
|
||||
|
||||
h4(..., .noWS = NULL)
|
||||
|
||||
h5(..., .noWS = NULL)
|
||||
|
||||
h6(..., .noWS = NULL)
|
||||
|
||||
a(..., .noWS = NULL)
|
||||
|
||||
br(..., .noWS = NULL)
|
||||
|
||||
div(..., .noWS = NULL)
|
||||
|
||||
span(..., .noWS = NULL)
|
||||
|
||||
pre(..., .noWS = NULL)
|
||||
|
||||
code(..., .noWS = NULL)
|
||||
|
||||
img(..., .noWS = NULL)
|
||||
|
||||
strong(..., .noWS = NULL)
|
||||
|
||||
em(..., .noWS = NULL)
|
||||
|
||||
hr(..., .noWS = NULL)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{Attributes and children of the element. Named arguments become
|
||||
attributes, and positional arguments become children. Valid children are
|
||||
tags, single-character character vectors (which become text nodes), raw
|
||||
HTML (see \code{\link{HTML}}), and \code{html_dependency} objects. You can
|
||||
also pass lists that contain tags, text nodes, or HTML. To use boolean
|
||||
attributes, use a named argument with a \code{NA} value. (see example)}
|
||||
|
||||
\item{.noWS}{A character vector used to omit some of the whitespace that
|
||||
would normally be written around this tag. Valid options include
|
||||
\code{before}, \code{after}, \code{outside}, \code{after-begin}, and
|
||||
\code{before-end}. Any number of these options can be specified.}
|
||||
}
|
||||
\description{
|
||||
Simple functions for constructing HTML documents.
|
||||
}
|
||||
\details{
|
||||
The \code{tags} environment contains convenience functions for all valid
|
||||
HTML5 tags. To generate tags that are not part of the HTML5 specification,
|
||||
you can use the \code{\link{tag}()} function.
|
||||
|
||||
Dedicated functions are available for the most common HTML tags that do not
|
||||
conflict with common R functions.
|
||||
|
||||
The result from these functions is a tag object, which can be converted using
|
||||
\code{\link{as.character}()}.
|
||||
}
|
||||
\examples{
|
||||
doc <- tags$html(
|
||||
tags$head(
|
||||
tags$title('My first page')
|
||||
),
|
||||
tags$body(
|
||||
h1('My first heading'),
|
||||
p('My first paragraph, with some ',
|
||||
strong('bold'),
|
||||
' text.'),
|
||||
div(id='myDiv', class='simpleDiv',
|
||||
'Here is a div with some attributes.')
|
||||
)
|
||||
)
|
||||
cat(as.character(doc))
|
||||
|
||||
# create an html5 audio tag with controls.
|
||||
# controls is a boolean attributes
|
||||
audio_tag <- tags$audio(
|
||||
controls = NA,
|
||||
tags$source(
|
||||
src = "myfile.wav",
|
||||
type = "audio/wav"
|
||||
)
|
||||
)
|
||||
cat(as.character(audio_tag))
|
||||
|
||||
# suppress the whitespace between tags
|
||||
oneline <- tags$span(
|
||||
tags$strong("I'm strong", .noWS="outside")
|
||||
)
|
||||
cat(as.character(oneline))
|
||||
}
|
||||
\references{
|
||||
\itemize{
|
||||
\item W3C html specification about boolean attributes
|
||||
\url{https://www.w3.org/TR/html5/infrastructure.html#sec-boolean-attributes}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/modules.R
|
||||
\name{callModule}
|
||||
\alias{callModule}
|
||||
\title{Invoke a Shiny module}
|
||||
\usage{
|
||||
callModule(module, id, ..., session = getDefaultReactiveDomain())
|
||||
}
|
||||
\arguments{
|
||||
\item{module}{A Shiny module server function}
|
||||
|
||||
\item{id}{An ID string that corresponds with the ID used to call the module's
|
||||
UI function}
|
||||
|
||||
\item{...}{Additional parameters to pass to module server function}
|
||||
|
||||
\item{session}{Session from which to make a child scope (the default should
|
||||
almost always be used)}
|
||||
}
|
||||
\value{
|
||||
The return value, if any, from executing the module server function
|
||||
}
|
||||
\description{
|
||||
Shiny's module feature lets you break complicated UI and server logic into
|
||||
smaller, self-contained pieces. Compared to large monolithic Shiny apps,
|
||||
modules are easier to reuse and easier to reason about. See the article at
|
||||
\url{http://shiny.rstudio.com/articles/modules.html} to learn more.
|
||||
}
|
||||
\seealso{
|
||||
\url{http://shiny.rstudio.com/articles/modules.html}
|
||||
}
|
||||
@@ -4,9 +4,16 @@
|
||||
\alias{checkboxGroupInput}
|
||||
\title{Checkbox Group Input Control}
|
||||
\usage{
|
||||
checkboxGroupInput(inputId, label, choices = NULL, selected = NULL,
|
||||
inline = FALSE, width = NULL, choiceNames = NULL,
|
||||
choiceValues = NULL)
|
||||
checkboxGroupInput(
|
||||
inputId,
|
||||
label,
|
||||
choices = NULL,
|
||||
selected = NULL,
|
||||
inline = FALSE,
|
||||
width = NULL,
|
||||
choiceNames = NULL,
|
||||
choiceValues = NULL
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{The \code{input} slot that will be used to access the value.}
|
||||
@@ -93,13 +100,20 @@ shinyApp(ui, server)
|
||||
\seealso{
|
||||
\code{\link[=checkboxInput]{checkboxInput()}}, \code{\link[=updateCheckboxGroupInput]{updateCheckboxGroupInput()}}
|
||||
|
||||
Other input elements: \code{\link{actionButton}},
|
||||
\code{\link{checkboxInput}}, \code{\link{dateInput}},
|
||||
\code{\link{dateRangeInput}}, \code{\link{fileInput}},
|
||||
\code{\link{numericInput}}, \code{\link{passwordInput}},
|
||||
\code{\link{radioButtons}}, \code{\link{selectInput}},
|
||||
\code{\link{sliderInput}}, \code{\link{submitButton}},
|
||||
\code{\link{textAreaInput}}, \code{\link{textInput}},
|
||||
\code{\link{varSelectInput}}
|
||||
Other input elements:
|
||||
\code{\link{actionButton}()},
|
||||
\code{\link{checkboxInput}()},
|
||||
\code{\link{dateInput}()},
|
||||
\code{\link{dateRangeInput}()},
|
||||
\code{\link{fileInput}()},
|
||||
\code{\link{numericInput}()},
|
||||
\code{\link{passwordInput}()},
|
||||
\code{\link{radioButtons}()},
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
}
|
||||
\concept{input elements}
|
||||
|
||||
@@ -45,13 +45,20 @@ shinyApp(ui, server)
|
||||
\seealso{
|
||||
\code{\link[=checkboxGroupInput]{checkboxGroupInput()}}, \code{\link[=updateCheckboxInput]{updateCheckboxInput()}}
|
||||
|
||||
Other input elements: \code{\link{actionButton}},
|
||||
\code{\link{checkboxGroupInput}},
|
||||
\code{\link{dateInput}}, \code{\link{dateRangeInput}},
|
||||
\code{\link{fileInput}}, \code{\link{numericInput}},
|
||||
\code{\link{passwordInput}}, \code{\link{radioButtons}},
|
||||
\code{\link{selectInput}}, \code{\link{sliderInput}},
|
||||
\code{\link{submitButton}}, \code{\link{textAreaInput}},
|
||||
\code{\link{textInput}}, \code{\link{varSelectInput}}
|
||||
Other input elements:
|
||||
\code{\link{actionButton}()},
|
||||
\code{\link{checkboxGroupInput}()},
|
||||
\code{\link{dateInput}()},
|
||||
\code{\link{dateRangeInput}()},
|
||||
\code{\link{fileInput}()},
|
||||
\code{\link{numericInput}()},
|
||||
\code{\link{passwordInput}()},
|
||||
\code{\link{radioButtons}()},
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
}
|
||||
\concept{input elements}
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
\alias{createRenderFunction}
|
||||
\title{Implement render functions}
|
||||
\usage{
|
||||
createRenderFunction(func, transform = function(value, session, name,
|
||||
...) value, outputFunc = NULL, outputArgs = NULL)
|
||||
createRenderFunction(
|
||||
func,
|
||||
transform = function(value, session, name, ...) value,
|
||||
outputFunc = NULL,
|
||||
outputArgs = NULL
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{func}{A function without parameters, that returns user data. If the
|
||||
|
||||
@@ -4,10 +4,21 @@
|
||||
\alias{dateInput}
|
||||
\title{Create date input}
|
||||
\usage{
|
||||
dateInput(inputId, label, value = NULL, min = NULL, max = NULL,
|
||||
format = "yyyy-mm-dd", startview = "month", weekstart = 0,
|
||||
language = "en", width = NULL, autoclose = TRUE,
|
||||
datesdisabled = NULL, daysofweekdisabled = NULL)
|
||||
dateInput(
|
||||
inputId,
|
||||
label,
|
||||
value = NULL,
|
||||
min = NULL,
|
||||
max = NULL,
|
||||
format = "yyyy-mm-dd",
|
||||
startview = "month",
|
||||
weekstart = 0,
|
||||
language = "en",
|
||||
width = NULL,
|
||||
autoclose = TRUE,
|
||||
datesdisabled = NULL,
|
||||
daysofweekdisabled = NULL
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{The \code{input} slot that will be used to access the value.}
|
||||
@@ -120,14 +131,20 @@ shinyApp(ui, server = function(input, output) { })
|
||||
\seealso{
|
||||
\code{\link[=dateRangeInput]{dateRangeInput()}}, \code{\link[=updateDateInput]{updateDateInput()}}
|
||||
|
||||
Other input elements: \code{\link{actionButton}},
|
||||
\code{\link{checkboxGroupInput}},
|
||||
\code{\link{checkboxInput}},
|
||||
\code{\link{dateRangeInput}}, \code{\link{fileInput}},
|
||||
\code{\link{numericInput}}, \code{\link{passwordInput}},
|
||||
\code{\link{radioButtons}}, \code{\link{selectInput}},
|
||||
\code{\link{sliderInput}}, \code{\link{submitButton}},
|
||||
\code{\link{textAreaInput}}, \code{\link{textInput}},
|
||||
\code{\link{varSelectInput}}
|
||||
Other input elements:
|
||||
\code{\link{actionButton}()},
|
||||
\code{\link{checkboxGroupInput}()},
|
||||
\code{\link{checkboxInput}()},
|
||||
\code{\link{dateRangeInput}()},
|
||||
\code{\link{fileInput}()},
|
||||
\code{\link{numericInput}()},
|
||||
\code{\link{passwordInput}()},
|
||||
\code{\link{radioButtons}()},
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
}
|
||||
\concept{input elements}
|
||||
|
||||
@@ -4,10 +4,21 @@
|
||||
\alias{dateRangeInput}
|
||||
\title{Create date range input}
|
||||
\usage{
|
||||
dateRangeInput(inputId, label, start = NULL, end = NULL, min = NULL,
|
||||
max = NULL, format = "yyyy-mm-dd", startview = "month",
|
||||
weekstart = 0, language = "en", separator = " to ", width = NULL,
|
||||
autoclose = TRUE)
|
||||
dateRangeInput(
|
||||
inputId,
|
||||
label,
|
||||
start = NULL,
|
||||
end = NULL,
|
||||
min = NULL,
|
||||
max = NULL,
|
||||
format = "yyyy-mm-dd",
|
||||
startview = "month",
|
||||
weekstart = 0,
|
||||
language = "en",
|
||||
separator = " to ",
|
||||
width = NULL,
|
||||
autoclose = TRUE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{The \code{input} slot that will be used to access the value.}
|
||||
@@ -124,13 +135,20 @@ shinyApp(ui, server = function(input, output) { })
|
||||
\seealso{
|
||||
\code{\link[=dateInput]{dateInput()}}, \code{\link[=updateDateRangeInput]{updateDateRangeInput()}}
|
||||
|
||||
Other input elements: \code{\link{actionButton}},
|
||||
\code{\link{checkboxGroupInput}},
|
||||
\code{\link{checkboxInput}}, \code{\link{dateInput}},
|
||||
\code{\link{fileInput}}, \code{\link{numericInput}},
|
||||
\code{\link{passwordInput}}, \code{\link{radioButtons}},
|
||||
\code{\link{selectInput}}, \code{\link{sliderInput}},
|
||||
\code{\link{submitButton}}, \code{\link{textAreaInput}},
|
||||
\code{\link{textInput}}, \code{\link{varSelectInput}}
|
||||
Other input elements:
|
||||
\code{\link{actionButton}()},
|
||||
\code{\link{checkboxGroupInput}()},
|
||||
\code{\link{checkboxInput}()},
|
||||
\code{\link{dateInput}()},
|
||||
\code{\link{fileInput}()},
|
||||
\code{\link{numericInput}()},
|
||||
\code{\link{passwordInput}()},
|
||||
\code{\link{radioButtons}()},
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
}
|
||||
\concept{input elements}
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
\alias{throttle}
|
||||
\title{Slow down a reactive expression with debounce/throttle}
|
||||
\usage{
|
||||
debounce(r, millis, priority = 100,
|
||||
domain = getDefaultReactiveDomain())
|
||||
debounce(r, millis, priority = 100, domain = getDefaultReactiveDomain())
|
||||
|
||||
throttle(r, millis, priority = 100,
|
||||
domain = getDefaultReactiveDomain())
|
||||
throttle(r, millis, priority = 100, domain = getDefaultReactiveDomain())
|
||||
}
|
||||
\arguments{
|
||||
\item{r}{A reactive expression (that invalidates too often).}
|
||||
@@ -49,7 +47,7 @@ continually arrive from \code{r} within the time window, the debounced
|
||||
reactive will not invalidate at all. Only after the invalidations stop (or
|
||||
slow down sufficiently) will the downstream invalidation be sent.
|
||||
|
||||
\code{ooo-oo-oo---- => -----------o-}
|
||||
\verb{ooo-oo-oo---- => -----------o-}
|
||||
|
||||
(In this graphical depiction, each character represents a unit of time, and
|
||||
the time window is 3 characters.)
|
||||
@@ -61,7 +59,7 @@ continually come from \code{r} within the time window, the throttled reactive
|
||||
will invalidate regularly, at a rate equal to or slower than than the time
|
||||
window.
|
||||
|
||||
\code{ooo-oo-oo---- => o--o--o--o---}
|
||||
\verb{ooo-oo-oo---- => o--o--o--o---}
|
||||
}
|
||||
\section{Limitations}{
|
||||
|
||||
|
||||
@@ -4,9 +4,17 @@
|
||||
\alias{diskCache}
|
||||
\title{Create a disk cache object}
|
||||
\usage{
|
||||
diskCache(dir = NULL, max_size = 10 * 1024^2, max_age = Inf,
|
||||
max_n = Inf, evict = c("lru", "fifo"), destroy_on_finalize = FALSE,
|
||||
missing = key_missing(), exec_missing = FALSE, logfile = NULL)
|
||||
diskCache(
|
||||
dir = NULL,
|
||||
max_size = 10 * 1024^2,
|
||||
max_age = Inf,
|
||||
max_n = Inf,
|
||||
evict = c("lru", "fifo"),
|
||||
destroy_on_finalize = FALSE,
|
||||
missing = key_missing(),
|
||||
exec_missing = FALSE,
|
||||
logfile = NULL
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{dir}{Directory to store files for the cache. If \code{NULL} (the
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
\alias{downloadHandler}
|
||||
\title{File Downloads}
|
||||
\usage{
|
||||
downloadHandler(filename, content, contentType = NA,
|
||||
outputArgs = list())
|
||||
downloadHandler(filename, content, contentType = NA, outputArgs = list())
|
||||
}
|
||||
\arguments{
|
||||
\item{filename}{A string of the filename, including extension, that the
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
\alias{exportTestValues}
|
||||
\title{Register expressions for export in test mode}
|
||||
\usage{
|
||||
exportTestValues(..., quoted_ = FALSE, env_ = parent.frame(),
|
||||
session_ = getDefaultReactiveDomain())
|
||||
exportTestValues(
|
||||
...,
|
||||
quoted_ = FALSE,
|
||||
env_ = parent.frame(),
|
||||
session_ = getDefaultReactiveDomain()
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{Named arguments that are quoted or unquoted expressions that will
|
||||
@@ -62,7 +66,7 @@ shinyApp(
|
||||
})
|
||||
|
||||
output$values <- renderText({
|
||||
paste0("vals$x: ", vals$x, "\\ny: ", y())
|
||||
paste0("vals$x: ", vals$x, "\ny: ", y())
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
@@ -4,9 +4,15 @@
|
||||
\alias{fileInput}
|
||||
\title{File Upload Control}
|
||||
\usage{
|
||||
fileInput(inputId, label, multiple = FALSE, accept = NULL,
|
||||
width = NULL, buttonLabel = "Browse...",
|
||||
placeholder = "No file selected")
|
||||
fileInput(
|
||||
inputId,
|
||||
label,
|
||||
multiple = FALSE,
|
||||
accept = NULL,
|
||||
width = NULL,
|
||||
buttonLabel = "Browse...",
|
||||
placeholder = "No file selected"
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{The \code{input} slot that will be used to access the value.}
|
||||
@@ -17,8 +23,17 @@ fileInput(inputId, label, multiple = FALSE, accept = NULL,
|
||||
multiple files at once. \strong{Does not work on older browsers, including
|
||||
Internet Explorer 9 and earlier.}}
|
||||
|
||||
\item{accept}{A character vector of MIME types; gives the browser a hint of
|
||||
what kind of files the server is expecting.}
|
||||
\item{accept}{A character vector of "unique file type specifiers" which
|
||||
gives the browser a hint as to the type of file the server expects.
|
||||
Many browsers use this prevent the user from selecting an invalid file.
|
||||
|
||||
A unique file type specifier can be:
|
||||
\itemize{
|
||||
\item A case insensitive extension like \code{.csv} or \code{.rds}.
|
||||
\item A valid MIME type, like \code{text/plain} or \code{application/pdf}
|
||||
\item One of \verb{audio/*}, \verb{video/*}, or \verb{image/*} meaning any audio, video,
|
||||
or image type, respectively.
|
||||
}}
|
||||
|
||||
\item{width}{The width of the input, e.g. \code{'400px'}, or \code{'100\%'};
|
||||
see \code{\link[=validateCssUnit]{validateCssUnit()}}.}
|
||||
@@ -33,7 +48,7 @@ Create a file upload control that can be used to upload one or more files.
|
||||
}
|
||||
\details{
|
||||
Whenever a file upload completes, the corresponding input variable is set
|
||||
to a dataframe. See the \code{Server value} section.
|
||||
to a dataframe. See the \verb{Server value} section.
|
||||
}
|
||||
\section{Server value}{
|
||||
|
||||
@@ -60,13 +75,7 @@ if (interactive()) {
|
||||
ui <- fluidPage(
|
||||
sidebarLayout(
|
||||
sidebarPanel(
|
||||
fileInput("file1", "Choose CSV File",
|
||||
accept = c(
|
||||
"text/csv",
|
||||
"text/comma-separated-values,text/plain",
|
||||
".csv")
|
||||
),
|
||||
tags$hr(),
|
||||
fileInput("file1", "Choose CSV File", accept = ".csv"),
|
||||
checkboxInput("header", "Header", TRUE)
|
||||
),
|
||||
mainPanel(
|
||||
@@ -77,17 +86,13 @@ ui <- fluidPage(
|
||||
|
||||
server <- function(input, output) {
|
||||
output$contents <- renderTable({
|
||||
# input$file1 will be NULL initially. After the user selects
|
||||
# and uploads a file, it will be a data frame with 'name',
|
||||
# 'size', 'type', and 'datapath' columns. The 'datapath'
|
||||
# column will contain the local filenames where the data can
|
||||
# be found.
|
||||
inFile <- input$file1
|
||||
file <- input$file1
|
||||
ext <- tools::file_ext(file$datapath)
|
||||
|
||||
if (is.null(inFile))
|
||||
return(NULL)
|
||||
req(file)
|
||||
validate(need(ext == "csv", "Please upload a csv file"))
|
||||
|
||||
read.csv(inFile$datapath, header = input$header)
|
||||
read.csv(file$datapath, header = input$header)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -96,13 +101,20 @@ shinyApp(ui, server)
|
||||
|
||||
}
|
||||
\seealso{
|
||||
Other input elements: \code{\link{actionButton}},
|
||||
\code{\link{checkboxGroupInput}},
|
||||
\code{\link{checkboxInput}}, \code{\link{dateInput}},
|
||||
\code{\link{dateRangeInput}}, \code{\link{numericInput}},
|
||||
\code{\link{passwordInput}}, \code{\link{radioButtons}},
|
||||
\code{\link{selectInput}}, \code{\link{sliderInput}},
|
||||
\code{\link{submitButton}}, \code{\link{textAreaInput}},
|
||||
\code{\link{textInput}}, \code{\link{varSelectInput}}
|
||||
Other input elements:
|
||||
\code{\link{actionButton}()},
|
||||
\code{\link{checkboxGroupInput}()},
|
||||
\code{\link{checkboxInput}()},
|
||||
\code{\link{dateInput}()},
|
||||
\code{\link{dateRangeInput}()},
|
||||
\code{\link{numericInput}()},
|
||||
\code{\link{passwordInput}()},
|
||||
\code{\link{radioButtons}()},
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
}
|
||||
\concept{input elements}
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
\alias{fillPage}
|
||||
\title{Create a page that fills the window}
|
||||
\usage{
|
||||
fillPage(..., padding = 0, title = NULL, bootstrap = TRUE,
|
||||
theme = NULL)
|
||||
fillPage(..., padding = 0, title = NULL, bootstrap = TRUE, theme = NULL)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{Elements to include within the page.}
|
||||
@@ -42,11 +41,11 @@ to the size of the window.
|
||||
|
||||
For example, \code{fluidPage(plotOutput("plot", height = "100\%"))} will not
|
||||
work as expected; the plot element's effective height will be \code{0},
|
||||
because the plot's containing elements (\code{<div>} and \code{<body>}) have
|
||||
because the plot's containing elements (\verb{<div>} and \verb{<body>}) have
|
||||
\emph{automatic} height; that is, they determine their own height based on
|
||||
the height of their contained elements. However,
|
||||
\code{fillPage(plotOutput("plot", height = "100\%"))} will work because
|
||||
\code{fillPage} fixes the \code{<body>} height at 100\% of the window height.
|
||||
\code{fillPage} fixes the \verb{<body>} height at 100\% of the window height.
|
||||
|
||||
Note that \code{fillPage(plotOutput("plot"))} will not cause the plot to fill
|
||||
the page. Like most Shiny output widgets, \code{plotOutput}'s default height
|
||||
@@ -83,9 +82,13 @@ fillPage(
|
||||
)
|
||||
}
|
||||
\seealso{
|
||||
Other layout functions: \code{\link{fixedPage}},
|
||||
\code{\link{flowLayout}}, \code{\link{fluidPage}},
|
||||
\code{\link{navbarPage}}, \code{\link{sidebarLayout}},
|
||||
\code{\link{splitLayout}}, \code{\link{verticalLayout}}
|
||||
Other layout functions:
|
||||
\code{\link{fixedPage}()},
|
||||
\code{\link{flowLayout}()},
|
||||
\code{\link{fluidPage}()},
|
||||
\code{\link{navbarPage}()},
|
||||
\code{\link{sidebarLayout}()},
|
||||
\code{\link{splitLayout}()},
|
||||
\code{\link{verticalLayout}()}
|
||||
}
|
||||
\concept{layout functions}
|
||||
|
||||
@@ -67,9 +67,13 @@ shinyApp(ui, server = function(input, output) { })
|
||||
\seealso{
|
||||
\code{\link[=column]{column()}}
|
||||
|
||||
Other layout functions: \code{\link{fillPage}},
|
||||
\code{\link{flowLayout}}, \code{\link{fluidPage}},
|
||||
\code{\link{navbarPage}}, \code{\link{sidebarLayout}},
|
||||
\code{\link{splitLayout}}, \code{\link{verticalLayout}}
|
||||
Other layout functions:
|
||||
\code{\link{fillPage}()},
|
||||
\code{\link{flowLayout}()},
|
||||
\code{\link{fluidPage}()},
|
||||
\code{\link{navbarPage}()},
|
||||
\code{\link{sidebarLayout}()},
|
||||
\code{\link{splitLayout}()},
|
||||
\code{\link{verticalLayout}()}
|
||||
}
|
||||
\concept{layout functions}
|
||||
|
||||
@@ -32,9 +32,13 @@ shinyApp(ui, server = function(input, output) { })
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
Other layout functions: \code{\link{fillPage}},
|
||||
\code{\link{fixedPage}}, \code{\link{fluidPage}},
|
||||
\code{\link{navbarPage}}, \code{\link{sidebarLayout}},
|
||||
\code{\link{splitLayout}}, \code{\link{verticalLayout}}
|
||||
Other layout functions:
|
||||
\code{\link{fillPage}()},
|
||||
\code{\link{fixedPage}()},
|
||||
\code{\link{fluidPage}()},
|
||||
\code{\link{navbarPage}()},
|
||||
\code{\link{sidebarLayout}()},
|
||||
\code{\link{splitLayout}()},
|
||||
\code{\link{verticalLayout}()}
|
||||
}
|
||||
\concept{layout functions}
|
||||
|
||||
@@ -101,9 +101,13 @@ shinyApp(ui, server = function(input, output) { })
|
||||
\seealso{
|
||||
\code{\link[=column]{column()}}
|
||||
|
||||
Other layout functions: \code{\link{fillPage}},
|
||||
\code{\link{fixedPage}}, \code{\link{flowLayout}},
|
||||
\code{\link{navbarPage}}, \code{\link{sidebarLayout}},
|
||||
\code{\link{splitLayout}}, \code{\link{verticalLayout}}
|
||||
Other layout functions:
|
||||
\code{\link{fillPage}()},
|
||||
\code{\link{fixedPage}()},
|
||||
\code{\link{flowLayout}()},
|
||||
\code{\link{navbarPage}()},
|
||||
\code{\link{sidebarLayout}()},
|
||||
\code{\link{splitLayout}()},
|
||||
\code{\link{verticalLayout}()}
|
||||
}
|
||||
\concept{layout functions}
|
||||
|
||||
@@ -9,6 +9,11 @@ getCurrentOutputInfo(session = getDefaultReactiveDomain())
|
||||
\arguments{
|
||||
\item{session}{The current Shiny session.}
|
||||
}
|
||||
\value{
|
||||
A list with information about the current output, including the
|
||||
\code{name} of the output. If no output is currently being executed, this will
|
||||
return \code{NULL}.
|
||||
}
|
||||
\description{
|
||||
Get information about the output that is currently being executed.
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ getUrlHash(session = getDefaultReactiveDomain())
|
||||
\value{
|
||||
For \code{getQueryString}, a named list. For example, the query
|
||||
string \code{?param1=value1¶m2=value2} becomes \code{list(param1 = value1, param2 = value2)}. For \code{getUrlHash}, a character vector with
|
||||
the hash (including the leading \code{#} symbol).
|
||||
the hash (including the leading \verb{#} symbol).
|
||||
}
|
||||
\description{
|
||||
Two user friendly wrappers for getting the query string and the hash
|
||||
@@ -27,7 +27,7 @@ depending on the values in the query string / hash (e.g. instead of basing
|
||||
the conditional on an input or a calculated reactive, you can base it on the
|
||||
query string). However, note that, if you're changing the query string / hash
|
||||
programatically from within the server code, you must use
|
||||
\code{updateQueryString(_yourNewQueryString_, mode = "push")}. The default
|
||||
\verb{updateQueryString(_yourNewQueryString_, mode = "push")}. The default
|
||||
\code{mode} for \code{updateQueryString} is \code{"replace"}, which doesn't
|
||||
raise any events, so any observers or reactives that depend on it will
|
||||
\emph{not} get triggered. However, if you're changing the query string / hash
|
||||
@@ -58,7 +58,7 @@ if (interactive()) {
|
||||
query <- getQueryString()
|
||||
queryText <- paste(names(query), query,
|
||||
sep = "=", collapse=", ")
|
||||
paste("Your query string is:\\n", queryText)
|
||||
paste("Your query string is:\n", queryText)
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -81,7 +81,7 @@ if (interactive()) {
|
||||
})
|
||||
output$hash <- renderText({
|
||||
hash <- getUrlHash()
|
||||
paste("Your hash is:\\n", hash)
|
||||
paste("Your hash is:\n", hash)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
@@ -4,8 +4,13 @@
|
||||
\alias{hoverOpts}
|
||||
\title{Create an object representing hover options}
|
||||
\usage{
|
||||
hoverOpts(id = NULL, delay = 300, delayType = c("debounce",
|
||||
"throttle"), clip = TRUE, nullOutside = TRUE)
|
||||
hoverOpts(
|
||||
id = NULL,
|
||||
delay = 300,
|
||||
delayType = c("debounce", "throttle"),
|
||||
clip = TRUE,
|
||||
nullOutside = TRUE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{id}{Input value name. For example, if the value is \code{"plot_hover"},
|
||||
|
||||
@@ -5,11 +5,14 @@
|
||||
\alias{uiOutput}
|
||||
\title{Create an HTML output element}
|
||||
\usage{
|
||||
htmlOutput(outputId, inline = FALSE, container = if (inline) span else
|
||||
div, ...)
|
||||
htmlOutput(
|
||||
outputId,
|
||||
inline = FALSE,
|
||||
container = if (inline) span else div,
|
||||
...
|
||||
)
|
||||
|
||||
uiOutput(outputId, inline = FALSE, container = if (inline) span else
|
||||
div, ...)
|
||||
uiOutput(outputId, inline = FALSE, container = if (inline) span else div, ...)
|
||||
}
|
||||
\arguments{
|
||||
\item{outputId}{output variable to read the value from}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
\name{htmlTemplate}
|
||||
\alias{htmlTemplate}
|
||||
\title{Process an HTML template}
|
||||
\usage{
|
||||
htmlTemplate(filename = NULL, ..., text_ = NULL, document_ = "auto")
|
||||
}
|
||||
\arguments{
|
||||
\item{filename}{Path to an HTML template file. Incompatible with
|
||||
\code{text_}.}
|
||||
|
||||
\item{...}{Variable values to use when processing the template.}
|
||||
|
||||
\item{text_}{A string to use as the template, instead of a file. Incompatible
|
||||
with \code{filename}.}
|
||||
|
||||
\item{document_}{Is this template a complete HTML document (\code{TRUE}), or
|
||||
a fragment of HTML that is to be inserted into an HTML document
|
||||
(\code{FALSE})? With \code{"auto"} (the default), auto-detect by searching
|
||||
for the string \code{"<HTML>"} within the template.}
|
||||
}
|
||||
\description{
|
||||
Process an HTML template and return a tagList object. If the template is a
|
||||
complete HTML document, then the returned object will also have class
|
||||
\code{html_document}, and can be passed to the function
|
||||
\code{\link{renderDocument}} to get the final HTML text.
|
||||
}
|
||||
\seealso{
|
||||
\code{\link{renderDocument}}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
\name{include}
|
||||
\alias{include}
|
||||
\alias{includeHTML}
|
||||
\alias{includeText}
|
||||
\alias{includeMarkdown}
|
||||
\alias{includeCSS}
|
||||
\alias{includeScript}
|
||||
\title{Include Content From a File}
|
||||
\usage{
|
||||
includeHTML(path)
|
||||
|
||||
includeText(path)
|
||||
|
||||
includeMarkdown(path)
|
||||
|
||||
includeCSS(path, ...)
|
||||
|
||||
includeScript(path, ...)
|
||||
}
|
||||
\arguments{
|
||||
\item{path}{The path of the file to be included. It is highly recommended to
|
||||
use a relative path (the base path being the Shiny application directory),
|
||||
not an absolute path.}
|
||||
|
||||
\item{...}{Any additional attributes to be applied to the generated tag.}
|
||||
}
|
||||
\description{
|
||||
Load HTML, text, or rendered Markdown from a file and turn into HTML.
|
||||
}
|
||||
\details{
|
||||
These functions provide a convenient way to include an extensive amount of
|
||||
HTML, textual, Markdown, CSS, or JavaScript content, rather than using a
|
||||
large literal R string.
|
||||
}
|
||||
\note{
|
||||
\code{includeText} escapes its contents, but does no other processing.
|
||||
This means that hard breaks and multiple spaces will be rendered as they
|
||||
usually are in HTML: as a single space character. If you are looking for
|
||||
preformatted text, wrap the call with \code{\link{pre}}, or consider using
|
||||
\code{includeMarkdown} instead.
|
||||
|
||||
The \code{includeMarkdown} function requires the \code{markdown}
|
||||
package.
|
||||
}
|
||||
@@ -7,14 +7,30 @@
|
||||
\alias{removeTab}
|
||||
\title{Dynamically insert/remove a tabPanel}
|
||||
\usage{
|
||||
insertTab(inputId, tab, target, position = c("before", "after"),
|
||||
select = FALSE, session = getDefaultReactiveDomain())
|
||||
insertTab(
|
||||
inputId,
|
||||
tab,
|
||||
target,
|
||||
position = c("before", "after"),
|
||||
select = FALSE,
|
||||
session = getDefaultReactiveDomain()
|
||||
)
|
||||
|
||||
prependTab(inputId, tab, select = FALSE, menuName = NULL,
|
||||
session = getDefaultReactiveDomain())
|
||||
prependTab(
|
||||
inputId,
|
||||
tab,
|
||||
select = FALSE,
|
||||
menuName = NULL,
|
||||
session = getDefaultReactiveDomain()
|
||||
)
|
||||
|
||||
appendTab(inputId, tab, select = FALSE, menuName = NULL,
|
||||
session = getDefaultReactiveDomain())
|
||||
appendTab(
|
||||
inputId,
|
||||
tab,
|
||||
select = FALSE,
|
||||
menuName = NULL,
|
||||
session = getDefaultReactiveDomain()
|
||||
)
|
||||
|
||||
removeTab(inputId, target, session = getDefaultReactiveDomain())
|
||||
}
|
||||
|
||||
@@ -2,17 +2,36 @@
|
||||
% Please edit documentation in R/insert-ui.R
|
||||
\name{insertUI}
|
||||
\alias{insertUI}
|
||||
\title{Insert UI objects}
|
||||
\alias{removeUI}
|
||||
\title{Insert and remove UI objects}
|
||||
\usage{
|
||||
insertUI(selector, where = c("beforeBegin", "afterBegin", "beforeEnd",
|
||||
"afterEnd"), ui, multiple = FALSE, immediate = FALSE,
|
||||
session = getDefaultReactiveDomain())
|
||||
insertUI(
|
||||
selector,
|
||||
where = c("beforeBegin", "afterBegin", "beforeEnd", "afterEnd"),
|
||||
ui,
|
||||
multiple = FALSE,
|
||||
immediate = FALSE,
|
||||
session = getDefaultReactiveDomain()
|
||||
)
|
||||
|
||||
removeUI(
|
||||
selector,
|
||||
multiple = FALSE,
|
||||
immediate = FALSE,
|
||||
session = getDefaultReactiveDomain()
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{selector}{A string that is accepted by jQuery's selector (i.e. the
|
||||
string \code{s} to be placed in a \code{$(s)} jQuery call). This selector
|
||||
will determine the element(s) relative to which you want to insert your
|
||||
UI object.}
|
||||
\item{selector}{A string that is accepted by jQuery's selector
|
||||
(i.e. the string \code{s} to be placed in a \verb{$(s)} jQuery call).
|
||||
|
||||
For \code{insertUI()} this determines the element(s) relative to which you
|
||||
want to insert your UI object. For \code{removeUI()} this determine the
|
||||
element(s) to be removed. If you want to remove a Shiny input or output,
|
||||
note that many of these are wrapped in \verb{<div>}s, so you may need to use a
|
||||
somewhat complex selector --- see the Examples below. (Alternatively, you
|
||||
could also wrap the inputs/outputs that you want to be able to remove
|
||||
easily in a \verb{<div>} with an id.)}
|
||||
|
||||
\item{where}{Where your UI object should go relative to the selector:
|
||||
\describe{
|
||||
@@ -23,8 +42,7 @@ first child}
|
||||
last child (default)}
|
||||
\item{\code{afterEnd}}{After the selector element itself}
|
||||
}
|
||||
Adapted from
|
||||
\href{https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML}{here}.}
|
||||
Adapted from \url{https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML}.}
|
||||
|
||||
\item{ui}{The UI object you want to insert. This can be anything that
|
||||
you usually put inside your apps's \code{ui} function. If you're inserting
|
||||
@@ -39,25 +57,27 @@ reference or remove it later on). If you want to insert raw html, use
|
||||
relative to all matched elements or just relative to the first
|
||||
matched element (default).}
|
||||
|
||||
\item{immediate}{Whether the UI object should be immediately inserted into
|
||||
the app when you call \code{insertUI}, or whether Shiny should wait until
|
||||
all outputs have been updated and all observers have been run (default).}
|
||||
\item{immediate}{Whether the UI object should be immediately inserted
|
||||
or removed, or whether Shiny should wait until all outputs have been
|
||||
updated and all observers have been run (default).}
|
||||
|
||||
\item{session}{The shiny session within which to call \code{insertUI}.}
|
||||
\item{session}{The shiny session. Advanced use only.}
|
||||
}
|
||||
\description{
|
||||
Insert a UI object into the app.
|
||||
}
|
||||
\details{
|
||||
This function allows you to dynamically add an arbitrarily large UI
|
||||
object into your app, whenever you want, as many times as you want.
|
||||
Unlike \code{\link[=renderUI]{renderUI()}}, the UI generated with \code{insertUI}
|
||||
is not updatable as a whole: once it's created, it stays there. Each
|
||||
new call to \code{insertUI} creates more UI objects, in addition to
|
||||
These functions allow you to dynamically add and remove arbirary UI
|
||||
into your app, whenever you want, as many times as you want.
|
||||
Unlike \code{\link[=renderUI]{renderUI()}}, the UI generated with \code{insertUI()} is persistent:
|
||||
once it's created, it stays there until removed by \code{removeUI()}. Each
|
||||
new call to \code{insertUI()} creates more UI objects, in addition to
|
||||
the ones already there (all independent from one another). To
|
||||
update a part of the UI (ex: an input object), you must use the
|
||||
appropriate \code{render} function or a customized \code{reactive}
|
||||
function. To remove any part of your UI, use \code{\link[=removeUI]{removeUI()}}.
|
||||
function.
|
||||
}
|
||||
\details{
|
||||
It's particularly useful to pair \code{removeUI} with \code{insertUI()}, but there is
|
||||
no restriction on what you can use on. Any element that can be selected
|
||||
through a jQuery selector can be removed through this function.
|
||||
}
|
||||
\examples{
|
||||
## Only run this example in interactive R sessions
|
||||
@@ -82,7 +102,24 @@ server <- function(input, output, session) {
|
||||
# Complete app with UI and server components
|
||||
shinyApp(ui, server)
|
||||
}
|
||||
|
||||
if (interactive()) {
|
||||
# Define UI
|
||||
ui <- fluidPage(
|
||||
actionButton("rmv", "Remove UI"),
|
||||
textInput("txt", "This is no longer useful")
|
||||
)
|
||||
|
||||
# Server logic
|
||||
server <- function(input, output, session) {
|
||||
observeEvent(input$rmv, {
|
||||
removeUI(
|
||||
selector = "div:has(> #txt)"
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
# Complete app with UI and server components
|
||||
shinyApp(ui, server)
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=removeUI]{removeUI()}}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,16 @@
|
||||
\alias{installExprFunction}
|
||||
\title{Install an expression as a function}
|
||||
\usage{
|
||||
installExprFunction(expr, name, eval.env = parent.frame(2),
|
||||
quoted = FALSE, assign.env = parent.frame(1),
|
||||
label = deparse(sys.call(-1)[[1]]), wrappedWithLabel = TRUE,
|
||||
..stacktraceon = FALSE)
|
||||
installExprFunction(
|
||||
expr,
|
||||
name,
|
||||
eval.env = parent.frame(2),
|
||||
quoted = FALSE,
|
||||
assign.env = parent.frame(1),
|
||||
label = deparse(sys.call(-1)[[1]]),
|
||||
wrappedWithLabel = TRUE,
|
||||
..stacktraceon = FALSE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{expr}{A quoted or unquoted expression}
|
||||
|
||||
@@ -4,13 +4,16 @@
|
||||
\alias{loadSupport}
|
||||
\title{Load an app's supporting R files}
|
||||
\usage{
|
||||
loadSupport(appDir, renv = new.env(parent = globalenv()),
|
||||
globalrenv = globalenv())
|
||||
loadSupport(
|
||||
appDir,
|
||||
renv = new.env(parent = globalenv()),
|
||||
globalrenv = globalenv()
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{appDir}{The application directory}
|
||||
|
||||
\item{renv}{The environmeny in which the files in the \code{R/} directory should
|
||||
\item{renv}{The environmeny in which the files in the \verb{R/} directory should
|
||||
be evaluated.}
|
||||
|
||||
\item{globalrenv}{The environment in which \code{global.R} should be evaluated. If
|
||||
@@ -18,19 +21,19 @@ be evaluated.}
|
||||
}
|
||||
\description{
|
||||
Loads all of the supporting R files of a Shiny application. Specifically,
|
||||
this function loads any top-level supporting \code{.R} files in the \code{R/} directory
|
||||
this function loads any top-level supporting \code{.R} files in the \verb{R/} directory
|
||||
adjacent to the \code{app.R}/\code{server.R}/\code{ui.R} files.
|
||||
}
|
||||
\details{
|
||||
Since Shiny 1.5.0, this function is called by default when running an
|
||||
application. If it causes problems, you can opt out by using
|
||||
\code{options(shiny.autoload.r=FALSE)}. Note that in a future version of Shiny,
|
||||
this option will no longer be available. If you set this option, it will
|
||||
application. If it causes problems, there are two ways to opt out. You can
|
||||
either place a file named \verb{_disable_autoload.R} in your R/ directory, or
|
||||
set \code{options(shiny.autoload.r=FALSE)}. If you set this option, it will
|
||||
affect any application that runs later in the same R session, potentially
|
||||
breaking it, so after running your application, you should unset option with
|
||||
\code{options(shiny.autoload.r=NULL)}
|
||||
|
||||
The files are sourced in alphabetical order (as determined by
|
||||
\link{list.files}). \code{global.R} is evaluated before the supporting R files in the
|
||||
\code{R/} directory.
|
||||
\verb{R/} directory.
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
\alias{markOutputAttrs}
|
||||
\title{Mark a render function with attributes that will be used by the output}
|
||||
\usage{
|
||||
markOutputAttrs(renderFunc, snapshotExclude = NULL,
|
||||
snapshotPreprocess = NULL)
|
||||
markOutputAttrs(renderFunc, snapshotExclude = NULL, snapshotPreprocess = NULL)
|
||||
}
|
||||
\arguments{
|
||||
\item{renderFunc}{A function that is suitable for assigning to a Shiny output
|
||||
|
||||
62
man/markdown.Rd
Normal file
62
man/markdown.Rd
Normal file
@@ -0,0 +1,62 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/shiny.R
|
||||
\name{markdown}
|
||||
\alias{markdown}
|
||||
\title{Insert inline Markdown}
|
||||
\usage{
|
||||
markdown(mds, extensions = TRUE, .noWS = NULL, ...)
|
||||
}
|
||||
\arguments{
|
||||
\item{mds}{A character vector of Markdown source to convert to HTML. If the
|
||||
vector has more than one element, a single-element character vector of
|
||||
concatenated HTML is returned.}
|
||||
|
||||
\item{extensions}{Enable Github syntax extensions; defaults to \code{TRUE}.}
|
||||
|
||||
\item{.noWS}{Character vector used to omit some of the whitespace that would
|
||||
normally be written around generated HTML. Valid options include \code{before},
|
||||
\code{after}, and \code{outside} (equivalent to \code{before} and \code{end}).}
|
||||
|
||||
\item{...}{Additional arguments to pass to \code{\link[commonmark:markdown_html]{commonmark::markdown_html()}}.
|
||||
These arguments are \emph{\link[rlang:dyn-dots]{dynamic}}.}
|
||||
}
|
||||
\value{
|
||||
a character vector marked as HTML.
|
||||
}
|
||||
\description{
|
||||
This function accepts
|
||||
\href{https://en.wikipedia.org/wiki/Markdown}{Markdown}-syntax text and returns
|
||||
HTML that may be included in Shiny UIs.
|
||||
}
|
||||
\details{
|
||||
Leading whitespace is trimmed from Markdown text with \code{\link[glue:trim]{glue::trim()}}.
|
||||
Whitespace trimming ensures Markdown is processed correctly even when the
|
||||
call to \code{markdown()} is indented within surrounding R code.
|
||||
|
||||
By default, \link[commonmark:extensions]{Github extensions} are enabled, but this
|
||||
can be disabled by passing \code{extensions = FALSE}.
|
||||
|
||||
Markdown rendering is performed by \code{\link[commonmark:markdown_html]{commonmark::markdown_html()}}. Additional
|
||||
arguments to \code{markdown()} are passed as arguments to \code{markdown_html()}
|
||||
}
|
||||
\examples{
|
||||
ui <- fluidPage(
|
||||
markdown("
|
||||
# Markdown Example
|
||||
|
||||
This is a markdown paragraph, and will be contained within a `<p>` tag
|
||||
in the UI.
|
||||
|
||||
The following is an unordered list, which will be represented in the UI as
|
||||
a `<ul>` with `<li>` children:
|
||||
|
||||
* a bullet
|
||||
* another
|
||||
|
||||
[Links](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a) work;
|
||||
so does *emphasis*.
|
||||
|
||||
To see more of what's possible, check out [commonmark.org/help](https://commonmark.org/help).
|
||||
")
|
||||
)
|
||||
}
|
||||
@@ -4,9 +4,15 @@
|
||||
\alias{memoryCache}
|
||||
\title{Create a memory cache object}
|
||||
\usage{
|
||||
memoryCache(max_size = 10 * 1024^2, max_age = Inf, max_n = Inf,
|
||||
evict = c("lru", "fifo"), missing = key_missing(),
|
||||
exec_missing = FALSE, logfile = NULL)
|
||||
memoryCache(
|
||||
max_size = 10 * 1024^2,
|
||||
max_age = Inf,
|
||||
max_n = Inf,
|
||||
evict = c("lru", "fifo"),
|
||||
missing = key_missing(),
|
||||
exec_missing = FALSE,
|
||||
logfile = NULL
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{max_size}{Maximum size of the cache, in bytes. If the cache exceeds
|
||||
|
||||
@@ -4,8 +4,14 @@
|
||||
\alias{modalDialog}
|
||||
\title{Create a modal dialog UI}
|
||||
\usage{
|
||||
modalDialog(..., title = NULL, footer = modalButton("Dismiss"),
|
||||
size = c("m", "s", "l"), easyClose = FALSE, fade = TRUE)
|
||||
modalDialog(
|
||||
...,
|
||||
title = NULL,
|
||||
footer = modalButton("Dismiss"),
|
||||
size = c("m", "s", "l"),
|
||||
easyClose = FALSE,
|
||||
fade = TRUE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{UI elements for the body of the modal dialog box.}
|
||||
|
||||
112
man/moduleServer.Rd
Normal file
112
man/moduleServer.Rd
Normal file
@@ -0,0 +1,112 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/modules.R
|
||||
\name{moduleServer}
|
||||
\alias{moduleServer}
|
||||
\alias{callModule}
|
||||
\title{Shiny modules}
|
||||
\usage{
|
||||
moduleServer(id, module, session = getDefaultReactiveDomain())
|
||||
|
||||
callModule(module, id, ..., session = getDefaultReactiveDomain())
|
||||
}
|
||||
\arguments{
|
||||
\item{id}{An ID string that corresponds with the ID used to call the module's
|
||||
UI function.}
|
||||
|
||||
\item{module}{A Shiny module server function.}
|
||||
|
||||
\item{session}{Session from which to make a child scope (the default should
|
||||
almost always be used).}
|
||||
|
||||
\item{...}{For \code{callModule}, additional parameters to pass to module server
|
||||
function.}
|
||||
}
|
||||
\value{
|
||||
The return value, if any, from executing the module server function
|
||||
}
|
||||
\description{
|
||||
Shiny's module feature lets you break complicated UI and server logic into
|
||||
smaller, self-contained pieces. Compared to large monolithic Shiny apps,
|
||||
modules are easier to reuse and easier to reason about. See the article at
|
||||
\url{http://shiny.rstudio.com/articles/modules.html} to learn more.
|
||||
}
|
||||
\details{
|
||||
Starting in Shiny 1.5.0, we recommend using \code{moduleServer} instead of
|
||||
\code{callModule}, because the syntax is a little easier to understand, and
|
||||
modules created with \code{moduleServer} can be tested with \code{\link[=testServer]{testServer()}}.
|
||||
}
|
||||
\examples{
|
||||
# Define the UI for a module
|
||||
counterUI <- function(id, label = "Counter") {
|
||||
ns <- NS(id)
|
||||
tagList(
|
||||
actionButton(ns("button"), label = label),
|
||||
verbatimTextOutput(ns("out"))
|
||||
)
|
||||
}
|
||||
|
||||
# Define the server logic for a module
|
||||
counterServer <- function(id) {
|
||||
moduleServer(
|
||||
id,
|
||||
function(input, output, session) {
|
||||
count <- reactiveVal(0)
|
||||
observeEvent(input$button, {
|
||||
count(count() + 1)
|
||||
})
|
||||
output$out <- renderText({
|
||||
count()
|
||||
})
|
||||
count
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
# Use the module in an app
|
||||
ui <- fluidPage(
|
||||
counterUI("counter1", "Counter #1"),
|
||||
counterUI("counter2", "Counter #2")
|
||||
)
|
||||
server <- function(input, output, session) {
|
||||
counterServer("counter1")
|
||||
counterServer("counter2")
|
||||
}
|
||||
if (interactive()) {
|
||||
shinyApp(ui, server)
|
||||
}
|
||||
|
||||
|
||||
|
||||
# If you want to pass extra parameters to the module's server logic, you can
|
||||
# add them to your function. In this case `prefix` is text that will be
|
||||
# printed before the count.
|
||||
counterServer2 <- function(id, prefix = NULL) {
|
||||
moduleServer(
|
||||
id,
|
||||
function(input, output, session) {
|
||||
count <- reactiveVal(0)
|
||||
observeEvent(input$button, {
|
||||
count(count() + 1)
|
||||
})
|
||||
output$out <- renderText({
|
||||
paste0(prefix, count())
|
||||
})
|
||||
count
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
ui <- fluidPage(
|
||||
counterUI("counter", "Counter"),
|
||||
)
|
||||
server <- function(input, output, session) {
|
||||
counterServer2("counter", "The current count is: ")
|
||||
}
|
||||
if (interactive()) {
|
||||
shinyApp(ui, server)
|
||||
}
|
||||
|
||||
}
|
||||
\seealso{
|
||||
\url{http://shiny.rstudio.com/articles/modules.html}
|
||||
}
|
||||
@@ -5,11 +5,22 @@
|
||||
\alias{navbarMenu}
|
||||
\title{Create a page with a top level navigation bar}
|
||||
\usage{
|
||||
navbarPage(title, ..., id = NULL, selected = NULL,
|
||||
navbarPage(
|
||||
title,
|
||||
...,
|
||||
id = NULL,
|
||||
selected = NULL,
|
||||
position = c("static-top", "fixed-top", "fixed-bottom"),
|
||||
header = NULL, footer = NULL, inverse = FALSE,
|
||||
collapsible = FALSE, collapsable, fluid = TRUE, responsive = NULL,
|
||||
theme = NULL, windowTitle = title)
|
||||
header = NULL,
|
||||
footer = NULL,
|
||||
inverse = FALSE,
|
||||
collapsible = FALSE,
|
||||
collapsable,
|
||||
fluid = TRUE,
|
||||
responsive = NULL,
|
||||
theme = NULL,
|
||||
windowTitle = title
|
||||
)
|
||||
|
||||
navbarMenu(title, ..., menuName = title, icon = NULL)
|
||||
}
|
||||
@@ -21,7 +32,7 @@ navbarMenu(title, ..., menuName = title, icon = NULL)
|
||||
section headers. If the string is a set of dashes like \code{"----"} a
|
||||
horizontal separator will be displayed in the menu.}
|
||||
|
||||
\item{id}{If provided, you can use \code{input$}\emph{\code{id}} in your
|
||||
\item{id}{If provided, you can use \verb{input$}\emph{\code{id}} in your
|
||||
server logic to determine which of the current tabs is active. The value
|
||||
will correspond to the \code{value} argument that is passed to
|
||||
\code{\link[=tabPanel]{tabPanel()}}.}
|
||||
@@ -106,9 +117,13 @@ navbarPage("App Title",
|
||||
\code{\link[=updateNavbarPage]{updateNavbarPage()}}, \code{\link[=insertTab]{insertTab()}},
|
||||
\code{\link[=showTab]{showTab()}}
|
||||
|
||||
Other layout functions: \code{\link{fillPage}},
|
||||
\code{\link{fixedPage}}, \code{\link{flowLayout}},
|
||||
\code{\link{fluidPage}}, \code{\link{sidebarLayout}},
|
||||
\code{\link{splitLayout}}, \code{\link{verticalLayout}}
|
||||
Other layout functions:
|
||||
\code{\link{fillPage}()},
|
||||
\code{\link{fixedPage}()},
|
||||
\code{\link{flowLayout}()},
|
||||
\code{\link{fluidPage}()},
|
||||
\code{\link{sidebarLayout}()},
|
||||
\code{\link{splitLayout}()},
|
||||
\code{\link{verticalLayout}()}
|
||||
}
|
||||
\concept{layout functions}
|
||||
|
||||
@@ -4,13 +4,19 @@
|
||||
\alias{navlistPanel}
|
||||
\title{Create a navigation list panel}
|
||||
\usage{
|
||||
navlistPanel(..., id = NULL, selected = NULL, well = TRUE,
|
||||
fluid = TRUE, widths = c(4, 8))
|
||||
navlistPanel(
|
||||
...,
|
||||
id = NULL,
|
||||
selected = NULL,
|
||||
well = TRUE,
|
||||
fluid = TRUE,
|
||||
widths = c(4, 8)
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{\code{\link[=tabPanel]{tabPanel()}} elements to include in the navlist}
|
||||
|
||||
\item{id}{If provided, you can use \code{input$}\emph{\code{id}} in your
|
||||
\item{id}{If provided, you can use \verb{input$}\emph{\code{id}} in your
|
||||
server logic to determine which of the current navlist items is active. The
|
||||
value will correspond to the \code{value} argument that is passed to
|
||||
\code{\link[=tabPanel]{tabPanel()}}.}
|
||||
|
||||
@@ -4,9 +4,18 @@
|
||||
\alias{nearPoints}
|
||||
\title{Find rows of data that are near a click/hover/double-click}
|
||||
\usage{
|
||||
nearPoints(df, coordinfo, xvar = NULL, yvar = NULL, panelvar1 = NULL,
|
||||
panelvar2 = NULL, threshold = 5, maxpoints = NULL,
|
||||
addDist = FALSE, allRows = FALSE)
|
||||
nearPoints(
|
||||
df,
|
||||
coordinfo,
|
||||
xvar = NULL,
|
||||
yvar = NULL,
|
||||
panelvar1 = NULL,
|
||||
panelvar2 = NULL,
|
||||
threshold = 5,
|
||||
maxpoints = NULL,
|
||||
addDist = FALSE,
|
||||
allRows = FALSE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{df}{A data frame from which to select rows.}
|
||||
|
||||
@@ -4,8 +4,15 @@
|
||||
\alias{numericInput}
|
||||
\title{Create a numeric input control}
|
||||
\usage{
|
||||
numericInput(inputId, label, value, min = NA, max = NA, step = NA,
|
||||
width = NULL)
|
||||
numericInput(
|
||||
inputId,
|
||||
label,
|
||||
value,
|
||||
min = NA,
|
||||
max = NA,
|
||||
step = NA,
|
||||
width = NULL
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{The \code{input} slot that will be used to access the value.}
|
||||
@@ -52,13 +59,20 @@ shinyApp(ui, server)
|
||||
\seealso{
|
||||
\code{\link[=updateNumericInput]{updateNumericInput()}}
|
||||
|
||||
Other input elements: \code{\link{actionButton}},
|
||||
\code{\link{checkboxGroupInput}},
|
||||
\code{\link{checkboxInput}}, \code{\link{dateInput}},
|
||||
\code{\link{dateRangeInput}}, \code{\link{fileInput}},
|
||||
\code{\link{passwordInput}}, \code{\link{radioButtons}},
|
||||
\code{\link{selectInput}}, \code{\link{sliderInput}},
|
||||
\code{\link{submitButton}}, \code{\link{textAreaInput}},
|
||||
\code{\link{textInput}}, \code{\link{varSelectInput}}
|
||||
Other input elements:
|
||||
\code{\link{actionButton}()},
|
||||
\code{\link{checkboxGroupInput}()},
|
||||
\code{\link{checkboxInput}()},
|
||||
\code{\link{dateInput}()},
|
||||
\code{\link{dateRangeInput}()},
|
||||
\code{\link{fileInput}()},
|
||||
\code{\link{passwordInput}()},
|
||||
\code{\link{radioButtons}()},
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
}
|
||||
\concept{input elements}
|
||||
|
||||
@@ -4,10 +4,17 @@
|
||||
\alias{observe}
|
||||
\title{Create a reactive observer}
|
||||
\usage{
|
||||
observe(x, env = parent.frame(), quoted = FALSE, label = NULL,
|
||||
suspended = FALSE, priority = 0,
|
||||
domain = getDefaultReactiveDomain(), autoDestroy = TRUE,
|
||||
..stacktraceon = TRUE)
|
||||
observe(
|
||||
x,
|
||||
env = parent.frame(),
|
||||
quoted = FALSE,
|
||||
label = NULL,
|
||||
suspended = FALSE,
|
||||
priority = 0,
|
||||
domain = getDefaultReactiveDomain(),
|
||||
autoDestroy = TRUE,
|
||||
..stacktraceon = TRUE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{x}{An expression (quoted or unquoted). Any return value will be
|
||||
|
||||
@@ -5,18 +5,35 @@
|
||||
\alias{eventReactive}
|
||||
\title{Event handler}
|
||||
\usage{
|
||||
observeEvent(eventExpr, handlerExpr, event.env = parent.frame(),
|
||||
event.quoted = FALSE, handler.env = parent.frame(),
|
||||
handler.quoted = FALSE, label = NULL, suspended = FALSE,
|
||||
priority = 0, domain = getDefaultReactiveDomain(),
|
||||
autoDestroy = TRUE, ignoreNULL = TRUE, ignoreInit = FALSE,
|
||||
once = FALSE)
|
||||
observeEvent(
|
||||
eventExpr,
|
||||
handlerExpr,
|
||||
event.env = parent.frame(),
|
||||
event.quoted = FALSE,
|
||||
handler.env = parent.frame(),
|
||||
handler.quoted = FALSE,
|
||||
label = NULL,
|
||||
suspended = FALSE,
|
||||
priority = 0,
|
||||
domain = getDefaultReactiveDomain(),
|
||||
autoDestroy = TRUE,
|
||||
ignoreNULL = TRUE,
|
||||
ignoreInit = FALSE,
|
||||
once = FALSE
|
||||
)
|
||||
|
||||
eventReactive(eventExpr, valueExpr, event.env = parent.frame(),
|
||||
event.quoted = FALSE, value.env = parent.frame(),
|
||||
value.quoted = FALSE, label = NULL,
|
||||
domain = getDefaultReactiveDomain(), ignoreNULL = TRUE,
|
||||
ignoreInit = FALSE)
|
||||
eventReactive(
|
||||
eventExpr,
|
||||
valueExpr,
|
||||
event.env = parent.frame(),
|
||||
event.quoted = FALSE,
|
||||
value.env = parent.frame(),
|
||||
value.quoted = FALSE,
|
||||
label = NULL,
|
||||
domain = getDefaultReactiveDomain(),
|
||||
ignoreNULL = TRUE,
|
||||
ignoreInit = FALSE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{eventExpr}{A (quoted or unquoted) expression that represents the event;
|
||||
@@ -148,7 +165,7 @@ button, then \code{ignoreInit = TRUE} will guarantee that the action (in
|
||||
instead of also being triggered when it is created/initialized. Similarly,
|
||||
if you're setting up an \code{eventReactive} that responds to a dynamically
|
||||
created button used to refresh some data (then returned by that \code{eventReactive}),
|
||||
then you should use \code{eventReactive([...], ignoreInit = TRUE)} if you want
|
||||
then you should use \verb{eventReactive([...], ignoreInit = TRUE)} if you want
|
||||
to let the user decide if/when they want to refresh the data (since, depending
|
||||
on the app, this may be a computationally expensive operation).
|
||||
|
||||
@@ -202,7 +219,7 @@ if (interactive()) {
|
||||
# Take an action every time button is pressed;
|
||||
# here, we just print a message to the console
|
||||
observeEvent(input$button, {
|
||||
cat("Showing", input$x, "rows\\n")
|
||||
cat("Showing", input$x, "rows\n")
|
||||
})
|
||||
# Take a reactive dependency on input$button, but
|
||||
# not on any of the stuff inside the function
|
||||
|
||||
@@ -92,14 +92,14 @@ ui <- function(req) {
|
||||
server <- function(input, output) {
|
||||
onBookmark(function(state) {
|
||||
savedTime <- as.character(Sys.time())
|
||||
cat("Last saved at", savedTime, "\\n")
|
||||
cat("Last saved at", savedTime, "\n")
|
||||
# state is a mutable reference object, and we can add arbitrary values to
|
||||
# it.
|
||||
state$values$time <- savedTime
|
||||
})
|
||||
|
||||
onRestore(function(state) {
|
||||
cat("Restoring from state bookmarked at", state$values$time, "\\n")
|
||||
cat("Restoring from state bookmarked at", state$values$time, "\n")
|
||||
})
|
||||
}
|
||||
enableBookmarking("url")
|
||||
|
||||
@@ -33,14 +33,14 @@ if (interactive()) {
|
||||
ui = basicPage("onStop demo"),
|
||||
|
||||
server = function(input, output, session) {
|
||||
onStop(function() cat("Session stopped\\n"))
|
||||
onStop(function() cat("Session stopped\n"))
|
||||
},
|
||||
|
||||
onStart = function() {
|
||||
cat("Doing application setup\\n")
|
||||
cat("Doing application setup\n")
|
||||
|
||||
onStop(function() {
|
||||
cat("Doing application cleanup\\n")
|
||||
cat("Doing application cleanup\n")
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -53,16 +53,16 @@ if (interactive()) {
|
||||
|
||||
\dontrun{
|
||||
# ==== app.R ====
|
||||
cat("Doing application setup\\n")
|
||||
cat("Doing application setup\n")
|
||||
onStop(function() {
|
||||
cat("Doing application cleanup\\n")
|
||||
cat("Doing application cleanup\n")
|
||||
})
|
||||
|
||||
shinyApp(
|
||||
ui = basicPage("onStop demo"),
|
||||
|
||||
server = function(input, output, session) {
|
||||
onStop(function() cat("Session stopped\\n"))
|
||||
onStop(function() cat("Session stopped\n"))
|
||||
}
|
||||
)
|
||||
# ==== end app.R ====
|
||||
@@ -70,9 +70,9 @@ shinyApp(
|
||||
|
||||
# Similarly, if you have a global.R, you can call onStop() from there.
|
||||
# ==== global.R ====
|
||||
cat("Doing application setup\\n")
|
||||
cat("Doing application setup\n")
|
||||
onStop(function() {
|
||||
cat("Doing application cleanup\\n")
|
||||
cat("Doing application cleanup\n")
|
||||
})
|
||||
# ==== end global.R ====
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
\alias{passwordInput}
|
||||
\title{Create a password input control}
|
||||
\usage{
|
||||
passwordInput(inputId, label, value = "", width = NULL,
|
||||
placeholder = NULL)
|
||||
passwordInput(inputId, label, value = "", width = NULL, placeholder = NULL)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{The \code{input} slot that will be used to access the value.}
|
||||
@@ -54,13 +53,20 @@ shinyApp(ui, server)
|
||||
\seealso{
|
||||
\code{\link[=updateTextInput]{updateTextInput()}}
|
||||
|
||||
Other input elements: \code{\link{actionButton}},
|
||||
\code{\link{checkboxGroupInput}},
|
||||
\code{\link{checkboxInput}}, \code{\link{dateInput}},
|
||||
\code{\link{dateRangeInput}}, \code{\link{fileInput}},
|
||||
\code{\link{numericInput}}, \code{\link{radioButtons}},
|
||||
\code{\link{selectInput}}, \code{\link{sliderInput}},
|
||||
\code{\link{submitButton}}, \code{\link{textAreaInput}},
|
||||
\code{\link{textInput}}, \code{\link{varSelectInput}}
|
||||
Other input elements:
|
||||
\code{\link{actionButton}()},
|
||||
\code{\link{checkboxGroupInput}()},
|
||||
\code{\link{checkboxInput}()},
|
||||
\code{\link{dateInput}()},
|
||||
\code{\link{dateRangeInput}()},
|
||||
\code{\link{fileInput}()},
|
||||
\code{\link{numericInput}()},
|
||||
\code{\link{radioButtons}()},
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
}
|
||||
\concept{input elements}
|
||||
|
||||
@@ -5,15 +5,35 @@
|
||||
\alias{imageOutput}
|
||||
\title{Create an plot or image output element}
|
||||
\usage{
|
||||
imageOutput(outputId, width = "100\%", height = "400px",
|
||||
click = NULL, dblclick = NULL, hover = NULL, hoverDelay = NULL,
|
||||
hoverDelayType = NULL, brush = NULL, clickId = NULL,
|
||||
hoverId = NULL, inline = FALSE)
|
||||
imageOutput(
|
||||
outputId,
|
||||
width = "100\%",
|
||||
height = "400px",
|
||||
click = NULL,
|
||||
dblclick = NULL,
|
||||
hover = NULL,
|
||||
hoverDelay = NULL,
|
||||
hoverDelayType = NULL,
|
||||
brush = NULL,
|
||||
clickId = NULL,
|
||||
hoverId = NULL,
|
||||
inline = FALSE
|
||||
)
|
||||
|
||||
plotOutput(outputId, width = "100\%", height = "400px", click = NULL,
|
||||
dblclick = NULL, hover = NULL, hoverDelay = NULL,
|
||||
hoverDelayType = NULL, brush = NULL, clickId = NULL,
|
||||
hoverId = NULL, inline = FALSE)
|
||||
plotOutput(
|
||||
outputId,
|
||||
width = "100\%",
|
||||
height = "400px",
|
||||
click = NULL,
|
||||
dblclick = NULL,
|
||||
hover = NULL,
|
||||
hoverDelay = NULL,
|
||||
hoverDelayType = NULL,
|
||||
brush = NULL,
|
||||
clickId = NULL,
|
||||
hoverId = NULL,
|
||||
inline = FALSE
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{outputId}{output variable to read the plot/image from.}
|
||||
@@ -95,7 +115,7 @@ not work for \pkg{\link[grid:grid-package]{grid}}-based graphics, such as
|
||||
|
||||
Plots and images in Shiny support mouse-based interaction, via clicking,
|
||||
double-clicking, hovering, and brushing. When these interaction events
|
||||
occur, the mouse coordinates will be sent to the server as \code{input$}
|
||||
occur, the mouse coordinates will be sent to the server as \verb{input$}
|
||||
variables, as specified by \code{click}, \code{dblclick}, \code{hover}, or
|
||||
\code{brush}.
|
||||
|
||||
@@ -175,15 +195,15 @@ shinyApp(
|
||||
plot(d$speed, d$dist)
|
||||
})
|
||||
output$plot_clickinfo <- renderPrint({
|
||||
cat("Click:\\n")
|
||||
cat("Click:\n")
|
||||
str(input$plot_click)
|
||||
})
|
||||
output$plot_hoverinfo <- renderPrint({
|
||||
cat("Hover (throttled):\\n")
|
||||
cat("Hover (throttled):\n")
|
||||
str(input$plot_hover)
|
||||
})
|
||||
output$plot_brushinfo <- renderPrint({
|
||||
cat("Brush (debounced):\\n")
|
||||
cat("Brush (debounced):\n")
|
||||
str(input$plot_brush)
|
||||
})
|
||||
output$plot_clickedpoints <- renderTable({
|
||||
@@ -255,15 +275,15 @@ shinyApp(
|
||||
)
|
||||
})
|
||||
output$image_clickinfo <- renderPrint({
|
||||
cat("Click:\\n")
|
||||
cat("Click:\n")
|
||||
str(input$image_click)
|
||||
})
|
||||
output$image_hoverinfo <- renderPrint({
|
||||
cat("Hover (throttled):\\n")
|
||||
cat("Hover (throttled):\n")
|
||||
str(input$image_hover)
|
||||
})
|
||||
output$image_brushinfo <- renderPrint({
|
||||
cat("Brush (debounced):\\n")
|
||||
cat("Brush (debounced):\n")
|
||||
str(input$image_brush)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,8 +4,14 @@
|
||||
\alias{plotPNG}
|
||||
\title{Run a plotting function and save the output as a PNG}
|
||||
\usage{
|
||||
plotPNG(func, filename = tempfile(fileext = ".png"), width = 400,
|
||||
height = 400, res = 72, ...)
|
||||
plotPNG(
|
||||
func,
|
||||
filename = tempfile(fileext = ".png"),
|
||||
width = 400,
|
||||
height = 400,
|
||||
res = 72,
|
||||
...
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{func}{A function that generates a plot.}
|
||||
|
||||
@@ -4,9 +4,16 @@
|
||||
\alias{radioButtons}
|
||||
\title{Create radio buttons}
|
||||
\usage{
|
||||
radioButtons(inputId, label, choices = NULL, selected = NULL,
|
||||
inline = FALSE, width = NULL, choiceNames = NULL,
|
||||
choiceValues = NULL)
|
||||
radioButtons(
|
||||
inputId,
|
||||
label,
|
||||
choices = NULL,
|
||||
selected = NULL,
|
||||
inline = FALSE,
|
||||
width = NULL,
|
||||
choiceNames = NULL,
|
||||
choiceValues = NULL
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{The \code{input} slot that will be used to access the value.}
|
||||
@@ -108,13 +115,20 @@ shinyApp(ui, server)
|
||||
\seealso{
|
||||
\code{\link[=updateRadioButtons]{updateRadioButtons()}}
|
||||
|
||||
Other input elements: \code{\link{actionButton}},
|
||||
\code{\link{checkboxGroupInput}},
|
||||
\code{\link{checkboxInput}}, \code{\link{dateInput}},
|
||||
\code{\link{dateRangeInput}}, \code{\link{fileInput}},
|
||||
\code{\link{numericInput}}, \code{\link{passwordInput}},
|
||||
\code{\link{selectInput}}, \code{\link{sliderInput}},
|
||||
\code{\link{submitButton}}, \code{\link{textAreaInput}},
|
||||
\code{\link{textInput}}, \code{\link{varSelectInput}}
|
||||
Other input elements:
|
||||
\code{\link{actionButton}()},
|
||||
\code{\link{checkboxGroupInput}()},
|
||||
\code{\link{checkboxInput}()},
|
||||
\code{\link{dateInput}()},
|
||||
\code{\link{dateRangeInput}()},
|
||||
\code{\link{fileInput}()},
|
||||
\code{\link{numericInput}()},
|
||||
\code{\link{passwordInput}()},
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
}
|
||||
\concept{input elements}
|
||||
|
||||
@@ -5,8 +5,14 @@
|
||||
\alias{is.reactive}
|
||||
\title{Create a reactive expression}
|
||||
\usage{
|
||||
reactive(x, env = parent.frame(), quoted = FALSE, label = NULL,
|
||||
domain = getDefaultReactiveDomain(), ..stacktraceon = TRUE)
|
||||
reactive(
|
||||
x,
|
||||
env = parent.frame(),
|
||||
quoted = FALSE,
|
||||
label = NULL,
|
||||
domain = getDefaultReactiveDomain(),
|
||||
..stacktraceon = TRUE
|
||||
)
|
||||
|
||||
is.reactive(x)
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ last modified timestamp of a file, and a value retrieval function that
|
||||
actually reads the contents of the file.
|
||||
|
||||
As another example, one might read a relational database table reactively by
|
||||
using a check function that does \code{SELECT MAX(timestamp) FROM table} and
|
||||
a value retrieval function that does \code{SELECT * FROM table}.
|
||||
using a check function that does \verb{SELECT MAX(timestamp) FROM table} and
|
||||
a value retrieval function that does \verb{SELECT * FROM table}.
|
||||
|
||||
The \code{intervalMillis}, \code{checkFunc}, and \code{valueFunc} functions
|
||||
will be executed in a reactive context; therefore, they may read reactive
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user