5

I am trying to implement into.js multipage functionality on a Shiny app.

The code bellow is an attempt that does not work. The first tab works nicely, the popup for the second page is displayed but without switching the tab.

ui.R

library(shiny)


    shinyUI(tagList(
            tags$head(
                    HTML("<link rel='stylesheet' type='text/css' href='css/introjs.min.css'>")
            ),
            navbarPage("Old Faithful Geyser Data",




      tabPanel(id = "fTab", "First tab",
            HTML("<h1 data-step='1' data-intro='This is a tooltip!'>Basic Usage</h1>"),
           sliderInput("bins",
                       "Number of bins:",
                       min = 1,
                       max = 50,
                       value = 30),

           plotOutput("distPlot"),
           HTML("<a id='startButton' class='btn btn-large btn-success' href='javascript:void(0);'>Help</a>")

      ),
      tabPanel(tabName = "sTab", "Second tab", id = "tt", 
               HTML("<h1 data-step='2' data-intro='This is a second tooltip!'>Basic Usage</h1>"),
                  sliderInput("bins2",
                              "Number of bins:",
                              min = 1,
                              max = 50,
                              value = 30),

                  plotOutput("distPlot2")
                  )
    ),
    HTML("<script type='text/javascript' src='js/intro.min.js'></script>"),
    HTML("<script type='text/javascript'>document.getElementById('startButton').onclick = function() {
         introJs().setOption('doneLabel', 'Next page').start().oncomplete(function() {
         window.location.hash = '#!tt?multipage=true';
         });
         };</script>")  
    ))

server.R

library(shiny)


    shinyServer(function(input, output) {

      output$distPlot <- renderPlot({

        x    <- faithful[, 2] 
        bins <- seq(min(x), max(x), length.out = input$bins + 1)


        hist(x, breaks = bins, col = 'darkgray', border = 'white')

      })
      output$distPlot2 <- renderPlot({


              x    <- faithful[, 2] 
              bins <- seq(min(x), max(x), length.out = input$bins2 + 1)


              hist(x, breaks = bins, col = 'darkgray', border = 'white')

      })

    })

The js and css files from intro.js are in the js and css folders inside the www folder. The intro.js files may be found here

My guess is that I am doing something wrong in the function in the javascript code at the bottom of ui.R. I have tried to adapt the example from here by replacing the window.location.href with window.location.hash and referencing the tab id which is "tt".

Valter Beaković
  • 3,140
  • 2
  • 20
  • 30

2 Answers2

4

Here is the working solution. Note that you need to

  • Determine the current step to switch tabs. The multi page example does not apply here, as all your steps are on one page (multiple tabs but one page), thus intro.js will display all steps before next page is clicked
  • Use JavaScript/JQuery to simulate the tab click event.

ui.R (server.R unchanged)

library(shiny)


shinyUI(tagList(
  tags$head(
    HTML("<link rel='stylesheet' type='text/css' href='css/introjs.min.css'>")
  ),
  navbarPage("Old Faithful Geyser Data",
             tabPanel(id = "fTab", "First tab",
                      HTML("<h1 data-step='1' data-intro='This is a tooltip!'>Basic Usage</h1>"),
                      sliderInput("bins",
                                  "Number of bins:",
                                  min = 1,
                                  max = 50,
                                  value = 30),

                      plotOutput("distPlot"),
                      HTML("<a id='startButton' class='btn btn-large btn-success' href='javascript:void(0);'>Help</a>")

             ),
             tabPanel(tabName = "sTab", "Second tab", id = "tt", 
                      HTML("<h1 data-step='2' data-intro='This is a second tooltip!'>Basic Usage</h1>"),
                      sliderInput("bins2",
                                  "Number of bins:",
                                  min = 1,
                                  max = 50,
                                  value = 30),

                      plotOutput("distPlot2")
             )
  ),
  HTML("<script type='text/javascript' src='js/intro.min.js'></script>"),
  HTML("<script type='text/javascript'>document.getElementById('startButton').onclick = function() {
       introJs().onchange(function(targetElement) {
          if (this._currentStep==0) {
             $('a[data-value=\"Second tab\"]').removeClass('active');
             $('a[data-value=\"First tab\"]').addClass('active');
             $('a[data-value=\"First tab\"]').trigger('click');
          }
          if (this._currentStep==1) {
             $('a[data-value=\"First tab\"]').removeClass('active');
             $('a[data-value=\"Second tab\"]').addClass('active');
             $('a[data-value=\"Second tab\"]').trigger('click');
          }
       }).start();
       };</script>")  
    ))
Xiongbing Jin
  • 11,779
  • 3
  • 47
  • 41
4

@warmoverflow gave an excellent answer. Here is a version of the same answer using rintrojs:

library(shiny)
library(rintrojs)

ui = shinyUI(tagList(
  introjsUI(),
  navbarPage(
    "Old Faithful Geyser Data",

    tabPanel(
      id = "fTab",
      "First tab",
      introBox(
        h1("Basic Usage"),
        data.step = 1,
        data.intro = "This is a tooltip"
      ),
      sliderInput(
        "bins",
        "Number of bins:",
        min = 1,
        max = 50,
        value = 30
      ),

      plotOutput("distPlot"),
      actionButton("startButton", "Help")
    ),
    tabPanel(
      tabName = "sTab",
      "Second tab",
      id = "tt",
      introBox(
        h1("Basic Usage 2"),
        data.step = 2,
        data.intro = "This is a second tooltip"
      ),
      sliderInput(
        "bins2",
        "Number of bins:",
        min = 1,
        max = 50,
        value = 30
      ),

      plotOutput("distPlot2")
    )
  )
))

server = shinyServer(function(input, output, session) {
  output$distPlot <- renderPlot({
    x    <- faithful[, 2]
    bins <- seq(min(x), max(x), length.out = input$bins + 1)


    hist(x,
         breaks = bins,
         col = 'darkgray',
         border = 'white')

  })
  output$distPlot2 <- renderPlot({
    x    <- faithful[, 2]
    bins <- seq(min(x), max(x), length.out = input$bins2 + 1)


    hist(x,
         breaks = bins,
         col = 'darkgray',
         border = 'white')

  })

  observeEvent(input$startButton, {
    introjs(
      session,
      events = list(
        "onchange" = "if (this._currentStep==0) {
        $('a[data-value=\"Second tab\"]').removeClass('active');
        $('a[data-value=\"First tab\"]').addClass('active');
        $('a[data-value=\"First tab\"]').trigger('click');
  }
        if (this._currentStep==1) {
        $('a[data-value=\"First tab\"]').removeClass('active');
        $('a[data-value=\"Second tab\"]').addClass('active');
        $('a[data-value=\"Second tab\"]').trigger('click');
        }"
)
      )

})

  })

shinyApp(ui = ui, server = server)

The tabs don't switch if you go back to the first step of the tour though, so the code only works going forwards through the tour.

NOTE: in rintrojs version 0.1.2.900 and higher, raw javascript needs to be wrapped in I()

Carl
  • 5,569
  • 6
  • 39
  • 74
  • I have tested the code but it works the same as mine. It does show the second popup but without switching the tab? – Valter Beaković Sep 19 '16 at 11:17
  • Sorry, I didn't specify, you will need to install the dev version from github until the next CRAN update. – Carl Sep 19 '16 at 12:47
  • Huh, I have installed the dev version and it is not working at all. I'll stick with @warmoverflow solution. Just for your info I am running Ubuntu 14.04 64bit and R3.3.1 – Valter Beaković Sep 20 '16 at 05:22
  • Weird, I just copy and pasted this code, and hosted it on shinyapps, which is Ubuntu, and it works: https://carlganz.shinyapps.io/SOtest/ – Carl Sep 20 '16 at 13:00
  • That's interensting I have installed rintrojs as per your suggestion with devtools::install_github("carlganz/rintrojs") and it is currently running rintrojs version 0.1.0..900. I have copied your code. So everything should be the same but check my shiny app [here](https://altervalter.shinyapps.io/introjs/) – Valter Beaković Sep 20 '16 at 13:18
  • Totally bazaar. When I initiate the Introjs from the JS console on your site it works, but the button doesn't. If you go to your shiny apps home page, and select the page for this app there will be a button that says "Download" next to the word "Bundle". If you could send the zip file it downloads to me at carlganz at gmail dot com I would greatly appreciate it. – Carl Sep 20 '16 at 13:28
  • Not at the Pc now, will send it in few hours. – Valter Beaković Sep 20 '16 at 14:45
  • Thanks for your help! – Carl Sep 20 '16 at 15:45
  • I am trying to implement the same, but i am not able to use the above code, as i have less understanding of the events which you have added, it would be a great help if you also provide a help on its description or provide a Link which tells about the same in detail. – Sovik Gupta Jun 26 '18 at 08:11
  • Doesn't work for me, possibly because of the `I()` needed in newer versions. Revise to make it work with newer versions? – dca Mar 04 '21 at 21:22