0

I want the user to see a different tooltip when hovering over grouped choices in selectizeInput. This was solved here for a flat vector of choices. i.e. choices = c("a", "b", "c", "d"). The difference is I would like this to work for a nested list of inputs, i.e. choices = list(first= c("a", "b"),second = c("c", "d"))

Here is an example that works for a flat vector of choices:

library(shiny)
library(shinyBS)

selectizeTooltip <- function(id, choice, title, placement = "bottom", trigger = "hover", options = NULL){
  
  options = shinyBS:::buildTooltipOrPopoverOptionsList(title, placement, trigger, options)
  options = paste0("{'", paste(names(options), options, sep = "': '", collapse = "', '"), "'}")
  bsTag <- shiny::tags$script(shiny::HTML(paste0("
    $(document).ready(function() {
      var opts = $.extend(", options, ", {html: true});
      var selectizeParent = document.getElementById('", id, "').parentElement;
      var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation){
          $(mutation.addedNodes).filter('div').filter(function(){return(this.getAttribute('data-value') == '", choice, "');}).each(function() {
            $(this).tooltip('destroy');
            $(this).tooltip(opts);
          });
        });
      });
      observer.observe(selectizeParent, { subtree: true, childList: true });
    });
  ")))
  htmltools::attachDependencies(bsTag, shinyBS:::shinyBSDep)
}

ui <- shinyUI(
  fluidPage(
    selectizeInput(inputId = "lala", label = "Label!", choices = c("a", "b", "c", "d")), # This changes in second example
    selectizeTooltip(id = "lala", choice = "a", title = "Tooltip for a", placement = "right"),
    selectizeTooltip(id = "lala", choice = "b", title = "Tooltip for b", placement = "right"),
    selectizeTooltip(id = "lala", choice = "c", title = "Tooltip for c", placement = "right"), 
    selectizeTooltip(id = "lala", choice = "d", title = "Tooltip for d", placement = "right")
    
  )
)

server <- function(input, output, session){
  observeEvent(input$lala,{
    print(input$lala)
  })
  
}

shinyApp(ui, server)

Here is the example that I want to get working

library(shiny)
library(shinyBS)


selectizeTooltip <- function(id, choice, title, placement = "bottom", trigger = "hover", options = NULL){
  

  options = shinyBS:::buildTooltipOrPopoverOptionsList(title, placement, trigger, options)
  options = paste0("{'", paste(names(options), options, sep = "': '", collapse = "', '"), "'}")
  bsTag <- shiny::tags$script(shiny::HTML(paste0("
    $(document).ready(function() {
      var opts = $.extend(", options, ", {html: true});
      var selectizeParent = document.getElementById('", id, "').parentElement;
      var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation){
          $(mutation.addedNodes).filter('div').filter(function(){return(this.getAttribute('data-value') == '", choice, "');}).each(function() {
            $(this).tooltip('destroy');
            $(this).tooltip(opts);
          });
        });
      });
      observer.observe(selectizeParent, { subtree: true, childList: true });
    });
  ")))
  htmltools::attachDependencies(bsTag, shinyBS:::shinyBSDep)
}

ui <- shinyUI(
  fluidPage(
    selectizeInput(inputId = "lala", label = "Label!", choices = list(`first` = c("a", "b"), 
                                                                      `second` = c("c", "d"))), # this is different
    selectizeTooltip(id = "lala", choice = "a", title = "Tooltip for a", placement = "right"),
    selectizeTooltip(id = "lala", choice = "b", title = "Tooltip for b", placement = "right"),
    selectizeTooltip(id = "lala", choice = "c", title = "Tooltip for c", placement = "right"), 
    selectizeTooltip(id = "lala", choice = "d", title = "Tooltip for d", placement = "right")
    
    # selectizeTooltip(id = "lala", choice = "first.a", title = "Tooltip for a", placement = "right"),
    # selectizeTooltip(id = "lala", choice = "first.b", title = "Tooltip for b", placement = "right"),
    # selectizeTooltip(id = "lala", choice = "second.c", title = "Tooltip for c", placement = "right"), 
    # selectizeTooltip(id = "lala", choice = "second.d", title = "Tooltip for d", placement = "right")
    
  )
)

server <- function(input, output, session){
  observeEvent(input$lala,{
    print(input$lala)
  })
}

shinyApp(ui, server)

I tried changing my choice to be id.choice (first.a) for example with no luck. I also tried bsTooltip with no luck. It appears that this function only allows you to have one tooltip whereas I need a tooltip for each choice in the selectizeInput.

I am open to different solutions like changing the structure of the choices in selectizeInput or using a different selector input, though I would like to be able to have groups of choices in any case. Or modifying the JS portion so it will recognize what I am looking for.

Thanks!

Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225

1 Answers1

0

Here is a way.

library(shiny)

ui <- fluidPage(
  selectizeInput(
    inputId = "sel",
    label = "Animals",
    choices = NULL,
    options = list(
      options = list(
        list( species = 'mammal', value = "dog", name = "Dog"),
        list( species = 'mammal', value = "cat", name = "Cat"),
        list( species = 'mammal', value = "horse", name = "Horse"),
        list( species = 'mammal', value = "kangaroo", name = "Kangaroo"),
        list( species = 'bird', value = 'duck', name = 'Duck'),
        list( species = 'bird', value = 'chicken', name = 'Chicken'),
        list( species = 'bird', value = 'ostrich', name = 'Ostrich'),
        list( species = 'bird', value = 'seagull', name = 'Seagull'),
        list( species = 'reptile', value = 'snake', name = 'Snake'),
        list( species = 'reptile', value = 'lizard', name = 'Lizard'),
        list( species = 'reptile', value = 'alligator', name = 'Alligator'),
        list( species = 'reptile', value = 'turtle', name = 'Turtle')
      ),
      optgroups = list(
        list( value = 'mammal',  label = 'Mammal',  tooltip = 'Mammalia'),
        list( value = 'bird',    label = 'Bird',    tooltip = 'Aves'),
        list( value = 'reptile', label = 'Reptile', tooltip = 'Reptilia')
      ),
      optgroupField = "species",
      labelField = "name",
      render = I(
        "{optgroup_header: function(data, escape) {
            return '<div class=\"optgroup-header\"><span title=\"' + data.tooltip + '\">' + escape(data.label) + '</span></div>';
          }
        }"
      )
    )
  )
)

server <- function(input, output, session) {}

shinyApp(ui, server)

enter image description here


Edit: customized tooltips

enter image description here

library(shiny)
library(bslib)

css <- '
.tooltip {
  pointer-events: none;
}
.tooltip > .tooltip-inner {
  pointer-events: none;
  background-color: #73AD21;
  color: #FFFFFF;
  border: 1px solid green;
  padding: 8px;
  font-size: 20px;
  font-style: italic;
  text-align: justify;
  margin-left: 0;
  max-width: 1000px;
}
.tooltip > .arrow::before {
  border-right-color: #73AD21;
}
'

js <- "
function () {
  setTimeout(function(){$('[data-toggle=tooltip]').tooltip();}, 100);
}
"

ui <- fluidPage(
  theme = bs_theme(version = 4),
  tags$head(tags$style(HTML(css))),
  
  selectizeInput(
    inputId = "sel",
    label = "Animals",
    choices = NULL,
    options = list(
      options = list(
        list( species = 'mammal', value = "dog", name = "Dog"),
        list( species = 'mammal', value = "cat", name = "Cat"),
        list( species = 'mammal', value = "horse", name = "Horse"),
        list( species = 'mammal', value = "kangaroo", name = "Kangaroo"),
        list( species = 'bird', value = 'duck', name = 'Duck'),
        list( species = 'bird', value = 'chicken', name = 'Chicken'),
        list( species = 'bird', value = 'ostrich', name = 'Ostrich'),
        list( species = 'bird', value = 'seagull', name = 'Seagull'),
        list( species = 'reptile', value = 'snake', name = 'Snake'),
        list( species = 'reptile', value = 'lizard', name = 'Lizard'),
        list( species = 'reptile', value = 'alligator', name = 'Alligator'),
        list( species = 'reptile', value = 'turtle', name = 'Turtle')
      ),
      optgroups = list(
        list( value = 'mammal',  label = 'Mammal',  tooltip = 'Mammalia'),
        list( value = 'bird',    label = 'Bird',    tooltip = 'Aves'),
        list( value = 'reptile', label = 'Reptile', tooltip = 'Reptilia')
      ),
      optgroupField = "species",
      labelField = "name",
      render = I(
        "{optgroup_header: function(data, escape) {
            return '<div class=\"optgroup-header\"><span data-toggle=\"tooltip\" data-placement=\"right\" title=\"' + data.tooltip + '\">' + escape(data.label) + '</span></div>';
          }
        }"
      ),
      onDropdownOpen = I(js)
    )
  )
)

server <- function(input, output, session) {}

shinyApp(ui, server)

Edit

If you also want tooltips for the options, add a tooltip field in the list of options, and add a field option in the render option (see at the end):

      options = list(
        list( species = 'mammal', value = "dog", name = "Dog", tooltip = "it is a pet"),
        ......,
      ),
      optgroups = list(
        list( value = 'mammal',  label = 'Mammal',  tooltip = 'Mammalia'),
        list( value = 'bird',    label = 'Bird',    tooltip = 'Aves'),
        list( value = 'reptile', label = 'Reptile', tooltip = 'Reptilia')
      ),
      optgroupField = "species",
      labelField = "name",
      render = I(
        "{
          optgroup_header: function(data, escape) {
            return '<div class=\"optgroup-header\"><span title=\"' + data.tooltip + '\">' + escape(data.label) + '</span></div>';
          },
          option: function(data, escape) {
            return '<div><span title=\"' + data.tooltip + '\">' + escape(data.name) + '</span></div>';
          }
        }"
      )
Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225