I have a shinydashboard with three collapsible menu items in the sidebar menu. The first menu item contains an action button that I am trying to right-align. When the menu item is expanded, the button is visible but overflows onto the label of the second menu item:
What's causing this effect and how can I go about fixing it?
Here is the code to reproduce the app:
library("shiny")
library("shinydashboard")
header = dashboardHeader()
sidebar = dashboardSidebar(
sidebarMenu(
menuItem("Item 1", tabName = "item1",
selectInput("letters", "Letters:", choices = LETTERS),
tags$div(class = "pull-right",
actionButton("clickme", label = "Click me", style = "primary")
)
),
menuItem("Item 2"),
menuItem("Item 3")
)
)
body = dashboardBody()
ui = dashboardPage(header, sidebar, body)
server = function(input,output,session){}
shinyApp(ui, server)
Replacing class = "pull-right" with style = "float:right;" has the same effect.
Adding this style:
.skin-blue .sidebar-menu>li>.treeview-menu {
overflow: auto;
}
seems to get rid of the ghosting problem but adds a scroll to the overflow portion of the selectInput dropdown, which I don't want:
To align the action button to the right we can do CSS similar to here
sidebar = dashboardSidebar(
sidebarMenu(
menuItem("Item 1", tabName = "item1",
selectInput("letters", "Letters:", choices = LETTERS),
actionButton("clickme", label = "Click me", style = "primary"),
tags$style(type='text/css', "button#clickme {margin-left: 60%;}")),
menuItem("Item 2"),
menuItem("Item 3")
)
)
Alternatively
menuItem("Item 1", tabName = "item1",
selectInput("letters", "Letters:", choices = LETTERS),
div(style="display:inline-block;margin-left: 52%;padding-bottom: 10px;",
actionButton("clickme", label = "Click me", style = "primary"))
Related
I would like to change the default arrow direction (as in the attached pic), so it will point to the right.
this is the current code:
sidebarMenu(
menuItem("Market data", tabName = "market data", icon = icon("users"), startExpanded = TRUE,
menuSubItem("Performance", tabName = "history", icon = icon("calendar-day")),
menuSubItem("SMP", tabName = "SMP", icon = icon("dollar-sign"))
),
menuItem("Consumer 1", tabName = "consumer1", icon = icon("user"), startExpanded = FALSE,
menuSubItem("Consumption", tabName = "consumption", icon = icon("history")),
menuSubItem("Profile", tabName = "profile", icon = icon("poll-h")),
menuSubItem("Forecast engine", tabName = "forecast", icon = icon("brain"))
)
Thanks in advance
General
In general, to change the defaults in shinydashboard you need to find the correct CSS tags for these elements and then add your own custom CSS file to overwrite the default.
If you plan on making a lot of these changes, the shinydashboard framework might not be right for you.
Specific solution to your question
With some poking around in the browser, you'll see that the tag for these arrows are .fa-angle-left:before. The symbol showed is defined with the following CSS:
.fa-angle-left:before{content:"\f104"}
To change it to a right arrow we need to change \f104 to \f105:
As noted in the documentation, you can add your own CSS-file as such:
Add the file www/custom.css to the same folder as your Shiny dashboard is located.
Add the following code to it:
.fa-angle-left:before{content:"\f105"}
If you want the arrow to still point down after you click it you also need to add
.sidebar-menu li.active>a>.fa-angle-left, .sidebar-menu li.active>a>.pull-right-container>.fa-angle-left {
transform: rotate(90deg);
}
When this is done, you need to add the following code to your sidebarMenu:
tags$head(
tags$link(rel = "stylesheet", type = "text/css", href = "custom.css")
)
Working example
app.R
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
dashboardSidebar(
sidebarMenu(
tags$head(
tags$link(rel = "stylesheet", type = "text/css", href = "custom.css")
),
menuItem(
"Market data",
tabName = "market data",
icon = icon("users"),
menuSubItem("SMP", tabName = "SMP", icon = icon("dollar-sign"))
)
)
)
),
dashboardBody()
)
server <- function(input, output) {}
shinyApp(ui, server)
www/custom.css
.fa-angle-left:before{content:"\f105"}
.sidebar-menu li.active>a>.fa-angle-left, .sidebar-menu li.active>a>.pull-right-container>.fa-angle-left {
transform: rotate(90deg);
}
I'm having trouble with the formats of conditional panels affecting other conditional panels. Below is reproducible code, and at the bottom are images better explaining the issue. In the fuller App this code derives from, the problem is more obvious and makes it look sloppy (in the fuller App, there are multiple screens the user clicks through as the user scrolls to the right along the shaded bar (Well Panel) at the top just underneath the tab label, and the misalignment gets more pronounced as the user scrolls to the right).
The problem is: as the user scrolls right through the Glide Controls / Well Panels to make selections, the Well Panels (at the top with radio buttons) begin to misalign with the table and/or plots that appear beneath. The misalignment gets more pronounced as the user scrolls right. This misalignment isn't as apparent in this reproducible example, but is more pronounced in the fuller App this derives from where there are multiple "screens" or Well Panels at the top for the user to scroll through and where there are data tables and/or plots presented underneath in the main panel.
For sake of simplicity all server code is eliminated in this example (no plots, no tables), as the issue still presents without the server code.
If I comment-out other conditional panels (marked "###" in the reproducible code) the misalignment goes away. So how can I make the conditional panels independent of one another, as a way of eliminating this misalignment? I'm open to any other suggestions for eliminating this misalignment.
The basic structure of the App is the user makes "big choices" along the sidebar panel, and makes more "refined choices" only the top bar underneath the tab label using Glide Controls/Well Panels etc. for a carousel affect.
Reproducible code:
library(shiny)
library(shinyglide)
library(shinyjs)
library(shinyWidgets)
ui <-
fluidPage(
useShinyjs(),
tags$style(".glide-controls { position: absolute; top: 8px; right: 14px; width: 160px; }"),
titlePanel("Hello"),
sidebarLayout(
sidebarPanel(selectInput("selectData", h5(strong("Select data to view:")),choices = list("Stratification","DnL balances"),selected = "Stratification")),
mainPanel(
tabsetPanel(
tabPanel("Private data", value = 1,
div(style = "margin-top:10px"),
conditionalPanel(condition = "input.selectData == 'Stratification'",
fluidRow(
column(12,
glide(
custom_controls = div(class = "glide-controls", glideControls()),
screen(
wellPanel(
radioButtons(
inputId = 'groupStrats',
label = NULL,
choiceNames = c('Calendar period','MOB'),
choiceValues = c('Period','MOB'),
selected = 'Period',
inline = TRUE),
style = "padding-top: 12px; padding-bottom: 0px;")
),
screen(
wellPanel(
radioButtons(
inputId = 'stratsView',
label = NULL,
choices = list("Table view" = 1,"Plot view" = 2),
selected = 1,
inline = TRUE),
style = "padding-top: 12px; padding-bottom: 0px;")
)
)
)
),
### Deleting next line resolves the well panel issue ###
conditionalPanel(condition = "input.stratsView == 2",fluidRow(column(12, plotOutput("stratPlot"))))
),
### Deleting the following conditional panel also resolves the well panel issue ###
conditionalPanel(condition = "input.selectData == 'DnL balances'",
fluidRow(
column(12,
glide(
custom_controls = div(class = "glide-controls", glideControls()),
screen(
wellPanel(
radioButtons(
inputId = 'groupBal',
label = NULL,
choiceNames = c('Calendar period','MOB'),
choiceValues = c('Period','MOB'),
selected = 'Period',
inline = TRUE),
style = "padding-top: 12px; padding-bottom: 0px;")
)
)
)
)
), # closes conditional panel
### Deleting the following conditional panel (or either checkbox... or selectize...) also resolves the well panel issue ###
conditionalPanel(condition = "input.selectData == 'Level 1 data'",
panel(
checkboxGroupInput(
inputId = "vars",
label = "Filter by (default view is all data):",
choices = c("Period", "MOB"),
selected = c("Period", "MOB"),
inline = TRUE),
selectizeGroupUI(
id = "my-filters",
params = list(Period = list(inputId = "Period", title = "Period:"),
MOB = list(inputId = "MOB", title = "MOB:"))
), # closes above selectize...
status = "primary"
)
) # closes conditional panel
), id = "tabselected"
)
)
)
)
server <- function(input, output, session) {}
shinyApp(ui, server)
Actually this is the same issue as here.
The conditionalPanels are visible for a very short time when first invoking the app.
This causes a vertical scrollbar to appear and leads to the misalignment.
Use style = "display: none;" to render the conditionalPanels hidden on startup (where needed) and please leave a thumbs up or other feedback here.
library(shiny)
library(shinyjs)
library(shinyglide)
library(shinyWidgets)
ui <-
fluidPage(
useShinyjs(),
tags$style(".glide-controls { position: absolute; top: 8px; right: 14px; width: 160px; }"),
titlePanel("Hello"),
sidebarLayout(
sidebarPanel(selectInput("selectData", h5(strong("Select data to view:")),choices = list("Stratification","DnL balances"),selected = "Stratification")),
mainPanel(
tabsetPanel(
tabPanel("Private data", value = 1,
div(style = "margin-top:10px"),
conditionalPanel(condition = "input.selectData == 'Stratification'",
fluidRow(
column(12,
glide(
custom_controls = div(class = "glide-controls", glideControls()),
screen(
wellPanel(
radioButtons(
inputId = 'groupStrats',
label = NULL,
choiceNames = c('Calendar period','MOB'),
choiceValues = c('Period','MOB'),
selected = 'Period',
inline = TRUE),
style = "padding-top: 12px; padding-bottom: 0px;")
),
screen(
wellPanel(
radioButtons(
inputId = 'stratsView',
label = NULL,
choices = list("Table view" = 1,"Plot view" = 2),
selected = 1,
inline = TRUE),
style = "padding-top: 12px; padding-bottom: 0px;")
)
)
)
),
### Deleting next line resolves the well panel issue ###
conditionalPanel(condition = "input.stratsView == 2", style = "display: none;", fluidRow(column(12, plotOutput("stratPlot"))))
),
### Deleting the following conditional panel also resolves the well panel issue ###
conditionalPanel(condition = "input.selectData == 'DnL balances'", style = "display: none;",
fluidRow(
column(12,
glide(
custom_controls = div(class = "glide-controls", glideControls()),
screen(
wellPanel(
radioButtons(
inputId = 'groupBal',
label = NULL,
choiceNames = c('Calendar period','MOB'),
choiceValues = c('Period','MOB'),
selected = 'Period',
inline = TRUE),
style = "padding-top: 12px; padding-bottom: 0px;")
)
)
)
)
), # closes conditional panel
### Deleting the following conditional panel (or either checkbox... or selectize...) also resolves the well panel issue ###
conditionalPanel(condition = "input.selectData == 'Level 1 data'", style = "display: none;",
panel(
checkboxGroupInput(
inputId = "vars",
label = "Filter by (default view is all data):",
choices = c("Period", "MOB"),
selected = c("Period", "MOB"),
inline = TRUE),
selectizeGroupUI(
id = "my-filters",
params = list(Period = list(inputId = "Period", title = "Period:"),
MOB = list(inputId = "MOB", title = "MOB:"))
), # closes above selectize...
status = "primary"
)
) # closes conditional panel
), id = "tabselected"
)
)
)
)
server <- function(input, output, session) {}
shinyApp(ui, server)
I'm developing a shiny app and I want to add an "infobox" next to a selectInput(). Basically I want to add a small "info" icon to the selectInput() label and when a user move the mouse hover the info icon, a box with some text appears. I've found here on stackedoverflow a solution with tags$span and tags$i and if I add only text it works fine, but if I want to add an html link, or simply another tags (like tags$strong), it doesn't work. Here a reproducible example.
library(shiny)
shinyApp(
ui = fluidPage(
br(),
selectInput("works",
label = tags$span(
"This works",
tags$i(
class = "glyphicon glyphicon-info-sign",
style = "color:#0072B2;",
title = "Further information "
)),
choices = c("a","b")),
selectInput("notwork",
label = tags$span(
"This not works",
tags$i(
class = "glyphicon glyphicon-info-sign",
style = "color:#0072B2;",
title = list("Further information ",
tags$a(href = "https://www.google.com", "here", .noWS = "after"))
)),
choices = c("a","b")),
selectInput("notwork2",
label = tags$span(
"Neither this",
tags$i(
class = "glyphicon glyphicon-info-sign",
style = "color:#0072B2;",
title = p("Further information ",
strong("here"))
)),
choices = c("a","b")),
),
server = function(input, output) {
}
)
It looks I can't pass any other html tags to that value.
You try to put HTML in a normal HTML title attribute, which is never supported. what you need is some sort of Tooltip. You can use bsButton in combination with bsPopover from the ShinyBS package. I did not bother for the styling, but I am pretty sure you can get it done from here.
library(shiny)
library(ShinyBS)
shinyApp(
ui = fluidPage(
br(),
selectInput("works",
label = tags$span(
"This works",
tags$i(
class = "glyphicon glyphicon-info-sign",
style = "color:#0072B2;",
title = "Further information "
)),
choices = c("a","b")),
selectInput("worksnow",
label = tags$span("This works now too", bsButton("thisworks", label = "", icon = icon("info"), style = "info", size = "extra-small")),
choices = c("a","b")
),
bsPopover(
id = "thisworks",
title = "More information",
content = paste0(
"Any HTML can be here ",
a("ShinyBS", href = "https://ebailey78.github.io/shinyBS/index.html", target="_blank")
),
placement = "right",
trigger = "hover",
options = list(container = "body")
)
),
server = function(input, output) {
}
)
The ui of the following example contains four selectInput. The last two of them are in a splitLayout. I noticed that, when I launch the app, the label of the last two would overlap if the window size is not large enough, as the first screenshot shows. Is it possible to make the label of the input in splitLayout dynamically change depends on the width of the window? A comparison would be the first two selectInput. As shown in the second screenshot, when I reduce the window width, the label would change to two lines. I would like to have the same behavior for the last two selectInput in splitLayout.
library(shiny)
# Define UI
ui <- fluidPage(
mainPanel(
selectInput(inputId = "A", label = "This is a long lebel with lots of words", choices = letters[1:5], selected = "a"),
selectInput(inputId = "B", label = "This is a long lebel with lots of words", choices = letters[1:5], selected = "a"),
splitLayout(
selectInput(inputId = "C", label = "This is a long lebel with lots of words", choices = letters[1:5], selected = "a"),
selectInput(inputId = "D", label = "This is a long lebel with lots of words", choices = letters[1:5], selected = "a"),
# Expand the menu in splitLayout
# From: https://stackoverflow.com/a/40098855/7669809
tags$head(tags$style(HTML("
.shiny-split-layout > div {
overflow: visible;
}
")))
)
)
)
# Server logic
server <- function(input, output, session){
}
# Complete app with UI and server components
shinyApp(ui, server)
First screenshot:
Sceond screenshot:
Update
#Simran has pointed out that overflow: visible is the cause of this issue. However, I need this to expand my menu in the selectInput based on this post: https://stackoverflow.com/a/40098855/7669809
I assume using fluidRow() with column() is an option for you.
Then you could use:
fluidRow(
column(width = 4,
selectInput(...)
),
column(width = 4,
selectInput(...)
)
)
Note 1:
You can control the width of an input by the width parameter of column().
Note 2:
Sidenote: If you want to use the full width of 12, you also have to set the mainPanel() to 12, see e.g. here:
https://stackoverflow.com/a/44214927/3502164
Full app - reproducible example:
library(shiny)
# Define UI
ui <- fluidPage(
mainPanel(
selectInput(inputId = "A", label = "This is a long lebel with lots of words", choices = letters[1:5], selected = "a"),
selectInput(inputId = "B", label = "This is a long lebel with lots of words", choices = letters[1:5], selected = "a"),
fluidRow(
column(width = 4,
selectInput(inputId = "C", label = "This is a long lebel with lots of words", choices = letters[1:5], selected = "a")
),
column(width = 4,
selectInput(inputId = "D", label = "This is a long lebel with lots of words", choices = letters[1:5], selected = "a")
)
),
# Expand the menu in splitLayout
# From: https://stackoverflow.com/a/40098855/7669809
tags$head(tags$style(HTML("
.shiny-split-layout > div {
display: inline-block;
}
")))
)
)
# Server logic
server <- function(input, output, session){
}
# Complete app with UI and server components
shinyApp(ui, server)
Remove overflow: visible. This is what is making the text spill over the div. I see that here in your code:
.shiny-split-layout > div {
overflow: visible;
}
In my shiny app I want to add an option to let users jump to a specific element in the app (a table, a plot, just anything with an id), on current or different tab, by clicking on infoBox (or any other object I want).
My solution was to surround infoBox with div and add thehref=#id_of_element attribute. Unfortunately this solution works only for tabs with an extra "data-toggle" = "tab" attribute (it also does not change the opened tab to active), but that's not what I want.
My question is: how can I add the mentioned option and why this solution isn't working? Here is a small example what I want to do:
UI
library(shiny)
library(shinydashboard)
shinyUI(
dashboardPage(
skin = "blue",
dashboardHeader(title = "Example"),
dashboardSidebar(
sidebarMenu(id = "sidebarmenu",
menuItem("Tab1", icon = icon("line-chart"),
menuSubItem("SubTab1", tabName = "sub1", icon = icon("bar-chart")),
menuSubItem("SubTab2", tabName = "sub2", icon = icon("database"))),
menuItem("Tab2", tabName = "tab2", icon = icon("users"))
)
),
dashboardBody(
tabItems(
tabItem(tabName = "sub1",
tags$div(href="#s2t2",
infoBox(value = "Go to table 2 in SubTab2 (not working)",title = "Click me")),
tags$div(href="#shiny-tab-tab2", "data-toggle" = "tab",
infoBox(value = "Go to Tab2 (this works)",title = "Click me"))
),
tabItem(tabName = "sub2",
tableOutput("s2t1"),
tableOutput("s2t2")
),
tabItem(tabName = "tab2",
tableOutput("t2t1"),
tableOutput("t2t2")
)
)
)
)
)
SERVER:
shinyServer(function(input, output,session) {
output$s2t1<- renderTable(mtcars)
output$s2t2<- renderTable(mtcars)
output$t2t1<- renderTable(mtcars)
output$t2t2<- renderTable(mtcars)
} )
I found my answer:
$(document).ready(function() {
$("#div1").click(function() {
$(".sidebar-menu a[href=\'#shiny-tab-tab2\']").tab("show");
setTimeout(function(){
var top = $("#t2t2").position().top;
$(window).scrollTop( top );
}, 300);
});
});
where div1 is div around infoBox