Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R shinydashboard: specifying div style width argument as percentage to fit a resizeable JS plot

Task: using R and shinydashboard, embed a custom Javascript-generated plot in the dashboard body. Specify width of the plot as percentage, so that the plot occupies its column (or box) regardless of viewer's screen setup.

Setup: R (3.5.2), shiny (1.2.0) and shinydashboard (0.7.1). The dashboard code (simplified reproducible example) is as follows:

library(shiny)
library(shinydashboard)

ui <- fluidPage(

dashboardPage(
    dashboardHeader(),
    dashboardSidebar(
        sidebarMenu(
            menuItem("Main", tabName = "tab1", icon = icon("dashboard")
            )
        )
    ),
    dashboardBody(
        tabItems(
            tabItem("tab1",
                    column(width = 12, 
                           tags$div(id = "main", style = "width: 100%; height: 400px"),
                           tags$script(src = "http://cdnjs.cloudflare.com/ajax/libs/echarts/4.1.0/echarts.min.js"),
                           tags$script(src = "myscript.js")
                    )
            )
        )
    )
 )
)


server <- function(input, output) {

}

# Run the application 
shinyApp(ui = ui, server = server)

The respective Javascript file myscript.js, which is to be placed in the www subfolder relative to the app file itself, is as follows:

// JS Plot with Echarts 4

option = {
    xAxis: {
        type: 'category',
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
        type: 'value'
    },
    series: [{
        data: [820, 932, 901, 934, 1290, 1330, 1320],
        type: 'line'
    }]
};

var myChart = echarts.init(document.getElementById('main'));
myChart.setOption(option);

Problem: for some reason, the 100% specification gets converted to 100px in the final result, producing this output:

screenshot_plot

Inspecting the plot I see that div#main has indeed the width of 100%, but then it contains another, smaller div, which is already 100px wide:

screenshot2

To me, it would seem that either tabItem or tabItems are at fault, because without using them the outcome is correct, and that smaller intermediary div takes its width from its parent correctly:

screenshot3

For completeness, the code for the working version (without tabItem(s)) is this:

library(shiny)
library(shinydashboard)

ui <- fluidPage(

    dashboardPage(
        dashboardHeader(),
        dashboardSidebar(),
        dashboardBody(
            column(width = 12, 
                   tags$div(id = "main", style = "width: 100%; height: 400px"),
                   tags$script(src = "http://cdnjs.cloudflare.com/ajax/libs/echarts/4.1.0/echarts.min.js"),
                   tags$script(src = "myscript.js")
            )
        )
    )
)

server <- function(input, output) {

}

# Run the application 
shinyApp(ui = ui, server = server)

As you can see, the code is almost identical, aside from the offending functions. I don't see however how a shinydashboard could possibly work without these functions, as they structure the whole application. Is there any workaround you can think of? Thanks.

like image 496
Maxim.K Avatar asked Jan 05 '19 11:01

Maxim.K


1 Answers

Very interesting, but yes it seems like a bug/feature in tabItems.

By setting the width via css, it just stretches the plot, so that doesnt really work. I found those little workarounds, that resize the plot on a window resize.

Option 1:
To initially resize the window I use shinyjs to collapse the sidebar at startup, so the plot gets resized too. This will start the app with a collapsed sidebar.

If you want the sidebar to not be collapsed at startup, you would have to add collapsed = TRUE to dashboardSidebar and change addClass in the server to removeClass.

Option 2:
If you don't want to use shinyjs you could add a little JS-snippet like:

jss <- HTML("
$(document).on('shiny:connected', function() {
  $('.sidebar-toggle').click();
});
")

with

tags$head(tags$script(jss))

in the dashboardBody and collapsed = TRUE in the dashboardSidebar.

app.R

ui <- fluidPage(

  dashboardPage(
    dashboardHeader(),
    dashboardSidebar(
      sidebarMenu(
        menuItem("Main", tabName = "tab1", icon = icon("dashboard")
        )
      )
    ),
    dashboardBody(
      useShinyjs(),
      tabItems(
        tabItem("tab1",
                column(width = 12,
                       tags$div(id = "main", style = "width: 100%; height: 400px"),
                       tags$script(src = "http://cdnjs.cloudflare.com/ajax/libs/echarts/4.1.0/echarts.min.js"),
                       tags$script(src = "myscript.js")
                )
        )
      )
    )
  )
)


server <- function(input, output) {
  addClass(selector = "body", class = "sidebar-collapse")
}

# Run the application
shinyApp(ui = ui, server = server)

myscript.js

option = {
    xAxis: {
        type: 'category',
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
        type: 'value'
    },
    series: [{
        data: [820, 932, 901, 934, 1290, 1330, 1320],
        type: 'line'
    }]
};

var myChart = echarts.init(document.getElementById('main'), width='100%');
myChart.setOption(option);


$(window).on('resize', function(){
    if(myChart != null && myChart != undefined){
        myChart.resize();
    }
});
like image 98
SeGa Avatar answered Nov 16 '22 22:11

SeGa