Compare commits

...

6 Commits

Author SHA1 Message Date
Garrick Aden-Buie
3654cc0882 feat(mock-session): Add removeInputs method 2025-09-23 15:49:42 -04:00
Garrick Aden-Buie
41f4558a3c feat(session): Add $removeInputs() method 2025-09-23 15:44:17 -04:00
Garrick Aden-Buie
f3264259b6 feat: give reactiveValues a remove attribute 2025-09-23 15:24:38 -04:00
Garrick Aden-Buie
5798efb992 feat(reactiveValues): Add $remove() method to remove a key from a reactive values object 2025-09-23 15:14:24 -04:00
Winston Chang
b6e9e9d216 Update package.json for publishing (#4284)
* Update package.json for publishing

* Use custom readme file for npm

* Update repository URLs

* Script cleanup

* Use name @posit/shiny
2025-09-09 16:37:06 -05:00
Barret Schloerke
5ddb99a5b4 update docs (#4285) 2025-09-05 13:52:30 -04:00
12 changed files with 169 additions and 35 deletions

View File

@@ -126,7 +126,7 @@ Config/Needs/check: shinytest2
Config/testthat/edition: 3
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.2
RoxygenNote: 7.3.3
Collate:
'globals.R'
'app-state.R'

View File

@@ -369,6 +369,29 @@ MockShinySession <- R6Class(
})
private$flush()
},
#' @description Removes inputs from the `session$inputs` object and flushes
#' the reactives.
#' @param inputIds Character vector of input ids to remove.
#' @examples
#' \dontrun{
#' session$setInputs(x=1, y=2)
#' session$removeInputs("x")
#' }
removeInputs = function(inputIds) {
is_clientdata <- grepl("^.clientdata_", inputIds)
if (any(is_clientdata)) {
abort(
"Cannot remove clientData inputs: ",
paste(inputIds[is_clientdata], collapse = ", ")
)
}
for (inputId in inputIds) {
private$.input$remove(inputId)
}
private$flush()
},
#' @description An internal method which shouldn't be used by others.
#' Schedules `callback` for execution after some number of `millis`

View File

@@ -398,8 +398,6 @@ ReactiveValues <- R6Class(
# invalidate all deps of `key`
domain <- getDefaultReactiveDomain()
hidden <- substr(key, 1, 1) == "."
key_exists <- .values$containsKey(key)
if (key_exists && !isTRUE(force) && .dedupe && identical(.values$get(key), value)) {
@@ -420,26 +418,15 @@ ReactiveValues <- R6Class(
.dependents$get(key)$invalidate()
}
# only invalidate if there are deps
if (!key_exists && isTRUE(.hasRetrieved$names)) {
rLog$valueChangeNames(.reactId, .values$keys(), domain)
.namesDeps$invalidate()
# invalidate names() or toList() if needed
if (!key_exists) {
private$invalidateNames(domain)
}
if (hidden) {
if (isTRUE(.hasRetrieved$asListAll)) {
rLog$valueChangeAsListAll(.reactId, .values$values(), domain)
.allValuesDeps$invalidate()
}
} else {
if (isTRUE(.hasRetrieved$asList)) {
react_vals <- .values$values()
react_vals <- react_vals[!grepl("^\\.", base::names(react_vals))]
# leave as is. both object would be registered to the listening object
rLog$valueChangeAsList(.reactId, react_vals, domain)
.valuesDeps$invalidate()
}
}
private$invalidateAsListAny(
all.names = substr(key, 1, 1) == ".",
domain = domain
)
invisible()
},
@@ -451,6 +438,21 @@ ReactiveValues <- R6Class(
})
},
remove = function(key) {
stopifnot(rlang::is_string(key))
if (!self$.values$containsKey(key)) {
return(invisible())
}
value <- self$.values$get(key)
self$.values$remove(key)
self$.nameOrder <- setdiff(self$.nameOrder, key)
private$invalidateNames()
private$invalidateAsListAny(all.names = substr(key, 1, 1) == ".")
invisible(value)
},
names = function() {
if (!isTRUE(.hasRetrieved$names)) {
domain <- getDefaultReactiveDomain()
@@ -529,7 +531,47 @@ ReactiveValues <- R6Class(
return(listValue)
}
),
private = list(
invalidateNames = function(domain = getDefaultReactiveDomain()) {
if (!isTRUE(self$.hasRetrieved$names)) {
return(invisible())
}
rLog$valueChangeNames(self$.reactId, self$.values$keys(), domain)
self$.namesDeps$invalidate()
},
invalidateAsListAny = function(
all.names,
domain = getDefaultReactiveDomain()
) {
if (isTRUE(all.names)) {
private$invalidateAsListAll(domain)
} else {
private$invalidateAsList(domain)
}
},
invalidateAsListAll = function(domain = getDefaultReactiveDomain()) {
if (!isTRUE(self$.hasRetrieved$asListAll)) {
return(invisible())
}
rLog$valueChangeAsListAll(self$.reactId, self$.values$values(), domain)
self$.allValuesDeps$invalidate()
},
invalidateAsList = function(domain = getDefaultReactiveDomain()) {
if (!isTRUE(self$.hasRetrieved$asList)) {
return(invisible())
}
react_vals <- self$.values$values()
react_vals <- react_vals[!grepl("^\\.", base::names(react_vals))]
# leave as is. both object would be registered to the listening object
rLog$valueChangeAsList(self$.reactId, react_vals, domain)
self$.valuesDeps$invalidate()
}
)
)
@@ -599,14 +641,15 @@ checkName <- function(x) {
# @param ns A namespace function (either `identity` or `NS(namespace)`)
.createReactiveValues <- function(values = NULL, readonly = FALSE,
ns = identity) {
structure(
list(
impl = values,
readonly = readonly,
ns = ns
),
class='reactivevalues'
class='reactivevalues',
remove = function(key) values$remove(key)
)
}

View File

@@ -2159,6 +2159,19 @@ ShinySession <- R6Class(
self$cycleStartAction(doManageInputs)
}
},
removeInputs = function(inputIds) {
is_clientdata <- grepl("^.clientdata_", inputIds)
if (any(is_clientdata)) {
abort(
"Cannot remove clientData inputs: ",
paste(inputIds[is_clientdata], collapse = ", ")
)
}
for (inputId in inputIds) {
private$.input$remove(inputId)
}
},
outputOptions = function(name, ...) {
# If no name supplied, return the list of options for all outputs
if (is.null(name))

View File

@@ -46,7 +46,7 @@ session to immediately unblock and carry on with other user interactions.
}
\examples{
\dontshow{if (rlang::is_interactive() && rlang::is_installed("mirai")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
\dontshow{if (rlang::is_interactive() && rlang::is_installed("mirai")) withAutoprint(\{ # examplesIf}
library(shiny)
library(bslib)
library(mirai)

View File

@@ -28,6 +28,15 @@ of \code{\link[=testServer]{testServer()}}.
\dontrun{
session$setInputs(x=1, y=2)
}
## ------------------------------------------------
## Method `MockShinySession$removeInputs`
## ------------------------------------------------
\dontrun{
session$setInputs(x=1, y=2)
session$removeInputs("x")
}
}
\section{Public fields}{
\if{html}{\out{<div class="r6-fields">}}
@@ -95,6 +104,7 @@ user. Always \code{NULL} for a \code{MockShinySesion}.}
\item \href{#method-MockShinySession-cycleStartAction}{\code{MockShinySession$cycleStartAction()}}
\item \href{#method-MockShinySession-fileUrl}{\code{MockShinySession$fileUrl()}}
\item \href{#method-MockShinySession-setInputs}{\code{MockShinySession$setInputs()}}
\item \href{#method-MockShinySession-removeInputs}{\code{MockShinySession$removeInputs()}}
\item \href{#method-MockShinySession-.scheduleTask}{\code{MockShinySession$.scheduleTask()}}
\item \href{#method-MockShinySession-elapse}{\code{MockShinySession$elapse()}}
\item \href{#method-MockShinySession-.now}{\code{MockShinySession$.now()}}
@@ -279,6 +289,35 @@ session$setInputs(x=1, y=2)
}
}
\if{html}{\out{<hr>}}
\if{html}{\out{<a id="method-MockShinySession-removeInputs"></a>}}
\if{latex}{\out{\hypertarget{method-MockShinySession-removeInputs}{}}}
\subsection{Method \code{removeInputs()}}{
Removes inputs from the \code{session$inputs} object and flushes
the reactives.
\subsection{Usage}{
\if{html}{\out{<div class="r">}}\preformatted{MockShinySession$removeInputs(inputIds)}\if{html}{\out{</div>}}
}
\subsection{Arguments}{
\if{html}{\out{<div class="arguments">}}
\describe{
\item{\code{inputIds}}{Character vector of input ids to remove.}
}
\if{html}{\out{</div>}}
}
\subsection{Examples}{
\if{html}{\out{<div class="r example copy">}}
\preformatted{\dontrun{
session$setInputs(x=1, y=2)
session$removeInputs("x")
}
}
\if{html}{\out{</div>}}
}
}
\if{html}{\out{<hr>}}
\if{html}{\out{<a id="method-MockShinySession-.scheduleTask"></a>}}

View File

@@ -83,7 +83,7 @@ instead apply to the entire page, set \code{spinner_selector = 'html'} and
\code{fade_selector = 'html'}.
}
\examples{
\dontshow{if (rlang::is_interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
\dontshow{if (rlang::is_interactive()) withAutoprint(\{ # examplesIf}
library(bslib)

View File

@@ -56,7 +56,7 @@ operations.
}
\examples{
\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
\dontshow{if (interactive()) withAutoprint(\{ # examplesIf}
library(shiny)
ui <- fixedPage(

View File

@@ -38,7 +38,7 @@ Authors:
Other contributors:
\itemize{
\item Posit Software, PBC (03wc8by49) [copyright holder, funder]
\item Posit Software, PBC (\href{https://ror.org/03wc8by49}{ROR}) [copyright holder, funder]
\item jQuery Foundation (jQuery library and jQuery UI library) [copyright holder]
\item jQuery contributors (jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt) [contributor, copyright holder]
\item jQuery UI contributors (jQuery UI library; authors listed in inst/www/shared/jqueryui/AUTHORS.txt) [contributor, copyright holder]

View File

@@ -35,7 +35,7 @@ automatically disabled when spinner(s) are active. When both \code{spinners} and
graying out of recalculating outputs).
}
\examples{
\dontshow{if (rlang::is_interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
\dontshow{if (rlang::is_interactive()) withAutoprint(\{ # examplesIf}
library(bslib)

14
npm-README.md Normal file
View File

@@ -0,0 +1,14 @@
@posit/shiny
============
This npm package contains TypeScript type definitions for Shiny's client-side JavaScript libraries.
It does not include the Shiny framework itself, though that may change in the future.
[Shiny](https://github.com/rstudio/shiny) is a web application framework for both R and Python, developed by Posit PBC.
## Installation
```bash
npm install @posit/shiny
```

View File

@@ -1,17 +1,17 @@
{
"private": true,
"homepage": "https://shiny.rstudio.com",
"repository": "github:rstudio/shiny",
"name": "@types/rstudio-shiny",
"homepage": "https://shiny.posit.co",
"repository": {
"type": "git",
"url": "git+https://github.com/rstudio/shiny.git"
},
"name": "@posit/shiny",
"version": "1.11.1-alpha.9000",
"license": "GPL-3.0-only",
"main": "",
"browser": "",
"types": "srcts/types/extras/globalShiny.d.ts",
"files": [
"DESCRIPTION",
"LICENSE",
"NEWS.md",
"srcts/types/**/*.d.ts"
],
"engines": {
@@ -69,7 +69,9 @@
"build_types": "tsc -p tsconfig.json",
"coverage_detailed": "npx --yes type-check --detail",
"coverage": "type-coverage -p tsconfig.json --at-least 90",
"circular": "npx --yes dpdm --transform ./srcts/src/index.ts"
"circular": "npx --yes dpdm --transform ./srcts/src/index.ts",
"prepack": "cp README.md README-orig.md && cp npm-README.md README.md",
"postpack": "test -f README-orig.md && cp README-orig.md README.md && rm README-orig.md"
},
"prettier": {
"plugins": [