Appsilon/shiny.react

[Bug]: A named list of inputs causes card to not show.

asadow opened this issue · 3 comments

asadow commented

Guidelines

  • I agree to follow this project's Contributing Guidelines.

Project Version

No response

Platform and OS Version

No response

Existing Issues

No response

What happened?

A card will disappear without warning or error if the div's are named: e.g.

$`Bargaining Unit(s)`
<div class="react-container">
  <script class="react-data" type="application/json">{"type":"element","module":"@/shiny.fluent","name":"Dropdown","props":{"type":"raw","value":{"inputId":"barg","label":"Bargaining Unit(s)","options":[{"key":"CUPE","text":"CUPE"},{"key":"PSA","text":"PSA"}],"multiSelect":true,"value":"CUPE"}}}</script>
  <script>jsmodule['@/shiny.react'].findAndRenderReactData()</script>
</div>

This is unfortunate as we can't do something like

c("barg", "dept", "crew", "tr_code") |>
    set_names(
      c("Bargaining Unit", "Department", "Crew", "Transaction Code")
    ) |>
    imap(
      \(x, idx) Dropdown.shinyInput(...)
    )

Steps to reproduce

  1. May add later a reprex...

...

Expected behavior

Expected card to remain.

Attachments

No response

Screenshots or Videos

No response

Additional Information

No response

Hello @asadow! Thanks for your interest in shiny.fluent and taking the time to report issues you encountered. Please include a minimal reproducible code example when reporting bugs - this is absolutely essential for us to be able to understand and address the issue.

Named lists should render just fine, check out this example:

library(purrr)
library(shiny.fluent)

ui <-
  c("barg", "dept") |>
  set_names(
    c("Bargaining Unit", "Department")
  ) |>
  imap(
    \(x, idx) Toggle.shinyInput(inputId = x, label = idx)
  )

shinyApp(ui = ui, server = function(input, output) {})

Thus I suspect the issue is unrelated to the list being named - I'm going to close it. Feel free to reopen with a code example - we'll appreciate it 🙂

asadow commented

Thanks Kamil! Here is a reprex. Stack() takes issue with a named list

library(purrr)
library(shiny.fluent)

ui <-
  Stack(
  ## This imap'd set does not show, without warning or error.
    c("barg", "dept") |>
  set_names(
    c("Bargaining Unit", "Department")
  ) |>
  imap(
    \(x, idx) Toggle.shinyInput(inputId = x, label = idx)
  ),
  ## Even this input will not show
  Toggle.shinyInput(inputId = "barg", label = "Bargaining Unit")
  
  ## This produces an error: Error in FUN(X[[i]], ...) : 
  ## Expected a recursive structure built of NULLs, lists and dependencies
  # c("barg", "dept") |>
  #   set_names(
  #     c("Bargaining Unit", "Department")
  #   ) |>
  #   imap(
  #     \(x, idx) selectInput(inputId = x, label = idx, choices = "CUPE")
  #   )
  
  ## Produces the same error as above.
  # selectInput(inputId = "barg", label = "barg", choices = "CUPE")

  )

shinyApp(ui = ui, server = function(input, output) {})

Thanks @asadow for the example! You're right, named lists won't work in this way in Stack() or other components. This is due to how R objects are translated to JS for the underlying JS library: named vectors/lists are translated to JS objects, unnamed vectors/lists are translated to JS arrays.

This was a conscious design decision in shiny.react, the package providing the low-level machinery for shiny.fluent. JS objects are often needed as arguments for the components, so we needed a way to represent them in R. We decided to allow for use of plain list() and use the presence or absence of names to determine whether it should be understood as an object or array. (An alternative approach would be e.g. to use special helper functions in R, like js_object() and js_array().)

Keeping this in mind you shouldn't have too much problem redesigning your code to work. See this example for an approach I'd recommend, it also shows why we need JS objects sometimes:

library(purrr)
library(shiny.fluent)

inputs <- list(
  list(
    id = "barg",
    label = "Bargaining Unit",
    options = list(
      list(key = "a", text = "Unit A"),
      list(key = "b", text = "Unit B")
    )
  ),
  list(
    id = "dept",
    label = "Department",
    options = list(
      list(key = "x", text = "Department X"),
      list(key = "y", text = "Department Y")
    )
  )
)

ui <- Stack(
  # Named lists are translated to JS objects.
  tokens = list(childrenGap = 20),
  # The elements to render must be passed in an unnamed list, so it is translated to a JS array.
  map(inputs, \(x) Dropdown.shinyInput(
    inputId = x$id, label = x$label, options = x$options
  ))
)

shinyApp(ui = ui, server = function(input, output) {})

Notes

Your example helped to uncover an unrelated problem: selectInput() doesn't work in React context. This means that trying to use it inside e.g. Stack() will throw an R error. I have created a separate issue for this in #72. Thanks!