Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always set data-value attribute for .sidebarMenuSelectedTabItem #216

Merged
merged 3 commits into from
Jun 9, 2017

Conversation

bborgesr
Copy link
Contributor

@bborgesr bborgesr commented May 31, 2017

Fixes #214: make sure that the data-value attribute of .sidebarMenuSelectedTabItem is always set in the body of the ensureActivatedTab() function.

Repro

See #214 or here is a more concise one (bug: input$tabs should start out with the value "tab1", but it starts out with NULL and stays that way until you click on the other tab):

library(shinydashboard)
library(shiny)

ui <- dashboardPage(
  dashboardHeader(),
  dashboardSidebar(
    sidebarMenu(id = "tabs",
      menuItem("tab1", tabName = "tab1", selected = TRUE),
      menuItem("tab2", tabName = "tab2")
  )),
  dashboardBody()
)

server <- function(input, output, session){
  observe({ print(input$tabs) })
}
shinyApp(ui, server)

Postemortem

This bug was hard to spot for a few reasons:

  1. It doesn't exist if you don't have selected = TRUE on any tab (the default). I.e. this has the right behavior:
library(shinydashboard)
library(shiny)

ui <- dashboardPage(
  dashboardHeader(),
  dashboardSidebar(
    sidebarMenu(id = "tabs",
      menuItem("tab1", tabName = "tab1"),
      menuItem("tab2", tabName = "tab2")
  )),
  dashboardBody()
)

server <- function(input, output, session){
  observe({ print(input$tabs) })
}
shinyApp(ui, server)
  1. It disappears as soon as you move on to another tab.

  2. It's orthogonal to the matching up of menuItems and tabItems. I.e. this works works as expected, even though input$tabs starts out NULL as well:

library(shinydashboard)
library(shiny)

ui <- dashboardPage(
  dashboardHeader(),
  dashboardSidebar(
    sidebarMenu(id = "tabs",
      menuItem("tab1", tabName = "tab1", selected = TRUE),
      menuItem("tab2", tabName = "tab2")
  )),
  dashboardBody(
      tabItems(
          tabItem("tab1", "This is tab 1"),
          tabItem("tab2", "This is tab 2")
      )
  )
)

server <- function(input, output, session){
  observe({ print(input$tabs) })
}
shinyApp(ui, server)
  1. It doesn't exist for dynamic sidebar menus. I.e. this has the right behavior:
shinyApp(ui, server)library(shinydashboard)
library(shiny)

ui <- dashboardPage(
    dashboardHeader(),
    dashboardSidebar(
        sidebarMenuOutput("menu")),
    dashboardBody()
)

server <- function(input, output, session){
    output$menu <- renderMenu({
        sidebarMenu(id = "tabs",
            menuItem("tab1", tabName = "tab1", selected = TRUE),
            menuItem("tab2", tabName = "tab2")
        )
    })
    observe({ print(input$tabs) })
}
shinyApp(ui, server)

…uSelectedTabItem` is always set in the body of the `ensureActivatedTab()` function
@wch
Copy link
Contributor

wch commented May 31, 2017

I'm having a bit of a hard time following the logic in the code... I could be missing something, but can all that code be reduced to this?

  var $startTab = $tablinks.filter("[data-start-selected='1']");
  if ($startTab.length === 0) {
    // If no tab starts selected, use the first one, if present
    $startTab = $tablinks.first();
  }

  // If there are no tabs, $startTab.length will be 0.
  if ($startTab.length !== 0) {
    $startTab.tab("show");

    $(".sidebarMenuSelectedTabItem").attr("data-value",
      $startTab.attr("data-value"));
  }

The things I'm not sure about are:

  • Am I right that $startTab.length will be 0 when there are no tabs?
  • I'm not sure exactly why there was the check for $tablinks.parent("li").hasClass("active"), and I've removed it in this version. Is there a problem with removing that check?

@bborgesr
Copy link
Contributor Author

bborgesr commented Jun 1, 2017

  • As for the logic, I think you're completely right. I was working off existing code and I basically built up on it, and didn't take a step back to try and frame it in a more coherent piece of code. I think your code is spot on.

  • Am I right that $startTab.length will be 0 when there are no tabs?

    Yes, although this is also technically and briefly true for dynamic sidebarMenu()s, since ensureActivatedTab() is called twice for those apps. We may consider this a small performance issue, but I think it's negligible and this new code certainly isn't adding to the "problem" -- once the menu is rendered in the server function, ensureActivatedTab() is called again and the input value (for input$tabs) is then correctly set.

  • As for the check $tablinks.parent("li").hasClass("active"), it can be traced back to this commit from 2014. I think I've never questioned that, but now that you mention it, I do think we can remove it. It seems that in the original context, the point of the check was just to make sure that we're not showing something that is already shown..? From my testing now (for all the variations of the repro and some quick testing on larger apps), I don't think there's any issue. (Also, logically, I don't see why there should be a problem.)


I've pushed your code, after some testing. I think we're good to go..?

@wch
Copy link
Contributor

wch commented Jun 1, 2017

Now that I look at it some more, I think that $tablinks.parent("li").hasClass("active") might have
something to do with menuSubItems, like when the first menuItem has menuSubItems.

I think this behaves the same as before, but there's some weird behavior with the tab1 menuItem. It doesn't start expanded, and also, when it is expanded and you click on tab1_1, the little arrow on the right changes from pointing down to pointing left. But that's sort of a separate issue, and fixing those things could be a separate PR if you prefer.

library(shinydashboard)
library(shiny)

ui <- dashboardPage(
  dashboardHeader(),
  dashboardSidebar(
    sidebarMenu(id = "tabs",
      menuItem("tab1",
        menuSubItem("tab1_1", tabName = "tab1_1")
      ),
      menuItem("tab2", tabName = "tab2")
  )),
  dashboardBody(
      tabItems(
          tabItem("tab1", "This is tab 1"),
          tabItem("tab1_1", "This is tab 1_1"),
          tabItem("tab2", "This is tab 2")
      )
  )
)

server <- function(input, output, session){
  observe({ print(input$tabs) })
}
shinyApp(ui, server)

@wch wch merged commit d339bfc into master Jun 9, 2017
@wch wch deleted the barbara/tabItem branch June 9, 2017 16:54
@wch wch removed the in progress label Jun 9, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants