I use the excelent crosstalk r package to make filters for my reactable htmlwidget. I show the reactable in flexdashboard. When using the groupBy feature of reactable, I noticed that there was no scroll bar when I make the table larger than the area where it has been put in.
Here is an example Tha helps making the issue clear:
---
title: "Focal Chart (Top)"
output:
flexdashboard::flex_dashboard:
orientation: rows
---
Row {data-height=500}
-------------------------------------
### Chart 1
```{r}
library(crosstalk)
library(reactable)
```
Row {data-height=500}
-------------------------------------
### With crosstalk filtering
```{r}
cars <- MASS::Cars93[1:20, c("Manufacturer", "Model", "Type", "Price")]
data <- SharedData$new(cars)
bscols(
widths = c(3, 9),
list(
filter_checkbox("type", "Type", data, ~Type),
filter_slider("price", "Price", data, ~Price, width = "100%"),
filter_select("mfr", "Manufacturer", data, ~Manufacturer)
),
reactable(data,groupBy = "Manufacturer")
)
```
### Without crosstalk
```{r}
reactable(data,groupBy = "Manufacturer")
```
Two tables are shown. When decollapsing e.g. Chevrolet in both tables, in the right one (the one without crosstalk filtering) a scroll bar will appear. In the left however, no scroll bar appears.
How can I make my crosstalk/reactable scrollable? Is this issue solvable? Many thanks in advance!
I don't think this is a complete answer but it might get you closer. I exaggerated the minWidth so you could see the effect.
sticky_style <- list(position = "sticky", left = 0, background = "#fff", zIndex = 1,
borderRight = "1px solid #eee")
crosstalk::bscols(
widths = c(3, 9),
list(
filter_checkbox("type", "Type", data, ~Type),
filter_slider("price", "Price", data, ~Price, width = "100%"),
filter_select("mfr", "Manufacturer", data, ~Manufacturer)
),
reactable(data,columns = list(
Manufacturer = colDef(
style = sticky_style,
headerStyle = sticky_style
)
), defaultColDef = colDef(minWidth = 300))
)
By the way, #DanielJachetta, I had the same problem so updated both crosstalk and reactable packages and the problem went away. Good luck.
Related
I created a datatable in a flexdashboard with a checkbox, but the checkbox flows off the page. I tried to adjust the padding {data-padding = 10} but nothing changed. Below is the code and a picture of what the dashboard looks like. How do I move everything to the right so that it's aligned with the title of the page?
---
title: "School Dashboard"
author: "Shannon Coulter"
output:
flexdashboard::flex_dashboard:
orientation: rows
social: menu
source_code: embed
theme: spacelab
---
```{r}
library(tidyverse)
library(crosstalk)
library(DT)
library(flexdashboard)
```
Student Lookup
================================================================================
### Chronic Absenteeism Lookup
```{r ca-lookup, echo=FALSE, message=FALSE, warning=FALSE}
ican_tab <- tibble(
year = c("2022", "2022", "2022", "2022", "2022"),
date = c("March", "March","March","March","March"),
school = c("ABC", "CDE","ABC","DEF","GHI"),
grade = c("6th", "7th","8th","4th","5th"),
race_eth = c("White", "Hispanic","White","Filipino","White"),
abs_levels = c("Not At-Risk of Chronic Absenteeism", "At-Risk of Chronic Absenteeism",
"Severe Chronic Absenteeism", "Severe Chronic Absenteeism",
"Moderate Chronic Absenteeism")
)
sd <- SharedData$new(ican_tab)
bscols(list(
filter_checkbox("abs_levels", "Level", sd, ~ abs_levels, inline = TRUE),
datatable(
sd,
extensions = c("Buttons",
"Scroller"),
options = list(
autoWidth = TRUE,
scrollY = F,
columnDefs = list(list(
className = 'dt-center',
targets = c(2, 3, 4, 5)
)),
lengthMenu = c(5, 10, 25, 100),
dom = "Blrtip",
deferRender = TRUE,
scrollY = 300,
scroller = TRUE,
buttons = list('copy',
'csv',
'pdf',
'print')
),
filter = "top",
style = "bootstrap",
class = "compact",
width = "100%",
colnames = c(
"Year",
"Date",
"School",
"Grade",
"Race",
"Level"
)
) %>%
formatStyle('abs_levels',
backgroundColor = styleEqual(
unique(ican_tab$abs_levels),
c(
"#73D055ff",
"#95D840FF",
"#B8DE29FF",
"#DCE319FF"
)
))
))
```
[![enter image description here][1]][1]
The easiest way to address this is probably to add style tags to your dashboard. You can put this anywhere. I usually put it right after the YAML or right after my first R chunk, where I just place my knitr options and libraries. This does not go inside an R chunk.
<style>
body { /*push content away from far right and left edges*/
margin-right: 2%;
margin-left: 2%;
}
</style>
Update based on your updated question and comments
I don't have the content around your table, so I will give you a few options that work. For the most part, any one option won't be enough. You can mix and match the options that work best for you.
This is what I've got for the original table:
Option 1: you can use CSS to push the table away from the edges (as in my original response
Option 2: change the font sizes
Option 3: constrain the size of the datatable htmlwidget
Option 4: manually make the columns narrower
Option 5: alter the filter labels (while keeping the same filters and data)
Aesthetically looks the best? It depends on what else is on the dashboard.
I think you will need the original CSS (option 1, in my original answer) regardless of what other options you choose to use.
Option 1 is above
Option 2
To change the font sizes, you have to modify the filter_checkbox and the datatable after they're made. Instead of presenting all of the programming code, I'm going to show you want to add or modify and how I broke down the objects.
Your original code for filter_checkbox remains the same. However, you'll assign it to an object, instead of including it in bscols.
Most of the code in your datatable will remain the same. there is an addition to the parameter options. I've included the original and change for that parameter.
# filter checkbox object
fc = filter_checkbox(...parameters unchanged...)
fc$attribs$style <- css(font.size = "90%") # <-change the font size
dt = datatable(
...
...
options = list( # this will be modified
autoWidth = TRUE, # <- same
scrollY = F, # <- same
initComplete = JS( # <- I'M NEW! change size of all font
"function(settings, json) {",
"$(this.api().table().container()).css({'font-size': '90%'});",
"}"),
columnDefs = list( # <- same
list(className = 'dt-center', targets = c(2, 3, 4, 5))),
...
... # remainder of datatable and formatStyles() original code
)
# now call them together
bscols(list(fc, dt))
The top version is with 90% font size, whereas the bottom is the original table.
Option 3
To constrain the size of the datatable widget, you'll need to create the object outside of bscols, like I did in option 2. If you were to name your widget dt as in my example, this is how you could constrain the widget size. This example sets the datatable to be 50% of the width and height viewer screen (or 1/4 of the webpage). Keep in mind that the filters are not part of the widget, so in all, the table is still more than 1/4th of the webpage. You will have to adjust the size for your purposes, of course. I recommend using a dynamic sizing mechanism like vw, em, rem, and the like.
dt$sizingPolicy$defaultWidth <- "50vw"
dt$sizingPolicy$defaultHeight <- "40vh"
The top image has options 1, 2, and 3; the bottom is the original table.
Option 4
To modify the width of the columns, you can add this modification to the parameter options in you call to datatable. This could be good, because most of the columns don't require as much width as the last column. However, if you change the font size or scale the table, it will change the font size dynamically, so this option may not be necessary.
Despite using em here, in the course of this going from R code to an html_document, it was changed to pixels. So this is not dynamically sized. (Not a great idea! Sigh!)
columnDefs = list(
list(className = 'dt-center', targets = c(2, 3, 4, 5)),
list(width = '5em', targets = c(1,2,3,4,5))), # <- I'm NEW!
Option 5
For this option, I took the programming behind crosstalk::filter_checkbox() and modified the code a bit. I changed the function to filter_checkbox2(). If you use it, you can render it both ways and just keep the one you like better.
This first bit of code is the three functions that work together to create a filter_checkbox object with my modifications so that you can have a label that isn't exactly the same as the levels.
It's important to note that the filters are alphabetized by datatable. It doesn't matter if they're factors, ordered, etc. If you use this new parameter groupLabels, they need to be in an order that aligns with the levels when they're alphabetized.
I put this code in an include=F chunk by itself:
# this is nearly identical to the original function
filter_checkbox2 = function (id, label, sharedData, group,
groupLabels = NULL, # they're optional
allLevels = FALSE, inline = FALSE, columns = 1) {
options <- makeGroupOptions(sharedData, group,
groupLabels, allLevels) # added groupLabels
labels <- options$items$label
values <- options$items$value
options$items <- NULL
makeCheckbox <- if (inline)
inlineCheckbox
else blockCheckbox
htmltools::browsable(attachDependencies(tags$div(id = id,
class = "form-group crosstalk-input-checkboxgroup crosstalk-input",
tags$label(class = "control-label", `for` = id, label),
tags$div(class = "crosstalk-options-group",
crosstalk:::columnize(columns,
mapply(labels, values, FUN = function(label, value) {
makeCheckbox(id, value, label)
}, SIMPLIFY = FALSE, USE.NAMES = FALSE))),
tags$script(type = "application/json", `data-for` = id,
jsonlite::toJSON(options, dataframe = "columns",
pretty = TRUE))),
c(list(crosstalk:::jqueryLib()),crosstalk:::crosstalkLibs())))
}
inlineCheckbox = function (id, value, label) { # unchanged
tags$label(class = "checkbox-inline",
tags$input(type = "checkbox",
name = id, value = value),
tags$span(label))
}
# added groupLabels (optional)
makeGroupOptions = function (sharedData, group, groupLabels = NULL, allLevels) {
df <- sharedData$data(withSelection = FALSE, withFilter = FALSE,
withKey = TRUE)
if (inherits(group, "formula"))
group <- lazyeval::f_eval(group, df)
if (length(group) < 1) {
stop("Can't form options with zero-length group vector")
}
lvls <- if (is.factor(group)) {
if (allLevels) {levels(group) }
else { levels(droplevels(group)) }
}
else { sort(unique(group)) }
matches <- match(group, lvls)
vals <- lapply(1:length(lvls), function(i) {
df$key_[which(matches == i)]
})
lvls_str <- as.character(lvls)
if(is.null(groupLabels)){groupLabels = lvls_str} # if none provided
if(length(groupLabels) != length(lvls_str)){ # if the # labels != the # groups
message("Warning: The number of group labels does not match the number of groups.\nGroups were used as labels.")
groupLabels = lvls_str
}
options <- list(items = data.frame(value = lvls_str, label = groupLabels, # changed from lvls_str
stringsAsFactors = FALSE), map = setNames(vals, lvls_str),
group = sharedData$groupName())
options
}
When I used this new version of I changed label = "Level" to label = "Chronic Absenteeism Level". Then removed " Chronic Absenteeism" from the filter labels. The data and the datatable does not change, just the filter checkbox labels.
filter_checkbox2("abs_levels", "Chronic Absenteeism Level",
sd, ~ abs_levels, inline = TRUE,
groupLabels = unlist(unique(ican_tab$abs_levels)) %>%
str_replace(" Chronic Absenteeism", "") %>% sort())
The first image is your table with options 1, 2, 3, and 5 (not 4).
The top version in the next image has options 1, 2, 3, and 5 (not 4). The bottom is the original table. After that
If I've left anything unclear or if have any other questions, let me know.
Happy Easter!
I wonder if there is any smart programming for looping value boxes (or even better: whole r-markdown-code) in R-flexdashboards using R-shiny.
My problem is:
I have data, which is updated every day.
Every day I can display several keyfigueres.
I do this with value-boxes, becaus it is very easy to add special colors for different treshholds.
I want to show the data of the last week (7-days), see image, widch show the data for 4 days:
Is there a possibility to loop my code day by day?
My executable code example is only for two days an the valuebox for date (1st column in the image):
---
title: "Test for Loop value boxes"
author: StatistiVolker
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
require(shiny)
require(flexdashboard)
require(tidyverse)
```
<!-- C19J_Summary.Rmd -->
Testcode
=======================================================================
Sidebar {.sidebar}
-----------------------------------------------------------------------
### Settings
```{r}
sliderInput("sliderSumDate",
"Datum",
min = as.Date("2020-03-01"), #min(C19JKInz()$datI),
max = Sys.Date()-1,
value = Sys.Date()-1,
animate = TRUE)
```
```{r}
# Date
selSumDate <- reactive({
input$sliderSumDate
})
```
<!-- Is it possible to loop this Code? -->
Row
-----------------------------------------------------------------------
<!-- actual day -->
### {.value-box}
```{r}
# Emit the download count
renderValueBox({
valueBox(format(as.Date(selSumDate()-0),"%d.%m.%Y (%a)"),
caption = "Datum",
# icon = "fa-calendar",
color = "cornflowerblue")
})
```
<!-- Next Code is almost the same as above, except one day earlier -->
<!-- Is it possible to loop this Code? -->
Row
-----------------------------------------------------------------------
<!-- day before -->
### {.value-box}
```{r}
# Emit the download count
renderValueBox({
valueBox(format(as.Date(selSumDate()-1),"%d.%m.%Y (%a)"),
caption = "Datum",
# icon = "fa-calendar",
color = "cornflowerblue")
})
```
Thank you for any idea to solve my problem.
PS: This was not useful, because it is not possible to control the colors for different treshholds
you have found an Easter egg:
---
title: "Test for Loop value boxes"
author: StatistiVolker
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: fill
runtime: shiny
---
```{r setup, include=FALSE}
require(shiny)
require(flexdashboard)
require(tidyverse)
```
<!-- C19J_Summary.Rmd -->
# Sidebar {.sidebar data-width=350}
### Settings
```{r}
sliderInput("sliderSumDate",
"Datum",
min = as.Date("2020-03-01"), #min(C19JKInz()$datI),
max = Sys.Date()-1,
value = Sys.Date()-1,
animate = TRUE)
```
```{r}
# Date
selSumDate <- reactive({
input$sliderSumDate
})
```
<!-- Is it possible to loop this Code? -->
```{r}
myValueBox <- function(title, caption="", color="cornflowerblue", myicon="", fontsize="25px"){
div(
class = "value-box level3",
style = glue::glue(
'
background-color: #{color}#;
height: 106px;
width: 18%;
display: inline-block;
overflow: hidden;
word-break: keep-all;
text-overflow: ellipsis;
', .open = '#{', .close = '}#'
),
div(
class = "inner",
p(class = "value", title, style = glue::glue("font-size:{fontsize}")),
p(class = "caption", caption)
),
div(class = "icon", myicon)
)
}
```
Testcode
=======================================================================
<!-- actual day -->
```{r}
uiOutput("el")
```
```{r}
# Emit the download count
colors = c("#8b0000", "#000000", "#228b22", "#ffd700")
output$el <- renderUI({
lapply(0:-6, function(x) {
div(
myValueBox(format(as.Date(selSumDate()-x),"%d.%m.%Y (%a)"), "Datum", myicon = icon("calendar")),
myValueBox(sample(1000, 1), "Infizierte", color = sample(colors, 1)),
myValueBox(sample(1000, 1), "Aktiv erkrankt", color = sample(colors, 1)),
myValueBox(sample(1000, 1), "Genesene", color = sample(colors, 1)),
myValueBox(sample(1000, 1), "Verstorbene", color = sample(colors, 1))
)
})
})
```
Impossible to create what you want with original {flexdashboard} package, no way to control row/column layout automatically. However, we can create our own value box.
Works the best on bigger screens (> 1000px width), also works on mobile (<670px), different styles will apply on small screens. Medium screens (670-1000) may have some problems, try to change width: 18%; to a number you want.
overflow text due to screen size issues are trimmed off. Change the
fontsize argument may also help.
Bigger screen
Mobile
I have tried everything, but am stumped trying to get a DT datatable to fill the panel of a flexdashboard. It employs a scroller, but I cannot find any way to force the table to be the full height of the panel. In the image linked below, it is the table on the right that causes problems and seems to be related to the fact that it is in a loop (inputting a table per tab). When I exclude the loop and just do one table, it will fill the panel. I can play about with some scroller parameters to make it larger, but then it will not be full panel on every screen. I just want some way to make it auto-fill the tabbed panel size each time. Thanks in advance for your help.
flex image - see right panel
```
## XXX Detailed Reports {.tabset}
``` {r, echo=FALSE, warning=FALSE, results="asis"}
for (i in 1:length(c_list)){
cty=c("XXX","FFF","GGG","TTT","RRR","PPP","LLL","WWW","EEE")
cat("### ", cty[i], "\n")
cat("#### ",cty[i],paste("Week ",floor(yday(as.Date(format(Sys.Date() - wday(Sys.Date() + 1), "%d %B %Y"),"%d %B %Y"))/7)+1,sep="")," Other Text here", "\n")
x = data.frame(c_list[i]) %>% mutate_if(is.character, function(x) {Encoding(x) <- 'latin1'; return(x)}) # remove strange sysmbols
tb=x %>%
DT::datatable(
extensions = c("Buttons", "Responsive", "Scroller", "RowGroup"),
options = list(dom = 'Bfrtip',
buttons = c('excel', "csv"),
rowGroup = list(dataSrc = 0),
deferRender = TRUE,
#scrollY = 800,
#scroller = TRUE,
scrollCollapse = FALSE,
columnDefs = list(list(visible=FALSE, targets=c(0))),
paging=FALSE,
searching=FALSE,
info=FALSE),
selection = 'none',
rownames=F) %>%
formatStyle(columns = colnames(.$x$data), `font-size` = "12px")
print(htmltools::tagList(tb))
#cat("\n")
}
```
EDIT
This seems to be an issue already known to the plotly community
github plotly issue #689
and there is an analogous question here on SO.
Unfortunately, it seems no solution is available yet. Any advice would be greatly appreciated.
I am trying to use Crosstalk and Plotly to create a dashboard and I have come across an unexpected behaviour.
When selecting through the Crosstalk filter, the Plotly bargraph leaves "gaps" for the unselected entries.
As a reproducible example, let's say I want to compare cities populations, what I am getting is this (code at the bottom):
It might very well be that I am missing something, is there a way to get rid of the gap? any advice on viable ways to do a similar comparison avoiding the issue?
Thanks in advance.
Code:
---
title: "Crosstalk+Plotly bargraph selection"
---
```{r setup, include=FALSE}
options(stringsAsFactors = FALSE)
library(crosstalk)
library(dplyr)
library(plotly)
#data on cities' population
city_pop <- data.frame("City" = c("Florence", "Milan", "Venice"),
"Population" = c(382258, 1352000, 261905))
#setting up Crosstalk shared data
sd <- SharedData$new(city_pop, key = city_pop$city)
#filter for the cities
filt <- filter_select(
id = "select_name",
label = "Selected City",
sharedData = sd,
group = ~City)
#barplot of cities' population
bars_pop <- plot_ly(sd, x = ~City, y = ~Population) %>%
add_bars(width=0.2,
x = ~City,
y = ~Population,
color = I("#89CFF0"),
name = "",
opacity=.9,
hoverinfo = 'y',
hovertemplate = paste('%{x} <br> number of Residents: %{y}<extra></extra>')
)
```
```{r, echo=FALSE}
filt
bars_pop
```
This worked for me - on the axis where it's happening, set categoryorder = "trace".
e.g.,
plot_ly(...) %>% layout(yaxis = list(categoryorder = "trace"))
I'm trying to build an HTML dashboard with multiple pages and all sorts of different elements using gauges, tables, charts from highcharter package, charts from ggplot2 package, images, etc.
I'm trying to change the background color of some elements. I've tried doing this using the built-in arguments of charts and tables, I've tried using div and setting style=background-color: #dddddd (Hex code for the grey I'm trying to use) and I've tried playing around with CSS style with something like:
<style type="text/css">
.chart-title { /* chart_title */
background-color: #dddddd;
</style>
However, I'm not familiar with CSS and couldn't find the correct substitution to ".chart-title" to get what I need. I tried using "body", "h3", "header", "box". I managed to change some colors but not where I needed them to change.
I pasted below a piece of my Dashboard and corresponding Print Screen of the result.
HOME
=======================================================================
Row {data-height=400}
-------------------------------------------------------------------------------------------------------------
### Productivity {.no-title}
```{r fig.width=1.5}
df1<-data.frame(Factory = c('<img src="seta4.png" height=60></img>', "150t/h"))
div(datatable(df1, rownames=FALSE, options = list(ordering=FALSE, scrollY = "150px", scrollX=FALSE, dom='t', autowidth=TRUE,
#columnDefs = list(list(width='5px', targets=c(1,2,3))),
columnDefs=list(list(className='dt-center', targets=c(0)))), escape = FALSE) %>%
formatStyle(names(df1), textAlign = 'center' ) %>%
formatStyle(names(df1), backgroundColor='rgb(221,221,221)', color='rgb(0,0,0)') %>%
formatStyle(names(df1), `font-size` ='15px'),
style="font-size:12px; font-weight:Bold; background-color: #dddddd")
```
### Occupation {.no-title}
```{r fig.width=2}
rate <- ocupacao[1]
div(gauge(rate, min = 0, max = 100, symbol = '%', gaugeSectors(
success = c(0, 39), warning = c(40, 79), danger = c(80, 100)
)), style="background-color: #dddddd")
```
### Ferro {.no-title}
```{r fig.width=3}
df1<-data.frame(Ferroviario = c('<img src="trem.png" height=60></img>', "30Kt"))
div(datatable(df1, rownames=FALSE, options = list(ordering=FALSE, scrollY = "150px", scrollX=FALSE, dom='t', autowidth=TRUE,
#columnDefs = list(list(width='5px', targets=c(1,2,3))),
columnDefs=list(list(className='dt-center', targets=c(0)))), escape = FALSE) %>%
formatStyle(names(df1), textAlign = 'center' ) %>%
formatStyle(names(df1), backgroundColor='rgb(221,221,221)', color='rgb(0,0,0)') %>%
formatStyle(names(df1), `font-size` ='15px'),
style="background-color: #dddddd; font-size:12px, font-weight:Bold")
```
Dashboard:
As you can see, I'm not getting the result I wanted. I wanted the boxes in the top row to have all grey backgrounds, but the most I can get is that only part of the background is grey and the edges/remaining parts are still white.
Is there any way to do this?